diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 3be1f4e1..526de7d0 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -2,13 +2,17 @@ /// Holds all code for the FLV namespace. #include "flv_tag.h" +#include "rtmpchunks.h" #include //for Tag::FileLoader #include //for Tag::FileLoader #include //for Tag::FileLoader #include //malloc #include //memcpy -char FLV::Header[13]; ///< Holds the last FLV header parsed. +/// Holds the last FLV header parsed. +/// Defaults to a audio+video header on FLV version 0x01 if no header received yet. +char FLV::Header[13] = {'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09, 0, 0, 0, 0}; + bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. std::string FLV::Error_Str = ""; @@ -209,6 +213,16 @@ FLV::Tag::Tag(const Tag& O){ isKeyframe = O.isKeyframe; }//copy constructor + +/// Copy constructor from a RTMP chunk. +/// Copies the contents of a RTMP chunk into a valid FLV tag. +/// Exactly the same as making a chunk by through the default (empty) constructor +/// and then calling FLV::Tag::ChunkLoader with the chunk as argument. +FLV::Tag::Tag(const RTMPStream::Chunk& O){ + len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; + ChunkLoader(O); +} + /// Assignment operator - works exactly like the copy constructor. /// This operator checks for self-assignment. FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ @@ -231,6 +245,32 @@ FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ return *this; }//assignment operator +/// FLV loader function from chunk. +/// Copies the contents and wraps it in a FLV header. +bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ + len = O.len + 15; + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + memcpy(data+11, &(O.data[0]), O.len); + } + ((unsigned int *)(data+len-4))[0] = O.len; + data[0] = O.msg_type_id; + data[3] = O.len & 0xFF; + data[2] = (O.len >> 8) & 0xFF; + data[1] = (O.len >> 16) & 0xFF; + tagTime(O.timestamp); + return true; +} + + /// Helper function for FLV::MemLoader. /// This function will try to read count bytes from data buffer D into buffer. /// This function should be called repeatedly until true. diff --git a/util/flv_tag.h b/util/flv_tag.h index 1d7bbc41..1350c870 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -5,6 +5,11 @@ #include "socket.h" #include +//forward declaration of RTMPStream::Chunk to avoid circular dependencies. +namespace RTMPStream{ + class Chunk; +}; + /// This namespace holds all FLV-parsing related functionality. namespace FLV { //variables @@ -30,7 +35,9 @@ namespace FLV { Tag(); ///< Constructor for a new, empty, tag. Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag. Tag & operator= (const Tag& O); ///< Assignment operator - works exactly like the copy constructor. + Tag(const RTMPStream::Chunk& O); /// 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -54,6 +55,8 @@ std::string RTMPStream::Chunk::Pack(){ } } } + //override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel + if (timestamp < prev.timestamp){chtype = 0x00;} } if (cs_id <= 63){ output += (unsigned char)(chtype | cs_id); @@ -76,15 +79,15 @@ std::string RTMPStream::Chunk::Pack(){ tmpi = timestamp - prev.timestamp; } if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} - output += (unsigned char)(tmpi / (256*256)); - output += (unsigned char)(tmpi / 256); - output += (unsigned char)(tmpi % 256); + output += (unsigned char)((tmpi >> 16) & 0xff); + output += (unsigned char)((tmpi >> 8) & 0xff); + output += (unsigned char)(tmpi & 0xff); if (chtype != 0x80){ //len tmpi = len; - output += (unsigned char)(tmpi / (256*256)); - output += (unsigned char)(tmpi / 256); - output += (unsigned char)(tmpi % 256); + output += (unsigned char)((tmpi >> 16) & 0xff); + output += (unsigned char)((tmpi >> 8) & 0xff); + output += (unsigned char)(tmpi & 0xff); //msg type id output += (unsigned char)msg_type_id; if (chtype != 0x40){ @@ -98,10 +101,10 @@ std::string RTMPStream::Chunk::Pack(){ } //support for 0x00ffffff timestamps if (ntime){ - output += (unsigned char)(ntime % 256); - output += (unsigned char)(ntime / 256); - output += (unsigned char)(ntime / (256*256)); - output += (unsigned char)(ntime / (256*256*256)); + output += (unsigned char)(ntime & 0xff); + output += (unsigned char)((ntime >> 8) & 0xff); + output += (unsigned char)((ntime >> 16) & 0xff); + output += (unsigned char)((ntime >> 24) & 0xff); } len_left = 0; while (len_left < len){ @@ -162,7 +165,7 @@ std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, /// \param ts Timestamp of the media data, relative to current system time. std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ RTMPStream::Chunk ch; - ch.cs_id = msg_type_id; + ch.cs_id = msg_type_id+42; ch.timestamp = ts; ch.len = len; ch.real_len = len; @@ -173,6 +176,21 @@ std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * dat return ch.Pack(); }//SendMedia +/// Packs up a chunk with media contents. +/// \param tag FLV::Tag with media to send. +std::string RTMPStream::SendMedia(FLV::Tag & tag){ + RTMPStream::Chunk ch; + ch.cs_id = ((unsigned char)tag.data[0]); + ch.timestamp = tag.tagTime(); + ch.len = tag.len-15; + ch.real_len = tag.len-15; + ch.len_left = 0; + ch.msg_type_id = (unsigned char)tag.data[0]; + ch.msg_stream_id = 1; + ch.data.append(tag.data+11, (size_t)(tag.len-15)); + return ch.Pack(); +}//SendMedia + /// Packs up a chunk for a control message with 1 argument. std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ RTMPStream::Chunk ch; @@ -199,7 +217,7 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned ch.msg_type_id = type; ch.msg_stream_id = 0; ch.data.resize(5); - *(int*)((char*)ch.data.c_str()) = htonl(data); + *(unsigned int*)((char*)ch.data.c_str()) = htonl(data); ch.data[4] = data2; return ch.Pack(); }//SendCTL @@ -215,7 +233,7 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(6); - *(int*)((char*)ch.data.c_str()+2) = htonl(data); + *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); @@ -232,8 +250,8 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(10); - *(int*)((char*)ch.data.c_str()+2) = htonl(data); - *(int*)((char*)ch.data.c_str()+6) = htonl(data2); + *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); + *(unsigned int*)(((char*)ch.data.c_str())+6) = htonl(data2); ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); @@ -270,11 +288,12 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ cs_id = chunktype & 0x3F; break; } - + RTMPStream::Chunk prev = lastrecv[cs_id]; //process the rest of the header, for each chunk type - switch (chunktype & 0xC0){ + headertype = chunktype & 0xC0; + switch (headertype){ case 0x00: if (indata.size() < i+11) return false; //can't read whole header timestamp = indata[i++]*256*256; @@ -296,7 +315,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; - timestamp += prev.timestamp; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} len = indata[i++]*256*256; len += indata[i++]*256; len += indata[i++]; @@ -310,7 +329,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; - timestamp += prev.timestamp; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} len = prev.len; len_left = prev.len_left; msg_type_id = prev.msg_type_id; @@ -344,7 +363,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp += indata[i++]*256; timestamp += indata[i++]; } - + //read data if length > 0, and allocate it if (real_len > 0){ if (prev.len_left > 0){ @@ -397,22 +416,22 @@ bool RTMPStream::doHandshake(){ uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; - + #if DEBUG >= 4 fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); #endif - + //FIRST 1536 bytes from server response //compute DH key position uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme); uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme); - + //generate DH key DHWrapper dhWrapper(1024); if (!dhWrapper.Initialize()) return false; if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; - + if (encrypted) { uint8_t secretKey[128]; if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; @@ -433,7 +452,7 @@ bool RTMPStream::doHandshake(){ memcpy(Server + serverDigestOffset, pTempHash, 32); delete[] pTempBuffer; delete[] pTempHash; - + //SECOND 1536 bytes from server response uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); pTempHash = new uint8_t[512]; diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index 8feeefb0..ff4eee4a 100644 --- a/util/rtmpchunks.h +++ b/util/rtmpchunks.h @@ -9,6 +9,11 @@ #include #include +//forward declaration of FLV::Tag to avoid circular dependencies. +namespace FLV{ + class Tag; +}; + /// Contains all functions and classes needed for RTMP connections. namespace RTMPStream{ @@ -30,6 +35,7 @@ namespace RTMPStream{ /// Holds a single RTMP chunk, either send or receive direction. class Chunk{ public: + unsigned char headertype; ///< For input chunks, the type of header. This is calculated automatically for output chunks. unsigned int cs_id; ///< ContentStream ID unsigned int timestamp; ///< Timestamp of this chunk. unsigned int len; ///< Length of the complete chunk. @@ -50,6 +56,7 @@ namespace RTMPStream{ std::string SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); std::string SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); + std::string SendMedia(FLV::Tag & tag); std::string SendCTL(unsigned char type, unsigned int data); std::string SendCTL(unsigned char type, unsigned int data, unsigned char data2); std::string SendUSR(unsigned char type, unsigned int data);