From 1b7ab627b56758212231ee0a70314e4b7ce25a10 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Sep 2011 17:20:39 +0200 Subject: [PATCH] Finished FLV2DTSC code, fixed a lot of bugs in new code, added DTSC info tool. --- DTSC_Analyser/Makefile | 23 ++++ DTSC_Analyser/main.cpp | 38 ++++++ FLV2DTSC/Makefile | 23 ++++ FLV2DTSC/main.cpp | 261 +++++++++++++++++++++++++++++++++++++++++ util/dtsc.cpp | 134 ++++++++++++--------- util/dtsc.h | 9 +- util/flv_tag.cpp | 2 +- 7 files changed, 432 insertions(+), 58 deletions(-) create mode 100644 DTSC_Analyser/Makefile create mode 100644 DTSC_Analyser/main.cpp create mode 100644 FLV2DTSC/Makefile create mode 100644 FLV2DTSC/main.cpp diff --git a/DTSC_Analyser/Makefile b/DTSC_Analyser/Makefile new file mode 100644 index 00000000..2d12ca7e --- /dev/null +++ b/DTSC_Analyser/Makefile @@ -0,0 +1,23 @@ +SRC = main.cpp ../util/dtsc.cpp +OBJ = $(SRC:.cpp=.o) +OUT = DTSC_Info +INCLUDES = +DEBUG = 4 +OPTIMIZE = -g +CCFLAGS = -Wall -Wextra -funsigned-char $(OPTIMIZE) -DDEBUG=$(DEBUG) +CC = $(CROSS)g++ +LD = $(CROSS)ld +AR = $(CROSS)ar +LIBS = +.SUFFIXES: .cpp +.PHONY: clean default +default: $(OUT) +.cpp.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(LIBS) -c $< -o $@ +$(OUT): $(OBJ) + $(CC) $(LIBS) -o $(OUT) $(OBJ) +clean: + rm -rf $(OBJ) $(OUT) Makefile.bak *~ +install: $(OUT) + cp -f ./$(OUT) /usr/bin/ + diff --git a/DTSC_Analyser/main.cpp b/DTSC_Analyser/main.cpp new file mode 100644 index 00000000..81176bac --- /dev/null +++ b/DTSC_Analyser/main.cpp @@ -0,0 +1,38 @@ +/// \file DTSC_Analyser/main.cpp +/// Contains the code for the DTSC Analysing tool. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../util/dtsc.h" //DTSC support + +/// Reads DTSC from stdin and outputs human-readable information to stderr. +int main() { + DTSC::Stream Strm; + + std::string inBuffer; + char charBuffer[1024*10]; + unsigned int charCount; + bool doneheader = false; + + while(std::cin.good()){ + //invalidate the current buffer + std::cin.read(charBuffer, 1024*10); + charCount = std::cin.gcount(); + inBuffer.append(charBuffer, charCount); + if (Strm.parsePacket(inBuffer)){ + if (!doneheader){ + doneheader = true; + Strm.metadata.Print(); + } + Strm.getPacket().Print(); + } + } + return 0; +} diff --git a/FLV2DTSC/Makefile b/FLV2DTSC/Makefile new file mode 100644 index 00000000..8787c723 --- /dev/null +++ b/FLV2DTSC/Makefile @@ -0,0 +1,23 @@ +SRC = main.cpp ../util/flv_tag.cpp ../util/dtsc.cpp ../util/amf.cpp ../util/socket.cpp +OBJ = $(SRC:.cpp=.o) +OUT = DDV_FLV2DTSC +INCLUDES = +DEBUG = 4 +OPTIMIZE = -g +CCFLAGS = -Wall -Wextra -funsigned-char $(OPTIMIZE) -DDEBUG=$(DEBUG) +CC = $(CROSS)g++ +LD = $(CROSS)ld +AR = $(CROSS)ar +LIBS = +.SUFFIXES: .cpp +.PHONY: clean default +default: $(OUT) +.cpp.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(LIBS) -c $< -o $@ +$(OUT): $(OBJ) + $(CC) $(LIBS) -o $(OUT) $(OBJ) +clean: + rm -rf $(OBJ) $(OUT) Makefile.bak *~ +install: $(OUT) + cp -f ./$(OUT) /usr/bin/ + diff --git a/FLV2DTSC/main.cpp b/FLV2DTSC/main.cpp new file mode 100644 index 00000000..0c3008fe --- /dev/null +++ b/FLV2DTSC/main.cpp @@ -0,0 +1,261 @@ +/// \file FLV2DTSC/main.cpp +/// Contains the code that will transform any valid FLV input into valid DTSC. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../util/flv_tag.h" //FLV support +#include "../util/dtsc.h" //DTSC support +#include "../util/amf.h" //AMF support + +// String onMetaData +// ECMA Array +// Bool hasVideo 1 +// Number videocodecid 4 (2 = H263, 4 = VP6, 7 = H264) +// Number width 320 +// Number height 240 +// Number framerate 23.976 (/ 1000) +// Number videodatarate 500.349 (kbps) +// Bool hasAudio 1 +// Bool stereo 1 +// Number audiodelay 0 +// Number audiosamplerate 11025 +// Number audiosamplesize 16 +// Number audiocodecid 2 (2 = MP3, 10 = AAC) +// Number audiodatarate 64.3269 (kbps) + + +/// Holds all code that converts filetypes to DTSC. +namespace Converters{ + + /// Inserts std::string type metadata into the passed DTMI object. + /// \arg meta The DTMI object to put the metadata into. + /// \arg cat Metadata category to insert into. + /// \arg elem Element name to put into the category. + /// \arg val Value to put into the element name. + void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val){ + if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} + meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); + std::cerr << "Metadata " << cat << "." << elem << " = " << val << std::endl; + } + + /// Inserts uint64_t type metadata into the passed DTMI object. + /// \arg meta The DTMI object to put the metadata into. + /// \arg cat Metadata category to insert into. + /// \arg elem Element name to put into the category. + /// \arg val Value to put into the element name. + void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val){ + if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} + meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); + std::cerr << "Metadata " << cat << "." << elem << " = " << val << std::endl; + } + + /// Returns true if the named category and elementname are available in the metadata. + /// \arg meta The DTMI object to check. + /// \arg cat Metadata category to check. + /// \arg elem Element name to check. + bool Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem){ + if (meta.getContentP(cat) == 0){return false;} + if (meta.getContentP(cat)->getContentP(elem) == 0){return false;} + return true; + } + + /// Reads FLV from STDIN, outputs DTSC to STDOUT. + int FLV2DTSC() { + FLV::Tag FLV_in; // Temporary storage for incoming FLV data. + AMF::Object meta_in; // Temporary storage for incoming metadata. + DTSC::DTMI meta_out; // Storage for outgoing DTMI header data. + DTSC::DTMI pack_out; // Storage for outgoing DTMI data. + std::stringstream prebuffer; // Temporary buffer before sending real data + bool sending = false; + unsigned int counter = 0; + + while (!feof(stdin)){ + if (FLV_in.FileLoader(stdin)){ + if (!sending){ + counter++; + if (counter > 10){ + sending = true; + meta_out.Pack(true); + meta_out.packed.replace(0, 4, DTSC::Magic_Header); + std::cout << meta_out.packed; + std::cout << prebuffer.rdbuf(); + prebuffer.str(""); + std::cerr << "Buffer done, starting real-time output..." << std::endl; + } + } + if (FLV_in.data[0] == 0x12){ + meta_in = AMF::parse((unsigned char*)FLV_in.data+11, FLV_in.len-15); + if (meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData") && meta_in.getContentP(1)){ + AMF::Object * tmp = meta_in.getContentP(1); + if (tmp->getContentP("videocodecid")){ + switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ + case 2: Meta_Put(meta_out, "video", "codec", "H263"); break; + case 4: Meta_Put(meta_out, "video", "codec", "VP6"); break; + case 7: Meta_Put(meta_out, "video", "codec", "H264"); break; + default: Meta_Put(meta_out, "video", "codec", "?"); break; + } + } + if (tmp->getContentP("audiocodecid")){ + switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ + case 2: Meta_Put(meta_out, "audio", "codec", "MP3"); break; + case 10: Meta_Put(meta_out, "audio", "codec", "AAC"); break; + default: Meta_Put(meta_out, "audio", "codec", "?"); break; + } + } + if (tmp->getContentP("width")){ + Meta_Put(meta_out, "video", "width", tmp->getContentP("width")->NumValue()); + } + if (tmp->getContentP("height")){ + Meta_Put(meta_out, "video", "height", tmp->getContentP("height")->NumValue()); + } + if (tmp->getContentP("framerate")){ + Meta_Put(meta_out, "video", "fpks", tmp->getContentP("framerate")->NumValue()*1000); + } + if (tmp->getContentP("videodatarate")){ + Meta_Put(meta_out, "video", "bps", (tmp->getContentP("videodatarate")->NumValue()*1024)/8); + } + if (tmp->getContentP("audiodatarate")){ + Meta_Put(meta_out, "audio", "bps", (tmp->getContentP("audiodatarate")->NumValue()*1024)/8); + } + if (tmp->getContentP("audiosamplerate")){ + Meta_Put(meta_out, "audio", "rate", tmp->getContentP("audiosamplerate")->NumValue()); + } + if (tmp->getContentP("audiosamplesize")){ + Meta_Put(meta_out, "audio", "size", tmp->getContentP("audiosamplesize")->NumValue()); + } + if (tmp->getContentP("stereo")){ + if (tmp->getContentP("stereo")->NumValue() == 1){ + Meta_Put(meta_out, "audio", "channels", 2); + }else{ + Meta_Put(meta_out, "audio", "channels", 1); + } + } + } + } + if (FLV_in.data[0] == 0x08){ + char audiodata = FLV_in.data[11]; + if (FLV_in.needsInitData() && FLV_in.isInitData()){ + if ((audiodata & 0xF0) == 0xA0){ + Meta_Put(meta_out, "audio", "init", std::string((char*)FLV_in.data+13, (size_t)FLV_in.len-17)); + }else{ + Meta_Put(meta_out, "audio", "init", std::string((char*)FLV_in.data+12, (size_t)FLV_in.len-16)); + } + continue;//skip rest of parsing, get next tag. + } + pack_out = DTSC::DTMI("audio", DTSC::DTMI_ROOT); + pack_out.addContent(DTSC::DTMI("datatype", "audio")); + pack_out.addContent(DTSC::DTMI("time", FLV_in.tagTime())); + if (!Meta_Has(meta_out, "audio", "codec")){ + switch (audiodata & 0xF0){ + case 0x20: Meta_Put(meta_out, "audio", "codec", "MP3"); break; + case 0xA0: Meta_Put(meta_out, "audio", "codec", "AAC"); break; + default: Meta_Put(meta_out, "audio", "codec", "?"); break; + } + } + if (!Meta_Has(meta_out, "audio", "rate")){ + switch (audiodata & 0x0C){ + case 0x0: Meta_Put(meta_out, "audio", "rate", 5500); break; + case 0x4: Meta_Put(meta_out, "audio", "rate", 11000); break; + case 0x8: Meta_Put(meta_out, "audio", "rate", 22000); break; + case 0xC: Meta_Put(meta_out, "audio", "rate", 44000); break; + } + } + if (!Meta_Has(meta_out, "audio", "size")){ + switch (audiodata & 0x02){ + case 0x0: Meta_Put(meta_out, "audio", "size", 8); break; + case 0x2: Meta_Put(meta_out, "audio", "size", 16); break; + } + } + if (!Meta_Has(meta_out, "audio", "channels")){ + switch (audiodata & 0x01){ + case 0x0: Meta_Put(meta_out, "audio", "channels", 1); break; + case 0x1: Meta_Put(meta_out, "audio", "channels", 2); break; + } + } + if ((audiodata & 0xF0) == 0xA0){ + pack_out.addContent(DTSC::DTMI("data", std::string((char*)FLV_in.data+13, (size_t)FLV_in.len-17))); + }else{ + pack_out.addContent(DTSC::DTMI("data", std::string((char*)FLV_in.data+12, (size_t)FLV_in.len-16))); + } + if (sending){ + std::cout << pack_out.Pack(true); + }else{ + prebuffer << pack_out.Pack(true); + } + } + if (FLV_in.data[0] == 0x09){ + char videodata = FLV_in.data[11]; + if (FLV_in.needsInitData() && FLV_in.isInitData()){ + if ((videodata & 0x0F) == 7){ + Meta_Put(meta_out, "video", "init", std::string((char*)FLV_in.data+16, (size_t)FLV_in.len-20)); + }else{ + Meta_Put(meta_out, "video", "init", std::string((char*)FLV_in.data+12, (size_t)FLV_in.len-16)); + } + continue;//skip rest of parsing, get next tag. + } + if (!Meta_Has(meta_out, "video", "codec")){ + switch (videodata & 0x0F){ + case 2: Meta_Put(meta_out, "video", "codec", "H263"); break; + case 4: Meta_Put(meta_out, "video", "codec", "VP6"); break; + case 7: Meta_Put(meta_out, "video", "codec", "H264"); break; + default: Meta_Put(meta_out, "video", "codec", "?"); break; + } + } + pack_out = DTSC::DTMI("video", DTSC::DTMI_ROOT); + pack_out.addContent(DTSC::DTMI("datatype", "video")); + switch (videodata & 0xF0){ + case 0x10: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; + case 0x20: pack_out.addContent(DTSC::DTMI("interframe", 1)); break; + case 0x30: pack_out.addContent(DTSC::DTMI("disposableframe", 1)); break; + case 0x40: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; + case 0x50: continue; break;//the video info byte we just throw away - useless to us... + } + if ((videodata & 0x0F) == 7){ + switch (FLV_in.data[12]){ + case 1: pack_out.addContent(DTSC::DTMI("nalu", 1)); break; + case 2: pack_out.addContent(DTSC::DTMI("nalu_end", 1)); break; + } + int offset = 0; + ((char*)(&offset))[0] = FLV_in.data[13]; + ((char*)(&offset))[1] = FLV_in.data[14]; + ((char*)(&offset))[2] = FLV_in.data[15]; + offset >>= 8; + pack_out.addContent(DTSC::DTMI("offset", offset)); + } + pack_out.addContent(DTSC::DTMI("time", FLV_in.tagTime())); + pack_out.addContent(DTSC::DTMI("data", std::string((char*)FLV_in.data+12, (size_t)FLV_in.len-16))); + if (sending){ + std::cout << pack_out.Pack(true); + }else{ + prebuffer << pack_out.Pack(true); + } + } + } + } + + // if the FLV input is very short, do output it correctly... + if (!sending){ + std::cerr << "EOF - outputting buffer..." << std::endl; + meta_out.Pack(true); + meta_out.packed.replace(0, 4, DTSC::Magic_Header); + std::cout << meta_out.packed; + std::cout << prebuffer.rdbuf(); + } + std::cerr << "Done!" << std::endl; + + return 0; + }//FLV2DTSC + +};//Buffer namespace + +/// Entry point for FLV2DTSC, simply calls Converters::FLV2DTSC(). +int main(){ + return Converters::FLV2DTSC(); +}//main diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 9d046287..8301e5b3 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -35,6 +35,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (buffer.length() < len+8){return false;} metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); buffer.erase(0, len+8); + return false; } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); @@ -56,6 +57,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ buffer.erase(0, len+8); while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); + return true; } #if DEBUG >= 2 std::cerr << "Error: Invalid DTMI data! I *will* get stuck!" << std::endl; @@ -92,25 +94,18 @@ bool DTSC::Stream::hasAudio(){ } /// Returns a packed DTSC packet, ready to sent over the network. -std::string DTSC::Stream::outPacket(unsigned int num){ - std::string tmp; - unsigned int size; - tmp = Magic_Packet; - size = htonl(buffers[num].Pack().length()); - tmp.append((char*)&size, 4); - tmp.append(buffers[num].Pack()); - return tmp; +std::string & DTSC::Stream::outPacket(unsigned int num){ + buffers[num].Pack(true); + return buffers[num].packed; } /// Returns a packed DTSC header, ready to sent over the network. -std::string DTSC::Stream::outHeader(){ - std::string tmp; - unsigned int size; - tmp = Magic_Header; - size = htonl(metadata.Pack().length()); - tmp.append((char*)&size, 4); - tmp.append(metadata.Pack()); - return tmp; +std::string & DTSC::Stream::outHeader(){ + if ((metadata.packed.length() < 4) || !metadata.netpacked){ + metadata.Pack(true); + metadata.packed.replace(0, 4, Magic_Header); + } + return metadata.packed; } /// advances all given out and internal Ring classes to point to the new buffer, after one has been added. @@ -198,7 +193,17 @@ int DTSC::DTMI::hasContent(){return contents.size();}; /// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. /// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. -void DTSC::DTMI::addContent(DTSC::DTMI c){contents.push_back(c); packed = "";}; +/// If the indice name already exists, replaces the indice. +void DTSC::DTMI::addContent(DTSC::DTMI c){ + std::vector::iterator it; + for (it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == c.Indice()){ + contents.erase(it); + break; + } + } + contents.push_back(c); packed = ""; +}; /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. @@ -237,11 +242,11 @@ DTSC::DTMI::DTMI(){ };//default constructor /// Constructor for numeric objects. -/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// The object type is by default DTMItype::DTMI_INT, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param val The numeric value of this object. Numeric objects only support uint64_t values. /// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num type initializer +DTSC::DTMI::DTMI(std::string indice, uint64_t val, DTSC::DTMItype setType){//num type initializer myIndice = indice; myType = setType; strval = ""; @@ -249,8 +254,6 @@ DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num t }; /// Constructor for string objects. -/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. -/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The string value of this object. /// \param setType The object type to force this object to. @@ -262,8 +265,7 @@ DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){// }; /// Constructor for container objects. -/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param indice The string indice of this object in its container, or empty string if none. /// \param setType The object type to force this object to. DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer myIndice = indice; @@ -290,7 +292,13 @@ void DTSC::DTMI::Print(std::string indent){ // print my numeric or string contents switch (myType){ case DTMI_INT: std::cerr << numval; break; - case DTMI_STRING: std::cerr << strval; break; + case DTMI_STRING: + if (strval.length() > 200 || ((strval.length() > 1) && ( (strval[0] < 'A') || (strval[0] > 'z') ) )){ + std::cerr << strval.length() << " bytes of data"; + }else{ + std::cerr << strval; + } + break; default: break;//we don't care about the rest, and don't want a compiler warning... } std::cerr << std::endl; @@ -303,11 +311,22 @@ void DTSC::DTMI::Print(std::string indent){ /// Packs the DTMI to a std::string for transfer over the network. /// If a packed version already exists, does not regenerate it. /// If the object is a container type, this function will call itself recursively and contain all contents. -std::string DTSC::DTMI::Pack(){ - if (packed != ""){return packed;} +/// \arg netpack If true, will pack as a full DTMI packet, if false only as the contents without header. +std::string DTSC::DTMI::Pack(bool netpack){ + if (packed != ""){ + if (netpacked == netpack){return packed;} + if (netpacked){ + packed.erase(0, 8); + }else{ + unsigned int size = htonl(packed.length()); + packed.insert(0, (char*)&size, 4); + packed.insert(0, Magic_Packet); + } + netpacked = !netpacked; + return packed; + } std::string r = ""; - //skip output of DDV container types, they do not exist. Only output their contents. - if (myType != DTMI_ROOT){r += myType;} + r += myType; //output the properly formatted data stream for this object's contents. switch (myType){ case DTMI_INT: @@ -324,6 +343,7 @@ std::string DTSC::DTMI::Pack(){ r += strval; break; case DTMI_OBJECT: + case DTMI_ROOT: if (contents.size() > 0){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ r += it->Indice().size() / 256; @@ -332,20 +352,19 @@ std::string DTSC::DTMI::Pack(){ r += it->Pack(); } } - r += (char)0; r += (char)0; r += (char)9; - break; - case DTMI_ROOT://only send contents - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - r += it->Pack(); - } - } + r += (char)0x0; r += (char)0x0; r += (char)0xEE; break; case DTMI_OBJ_END: break; } packed = r; - return r; + netpacked = netpack; + if (netpacked){ + unsigned int size = htonl(packed.length()); + packed.insert(0, (char*)&size, 4); + packed.insert(0, Magic_Packet); + } + return packed; };//pack /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. @@ -372,7 +391,7 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un tmpdbl[2] = data[i+6]; tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; - i+=9;//skip 8(a double)+1 forwards + i+=9;//skip 8(an uint64_t)+1 forwards return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); break; case DTMI_STRING: @@ -382,17 +401,30 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un i += tmpi + 5;//skip length+size+1 forwards return DTSC::DTMI(name, tmpstr, DTMI_STRING); break; - case DTMI_OBJECT:{ + case DTMI_ROOT:{ ++i; - DTSC::DTMI ret(name, DTMI_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + DTSC::DTMI ret(name, DTMI_ROOT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x000009 + i += 3;//skip 0x0000EE + return ret; + } break; + case DTMI_OBJECT:{ + ++i; + DTSC::DTMI ret(name, DTMI_OBJECT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x0000EE return ret; } break; } @@ -403,22 +435,18 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un }//parseOne /// Parses a C-string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +/// This function will find one DTMI object in the string and return it. DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ - DTSC::DTMI ret("returned", DTMI_ROOT);//container type - unsigned int i = 0, j = 0; - while (i < len){ - ret.addContent(parseOneDTMI(data, len, i, "")); - if (i > j){j = i;}else{return ret;} - } + DTSC::DTMI ret;//container type + unsigned int i = 0; + ret = parseOneDTMI(data, len, i, ""); ret.packed = std::string((char*)data, (size_t)len); + ret.netpacked = false; return ret; }//parse /// Parses a std::string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +/// This function will find one DTMI object in the string and return it. DTSC::DTMI DTSC::parseDTMI(std::string data){ return parseDTMI((const unsigned char*)data.c_str(), data.size()); }//parse diff --git a/util/dtsc.h b/util/dtsc.h index 3a025753..7d5827df 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -45,11 +45,12 @@ namespace DTSC{ DTMI* getContentP(std::string s); DTMI getContent(std::string s); DTMI(); - DTMI(std::string indice, double val, DTMItype setType = DTMI_INT); + DTMI(std::string indice, uint64_t val, DTMItype setType = DTMI_INT); DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); void Print(std::string indent = ""); - std::string Pack(); + std::string Pack(bool netpack = false); + bool netpacked; std::string packed; protected: std::string myIndice; ///< Holds this objects indice, if any. @@ -102,8 +103,8 @@ namespace DTSC{ bool hasVideo(); bool hasAudio(); bool parsePacket(std::string & buffer); - std::string outPacket(unsigned int num); - std::string outHeader(); + std::string & outPacket(unsigned int num); + std::string & outHeader(); Ring * getRing(); void dropRing(Ring * ptr); private: diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 526de7d0..2a7b671c 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -109,7 +109,7 @@ std::string FLV::Tag::tagType(){ case 4: R += "VP6"; break; case 5: R += "VP6Alpha"; break; case 6: R += "ScreenVideo2"; break; - case 7: R += "AVC"; break; + case 7: R += "H264"; break; default: R += "unknown"; break; } R += " video ";