From 85a4ed5d8ca05fc102ce199018e7528037fff7ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 00:35:01 +0200 Subject: [PATCH 1/5] Added RTMPChunk to FLV converter to FLV lib, improved RTMP debugger to allow for dumping RTMP streams to FLV, improved RTMP debugger to show and work with more details --- util/flv_tag.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- util/flv_tag.h | 3 +++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 3be1f4e1..cc44c24d 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -8,7 +8,10 @@ #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 +212,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 +244,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..862cdd68 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -4,6 +4,7 @@ #pragma once #include "socket.h" #include +#include "rtmpchunks.h" /// This namespace holds all FLV-parsing related functionality. namespace FLV { @@ -30,7 +31,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); /// Date: Sun, 21 Aug 2011 17:27:38 +0200 Subject: [PATCH 2/5] Remove second streambegin from RTMP server, attempted fixes in SendUSR/SendCTL RTMP chunking functions --- util/rtmpchunks.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index 9ab9aaa7..574bcf53 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -162,7 +162,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; @@ -199,7 +199,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 +215,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 +232,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,7 +270,7 @@ 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 @@ -344,7 +344,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 +397,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 +433,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]; From 693b9e47e5d47a1ef94f16400a968ada51acbcbe Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 18:45:59 +0200 Subject: [PATCH 3/5] Some RTMP timestamp issues found and fixed, as well as some RTMP Parser fixes (but now segfaults? Needs more testing...) --- util/rtmpchunks.cpp | 14 ++++++++------ util/rtmpchunks.h | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index 574bcf53..f6f11076 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -42,6 +42,7 @@ std::string RTMPStream::Chunk::Pack(){ unsigned int tmpi; unsigned char chtype = 0x00; timestamp -= firsttime; + if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -215,7 +216,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); - *(unsigned 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 +233,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); - *(unsigned int*)((char*)ch.data.c_str()+2) = htonl(data); - *(unsigned 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(); @@ -274,7 +275,8 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ 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 +298,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 +312,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; diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index 8feeefb0..b114f440 100644 --- a/util/rtmpchunks.h +++ b/util/rtmpchunks.h @@ -30,6 +30,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. From d526616cd32824fe791ce1a5390fe81397b83bba Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 20:49:42 +0200 Subject: [PATCH 4/5] Attempts at fixing timestamp issues, improved FLV/RTMP cooperatibility, fixed several bugs --- util/flv_tag.cpp | 1 + util/flv_tag.h | 6 +++++- util/rtmpchunks.cpp | 40 ++++++++++++++++++++++++++++------------ util/rtmpchunks.h | 6 ++++++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index cc44c24d..526de7d0 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -2,6 +2,7 @@ /// 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 diff --git a/util/flv_tag.h b/util/flv_tag.h index 862cdd68..1350c870 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -4,7 +4,11 @@ #pragma once #include "socket.h" #include -#include "rtmpchunks.h" + +//forward declaration of RTMPStream::Chunk to avoid circular dependencies. +namespace RTMPStream{ + class Chunk; +}; /// This namespace holds all FLV-parsing related functionality. namespace FLV { diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index f6f11076..ad0e6c57 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -2,6 +2,7 @@ /// Holds all code for the RTMPStream namespace. #include "rtmpchunks.h" +#include "flv_tag.h" #include "crypto.h" char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake @@ -41,8 +42,8 @@ std::string RTMPStream::Chunk::Pack(){ RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; - timestamp -= firsttime; - if (timestamp < prev.timestamp){timestamp = prev.timestamp;} + //timestamp -= firsttime; + //if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -77,15 +78,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){ @@ -99,10 +100,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){ @@ -174,6 +175,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; diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index b114f440..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{ @@ -51,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); From 005f52a6423a9ca6bf71cadd7d0eedee8f3f1a39 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 21:03:21 +0200 Subject: [PATCH 5/5] More timestamp attempts... --- util/rtmpchunks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index ad0e6c57..6aa5f242 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -43,7 +43,6 @@ std::string RTMPStream::Chunk::Pack(){ unsigned int tmpi; unsigned char chtype = 0x00; //timestamp -= firsttime; - //if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -56,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);