From a6b072988c4b4966add925cf989b950d7503d3d6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 24 Aug 2012 11:32:02 +0200 Subject: [PATCH] Added toNetPacked() to JSON, removed DTSC::DTMI completely (now superseded by JSON). --- lib/dtsc.cpp | 326 +++--------------------------------------------- lib/dtsc.h | 55 +------- lib/flv_tag.cpp | 102 +++++++-------- lib/json.cpp | 35 ++++++ lib/json.h | 6 + 5 files changed, 113 insertions(+), 411 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 72042b22..86d36257 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -26,7 +26,7 @@ DTSC::Stream::Stream(unsigned int rbuffers){ /// Returns the time in milliseconds of the last received packet. /// This is _not_ the time this packet was received, only the stored time. unsigned int DTSC::Stream::getTime(){ - return buffers.front().getContentP("time")->NumValue(); + return buffers.front()["time"].asInt(); } /// Attempts to parse a packet from the given std::string buffer. @@ -40,20 +40,22 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len+8){return false;} - metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); + unsigned int i = 0; + metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); 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]); if (buffer.length() < len+8){return false;} - buffers.push_front(DTSC::DTMI("empty", DTMI_ROOT)); - buffers.front() = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); + buffers.push_front(JSON::Value()); + unsigned int i = 0; + buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); datapointertype = INVALID; - if (buffers.front().getContentP("data")){ - datapointer = &(buffers.front().getContentP("data")->StrValue()); - if (buffers.front().getContentP("datatype")){ - std::string tmp = buffers.front().getContentP("datatype")->StrValue(); + if (buffers.front().isMember("data")){ + datapointer = &(buffers.front()["data"].strVal); + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "audio"){datapointertype = AUDIO;} if (tmp == "meta"){datapointertype = META;} @@ -91,7 +93,7 @@ std::string & DTSC::Stream::lastData(){ /// Returns the packed in this buffer number. /// \arg num Buffer number. -DTSC::DTMI & DTSC::Stream::getPacket(unsigned int num){ +JSON::Value & DTSC::Stream::getPacket(unsigned int num){ return buffers[num]; } @@ -102,29 +104,24 @@ DTSC::datatype DTSC::Stream::lastType(){ /// Returns true if the current stream contains at least one video track. bool DTSC::Stream::hasVideo(){ - return (metadata.getContentP("video") != 0); + return metadata.isMember("video"); } /// Returns true if the current stream contains at least one audio track. bool DTSC::Stream::hasAudio(){ - return (metadata.getContentP("audio") != 0); + return metadata.isMember("audio"); } /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(unsigned int num){ static std::string emptystring; if (num >= buffers.size()) return emptystring; - buffers[num].Pack(true); - return buffers[num].packed; + return buffers[num].toNetPacked(); } /// Returns a packed DTSC header, ready to sent over the network. 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; + return metadata.toNetPacked(); } /// advances all given out and internal Ring classes to point to the new buffer, after one has been added. @@ -142,7 +139,7 @@ void DTSC::Stream::advanceRings(){ dit->b++; if (dit->b >= buffers.size()){keyframes.erase(dit); break;} } - if ((lastType() == VIDEO) && (buffers.front().getContentP("keyframe"))){ + if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ keyframes.push_front(DTSC::Ring(0)); } //increase buffer size if no keyframes available @@ -187,297 +184,6 @@ DTSC::Stream::~Stream(){ for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} } -/// Returns the std::string Indice for the current object, if available. -/// Returns an empty string if no indice exists. -std::string DTSC::DTMI::Indice(){return myIndice;}; - -/// Returns the DTSC::DTMItype AMF0 object type for this object. -DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; - -/// Returns the numeric value of this object, if available. -/// If this object holds no numeric value, 0 is returned. -uint64_t & DTSC::DTMI::NumValue(){return numval;}; - -/// Returns the std::string value of this object, if available. -/// If this object holds no string value, an empty string is returned. -std::string & DTSC::DTMI::StrValue(){return strval;}; - -/// Returns the C-string value of this object, if available. -/// If this object holds no string value, an empty C-string is returned. -const char * DTSC::DTMI::Str(){return strval.c_str();}; - -/// Returns a count of the amount of objects this object currently holds. -/// If this object is not a container type, this function will always return 0. -int DTSC::DTMI::hasContent(){return contents.size();}; - -/// Returns true if this DTSC::DTMI value is non-default. -/// Non-default means it is either not a root element or has content. -bool DTSC::DTMI::isEmpty(){ - if (myType != DTMI_ROOT){return false;} - return (hasContent() == 0); -}; - -/// 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. -/// 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 null pointer if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(int i){ - if (contents.size() <= (unsigned int)i){return 0;} - return &contents.at(i); -}; - -/// Returns a copy of the object held at indice i. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(int i){return contents.at(i);}; - -/// Returns a pointer to the object held at indice s. -/// Returns NULL if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} - } - return 0; -}; - -/// Returns a copy of the object held at indice s. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} - } - return DTSC::DTMI("error", DTMI_ROOT); -}; - -/// Default constructor. -/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) -DTSC::DTMI::DTMI(){ - *this = DTSC::DTMI("error", DTMI_ROOT); -};//default constructor - -/// Constructor for numeric objects. -/// 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 objects only support uint64_t values. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, uint64_t val, DTSC::DTMItype setType){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; -}; - -/// Constructor for string objects. -/// \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. -DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; -}; - -/// Constructor for container objects. -/// \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; - myType = setType; - strval = ""; - numval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -void DTSC::DTMI::Print(std::string indent){ - std::cerr << indent; - // print my type - switch (myType){ - case DTMI_INT: std::cerr << "Integer"; break; - case DTMI_STRING: std::cerr << "String"; break; - case DTMI_OBJECT: std::cerr << "Object"; break; - case DTMI_OBJ_END: std::cerr << "Object end"; break; - case DTMI_ROOT: std::cerr << "Root Node"; break; - } - // print my string indice, if available - std::cerr << " " << myIndice << " "; - // print my numeric or string contents - switch (myType){ - case DTMI_INT: std::cerr << numval; 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; - // if I hold other objects, print those too, recursively. - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} - } -};//print - -/// 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. -/// \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 = ""; - r += myType; - //output the properly formatted data stream for this object's contents. - switch (myType){ - case DTMI_INT: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - break; - case DTMI_STRING: - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - 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; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } - r += (char)0x0; r += (char)0x0; r += (char)0xEE; - break; - case DTMI_OBJ_END: - break; - } - packed = 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. -/// This function updates i every call with the new position in the data. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \param i Current parsing position in the raw data. -/// \param name Indice name for any new object created. -/// \returns A single DTSC::DTMI, parsed from the raw data. -DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ - unsigned int tmpi = 0; - unsigned char tmpdbl[8]; - uint64_t * d;// hack to work around strict aliasing - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif - switch (data[i]){ - case DTMI_INT: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(an uint64_t)+1 forwards - d = (uint64_t*)tmpdbl; - return DTSC::DTMI(name, *d, DTMI_INT); - break; - case DTMI_STRING:{ - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data - i += tmpi + 5;//skip length+size+1 forwards - return DTSC::DTMI(name, tmpstr, DTMI_STRING); - } break; - case DTMI_ROOT:{ - ++i; - 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 - std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set 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; - 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 - std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set 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; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); - #endif - return DTSC::DTMI("error", DTMI_ROOT); -}//parseOne - -/// Parses a C-string to a valid 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;//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 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 - /// Open a filename for DTSC reading/writing. /// If create is true and file does not exist, attempt to create. DTSC::File::File(std::string filename, bool create){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 8a3815a6..bc23d618 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -9,6 +9,7 @@ #include #include #include //for FILE +#include "json.h" @@ -21,6 +22,7 @@ /// - fpks (int, frames per kilosecond (FPS * 1000)) /// - bps (int, bytes per second) /// - init (string, init data) +/// - keycount (int, count of keyframes) /// - keyms (int, average ms per keyframe) /// - keyvar (int, max ms per keyframe variance) /// - keys (array of byte position ints - first is first keyframe, last is last keyframe, in between have ~equal spacing) @@ -49,53 +51,6 @@ /// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) namespace DTSC{ - /// Enumerates all possible DTMI types. - enum DTMItype { - DTMI_INT = 0x01, ///< Unsigned 64-bit integer. - DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. - DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. - DTMI_OBJ_END = 0xEE, ///< End of object marker. - DTMI_ROOT = 0xFF ///< Root node for all DTMI data. - }; - - /// Recursive class that holds DDVTECH MediaInfo. - class DTMI { - public: - std::string Indice(); - DTMItype GetType(); - uint64_t & NumValue(); - std::string & StrValue(); - const char * Str(); - int hasContent(); - bool isEmpty(); - void addContent(DTMI c); - DTMI* getContentP(int i); - DTMI getContent(int i); - DTMI* getContentP(std::string s); - DTMI getContent(std::string s); - DTMI(); - 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(bool netpack = false); - bool netpacked; - std::string packed; - protected: - std::string myIndice; ///< Holds this objects indice, if any. - DTMItype myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - uint64_t numval; ///< Holds this objects numeric value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType - - /// Parses a C-string to a valid DTSC::DTMI. - DTMI parseDTMI(const unsigned char * data, unsigned int len); - /// Parses a std::string to a valid DTSC::DTMI. - DTMI parseDTMI(std::string data); - /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. - DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); - /// This enum holds all possible datatypes for DTSC packets. enum datatype { AUDIO, ///< Stream Audio data @@ -141,8 +96,8 @@ namespace DTSC{ Stream(); ~Stream(); Stream(unsigned int buffers); - DTSC::DTMI metadata; - DTSC::DTMI & getPacket(unsigned int num = 0); + JSON::Value metadata; + JSON::Value & getPacket(unsigned int num = 0); datatype lastType(); std::string & lastData(); bool hasVideo(); @@ -154,7 +109,7 @@ namespace DTSC{ unsigned int getTime(); void dropRing(Ring * ptr); private: - std::deque buffers; + std::deque buffers; std::set rings; std::deque keyframes; void advanceRings(); diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 95070b19..2c005dc1 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -257,14 +257,14 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; - if (S.metadata.getContentP("video") && S.metadata.getContentP("video")->getContentP("codec")){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){len += 4;} + if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){ + if (S.metadata["video"]["codec"].asString() == "H264"){len += 4;} } break; case DTSC::AUDIO: len = S.lastData().length() + 16; - if (S.metadata.getContentP("audio") && S.metadata.getContentP("audio")->getContentP("codec")){ - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){len += 1;} + if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){ + if (S.metadata["audio"]["codec"].asString() == "AAC"){len += 1;} } break; case DTSC::META: @@ -289,18 +289,18 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ memcpy(data+12, S.lastData().c_str(), S.lastData().length()); }else{ memcpy(data+16, S.lastData().c_str(), S.lastData().length()); - if (S.getPacket().getContentP("nalu")){data[12] = 1;}else{data[12] = 2;} - int offset = S.getPacket().getContentP("offset")->NumValue(); + if (S.getPacket().isMember("nalu")){data[12] = 1;}else{data[12] = 2;} + int offset = S.getPacket()["offset"].asInt(); data[13] = (offset >> 16) & 0xFF; data[14] = (offset >> 8) & 0XFF; data[15] = offset & 0xFF; } data[11] = 0; - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){data[11] += 7;} - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){data[11] += 2;} - if (S.getPacket().getContentP("keyframe")){data[11] += 0x10;} - if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} - if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} + if (S.metadata["video"]["codec"].asString() == "H264"){data[11] += 7;} + if (S.metadata["video"]["codec"].asString() == "H263"){data[11] += 2;} + if (S.getPacket().isMember("keyframe")){data[11] += 0x10;} + if (S.getPacket().isMember("interframe")){data[11] += 0x20;} + if (S.getPacket().isMember("disposableframe")){data[11] += 0x30;} break; case DTSC::AUDIO:{ if ((unsigned int)len == S.lastData().length() + 16){ @@ -310,9 +310,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[12] = 1;//raw AAC data, not sequence header } data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} + if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if(datarate >= 22050){ @@ -320,8 +320,8 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else if(datarate >= 11025){ data[11] += 0x04; } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} + if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} break; } case DTSC::META: @@ -343,7 +343,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[8] = 0; data[9] = 0; data[10] = 0; - tagTime(S.getPacket().getContentP("time")->NumValue()); + tagTime(S.getPacket()["time"].asInt()); return true; } @@ -364,8 +364,8 @@ void FLV::Tag::setLen(){ /// Takes the DTSC Video init data and makes it into FLV. /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ - len = S.metadata.getContentP("video")->getContentP("init")->StrValue().length() + 20; + if (S.metadata["video"]["codec"].asString() == "H264"){ + len = S.metadata["video"]["init"].asString().length() + 20; } if (len > 0){ if (!data){ @@ -377,7 +377,7 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+16, S.metadata.getContentP("video")->getContentP("init")->StrValue().c_str(), len-20); + memcpy(data+16, S.metadata["video"]["init"].asString().c_str(), len-20); data[12] = 0;//H264 sequence header data[13] = 0; data[14] = 0; @@ -401,8 +401,8 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ len = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ - len = S.metadata.getContentP("audio")->getContentP("init")->StrValue().length() + 17; + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + len = S.metadata["audio"]["init"].asString().length() + 17; } if (len > 0){ if (!data){ @@ -414,12 +414,12 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+13, S.metadata.getContentP("audio")->getContentP("init")->StrValue().c_str(), len-17); + memcpy(data+13, S.metadata["audio"]["init"].asString().c_str(), len-17); data[12] = 0;//AAC sequence header data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} + if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if(datarate >= 22050){ @@ -427,8 +427,8 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ }else if(datarate >= 11025){ data[11] += 0x04; } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} + if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} } setLen(); data[0] = 0x08; @@ -450,54 +450,54 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.getContentP("video")){ + if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ + if (S.metadata["video"]["codec"].asString() == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "VP6"){ + if (S.metadata["video"]["codec"].asString() == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){ + if (S.metadata["video"]["codec"].asString() == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata.getContentP("video")->getContentP("width")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("width")){ + amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata["video"]["width"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata.getContentP("video")->getContentP("height")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("height")){ + amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata["video"]["height"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata.getContentP("video")->getContentP("fpks")->NumValue() / 1000.0, AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("fpks")){ + amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata.getContentP("video")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata["video"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } - if (S.metadata.getContentP("audio")){ + if (S.metadata.isMember("audio")){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ + if (S.metadata["audio"]["codec"].asString() == "AAC"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){ + if (S.metadata["audio"]["codec"].asString() == "MP3"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("channels")){ - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){ + if (S.metadata["audio"].isMember("channels")){ + if (S.metadata["audio"]["channels"].asInt() > 1){ amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); }else{ amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); } } - if (S.metadata.getContentP("audio")->getContentP("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata.getContentP("audio")->getContentP("rate")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("rate")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata.getContentP("audio")->getContentP("size")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("size")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata["audio"]["size"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata.getContentP("audio")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata["audio"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } diff --git a/lib/json.cpp b/lib/json.cpp index 84a244ea..7adf2d32 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -5,6 +5,8 @@ #include #include #include //for uint64_t +#include //for memcpy +#include //for htonl int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; @@ -345,6 +347,7 @@ JSON::Value & JSON::Value::operator[](unsigned int i){ /// Packs to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. +/// As a side effect, this function clear the internal buffer of any object-types. std::string JSON::Value::toPacked(){ std::string r; if (isInt() || isNull() || isBool()){ @@ -374,6 +377,7 @@ std::string JSON::Value::toPacked(){ } } r += (char)0x0; r += (char)0x0; r += (char)0xEE; + strVal.clear(); } if (isArray()){ r += 0x0A; @@ -386,6 +390,37 @@ std::string JSON::Value::toPacked(){ };//toPacked +/// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. +/// Non-object-types will print an error to stderr and return an empty string. +/// This function returns a reference to an internal buffer where the prepared data is kept. +/// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer. +std::string & JSON::Value::toNetPacked(){ + static std::string emptystring; + //check if this is legal + if (myType != OBJECT){ + fprintf(stderr, "Fatal error: Only objects may be NetPacked! Aborting.\n"); + return emptystring; + } + //if sneaky storage doesn't contain correct data, re-calculate it + if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T'){ + std::string packed = toPacked(); + strVal.resize(packed.size() + 8); + //insert proper header for this type of data + if (isMember("data")){ + memcpy((void*)strVal.c_str(), "DTPD", 4); + }else{ + memcpy((void*)strVal.c_str(), "DTSC", 4); + } + //insert the packet length at bytes 4-7 + unsigned int size = htonl(packed.size()); + memcpy((void*)(strVal.c_str() + 4), (void*)&size, 4); + //copy the rest of the string + memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + } + return strVal; +} + + /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) std::string JSON::Value::toString(){ diff --git a/lib/json.h b/lib/json.h index 00840ab8..fee192f2 100644 --- a/lib/json.h +++ b/lib/json.h @@ -6,6 +6,9 @@ #include #include +//empty definition of DTSC::Stream so it can be a friend. +namespace DTSC{class Stream;} + /// JSON-related classes and functions namespace JSON{ @@ -30,6 +33,8 @@ namespace JSON{ int c2hex(int c); static void skipToEnd(std::istream & fromstream); public: + //friends + friend class DTSC::Stream;//for access to strVal //constructors Value(); Value(std::istream & fromstream); @@ -60,6 +65,7 @@ namespace JSON{ Value & operator[](unsigned int i); //handy functions and others std::string toPacked(); + std::string & toNetPacked(); std::string toString(); std::string toPrettyString(int indentation = 0); void append(const Value & rhs);