From b240874ede22c6d4dd9cd9243a48e50b36ed618d Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
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

---
 RTMP_Parser/Makefile |  4 ++--
 RTMP_Parser/main.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++--
 util/flv_tag.cpp     | 41 ++++++++++++++++++++++++++++++-
 util/flv_tag.h       |  3 +++
 4 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/RTMP_Parser/Makefile b/RTMP_Parser/Makefile
index 95aa3b19..442e9db2 100644
--- a/RTMP_Parser/Makefile
+++ b/RTMP_Parser/Makefile
@@ -1,4 +1,4 @@
-SRC = main.cpp ../util/amf.cpp ../util/rtmpchunks.cpp ../util/crypto.cpp
+SRC = main.cpp ../util/amf.cpp ../util/rtmpchunks.cpp ../util/crypto.cpp ../util/flv_tag.cpp ../util/socket.cpp
 OBJ = $(SRC:.cpp=.o)
 OUT = RTMP_Parser
 INCLUDES =
@@ -8,7 +8,7 @@ CC = $(CROSS)g++
 LD = $(CROSS)ld
 AR = $(CROSS)ar
 LIBS = -lssl -lcrypto
-.SUFFIXES: .cpp 
+.SUFFIXES: .cpp
 .PHONY: clean default
 default: $(OUT)
 .cpp.o:
diff --git a/RTMP_Parser/main.cpp b/RTMP_Parser/main.cpp
index 4f03e803..ab2fbc1c 100644
--- a/RTMP_Parser/main.cpp
+++ b/RTMP_Parser/main.cpp
@@ -2,30 +2,61 @@
 /// Debugging tool for RTMP data.
 /// Expects RTMP data of one side of the conversion through stdin, outputs human-readable information to stderr.
 /// Automatically skips 3073 bytes of handshake data.
+/// Optionally reconstructs an FLV file
+/// Singular argument is a bitmask indicating the following (defaulting to 0):
+/// - 0  = Info: Output chunk meanings and fulltext commands to stderr.
+/// - 1  = Reconstruct: Output valid .flv file to stdout.
+/// - 2  = Explicit: Audio/video data details.
+/// - 4  = Verbose: details about every whole chunk.
 
 #define DEBUG 10 //maximum debugging level
 #include <cstdlib>
 #include <iostream>
 #include <fstream>
 #include <string>
+#include "../util/flv_tag.h"
 #include "../util/amf.h"
 #include "../util/rtmpchunks.h"
 
+int Detail = 0;
+#define DETAIL_RECONSTRUCT 1
+#define DETAIL_EXPLICIT 2
+#define DETAIL_VERBOSE 4
+
 /// Debugging tool for RTMP data.
 /// Expects RTMP data of one side of the conversion through stdin, outputs human-readable information to stderr.
 /// Will output FLV file to stdout, if available
 /// Automatically skips 3073 bytes of handshake data.
-int main(){
+int main(int argc, char ** argv){
+
+  if (argc > 1){
+    Detail = atoi(argv[1]);
+    fprintf(stderr, "Detail level set:\n");
+    if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
+      fprintf(stderr, " - Will reconstuct FLV file to stdout\n");
+      std::cout.write(FLV::Header, 13);
+    }
+    if ((Detail & DETAIL_EXPLICIT) == DETAIL_EXPLICIT){
+      fprintf(stderr, " - Will list explicit video/audio data information\n");
+    }
+    if ((Detail & DETAIL_VERBOSE) == DETAIL_VERBOSE){
+      fprintf(stderr, " - Will list verbose chunk information\n");
+    }
+  }
 
   std::string inbuffer;
   while (std::cin.good()){inbuffer += std::cin.get();}//read all of std::cin to temp
   inbuffer.erase(0, 3073);//strip the handshake part
   RTMPStream::Chunk next;
+  FLV::Tag F;//FLV holder
   AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER);
   AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER);
-  
+
 
   while (next.Parse(inbuffer)){
+    if ((Detail & DETAIL_VERBOSE) == DETAIL_VERBOSE){
+      fprintf(stderr, "Chunk info: CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.cs_id, next.timestamp, next.len, next.msg_type_id, next.msg_stream_id);
+    }
     switch (next.msg_type_id){
       case 0://does not exist
         fprintf(stderr, "Error chunk - %i, %i, %i, %i, %i\n", next.cs_id, next.timestamp, next.real_len, next.len_left, next.msg_stream_id);
@@ -84,9 +115,27 @@ int main(){
         break;
       case 8:
         fprintf(stderr, "Received %i bytes audio data\n", next.len);
+        if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
+          F.ChunkLoader(next);
+          if ((Detail & DETAIL_EXPLICIT) == DETAIL_EXPLICIT){
+            std::cerr << "Got a " << F.len << " bytes " << F.tagType() << " FLV tag of time " << F.tagTime() << "." << std::endl;
+          }
+          if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
+            std::cout.write(F.data, F.len);
+          }
+        }
         break;
       case 9:
         fprintf(stderr, "Received %i bytes video data\n", next.len);
+        if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
+          F.ChunkLoader(next);
+          if ((Detail & DETAIL_EXPLICIT) == DETAIL_EXPLICIT){
+            std::cerr << "Got a " << F.len << " bytes " << F.tagType() << " FLV tag of time " << F.tagTime() << "." << std::endl;
+          }
+          if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
+            std::cout.write(F.data, F.len);
+          }
+        }
         break;
       case 15:
         fprintf(stderr, "Received AFM3 data message\n");
@@ -110,6 +159,10 @@ int main(){
         fprintf(stderr, "Received AFM0 data message (metadata):\n");
         amfdata = AMF::parse(next.data);
         amfdata.Print();
+        if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
+          F.ChunkLoader(next);
+          std::cout.write(F.data, F.len);
+        }
         } break;
       case 19:
         fprintf(stderr, "Received AFM0 shared object\n");
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 <stdlib.h> //malloc
 #include <string.h> //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 <string>
+#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); ///<Copy constructor from a RTMP chunk.
       //loader functions
+      bool ChunkLoader(const RTMPStream::Chunk& O);
       bool MemLoader(char * D, unsigned int S, unsigned int & P);
       bool SockLoader(int sock);
       bool SockLoader(Socket::Connection sock);

From 5e7b316d216c6cf85bc7f6c0f6afb30a27859c7b Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
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

---
 Connector_RTMP/main.cpp |  4 ++--
 RTMP_Parser/main.cpp    |  4 ++--
 util/rtmpchunks.cpp     | 24 ++++++++++++------------
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/Connector_RTMP/main.cpp b/Connector_RTMP/main.cpp
index 05a1c889..7ad9a403 100644
--- a/Connector_RTMP/main.cpp
+++ b/Connector_RTMP/main.cpp
@@ -372,7 +372,7 @@ void Connector_RTMP::parseChunk(){
               if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
             }
             streamname = "/tmp/shared_socket_" + streamname;
-            Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
+            //Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
             //send a status reply
             AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
             amfreply.addContent(AMF::Object("", "onStatus"));//status reply
@@ -528,7 +528,7 @@ void Connector_RTMP::parseChunk(){
             if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
           }
           streamname = "/tmp/shared_socket_" + streamname;
-          Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
+          //Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
           //send a status reply
           AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
           amfreply.addContent(AMF::Object("", "onStatus"));//status reply
diff --git a/RTMP_Parser/main.cpp b/RTMP_Parser/main.cpp
index ab2fbc1c..09debfb0 100644
--- a/RTMP_Parser/main.cpp
+++ b/RTMP_Parser/main.cpp
@@ -114,10 +114,10 @@ int main(int argc, char ** argv){
         fprintf(stderr, "CTRL: Set peer bandwidth: %i\n", RTMPStream::snd_window_size);
         break;
       case 8:
-        fprintf(stderr, "Received %i bytes audio data\n", next.len);
         if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
           F.ChunkLoader(next);
           if ((Detail & DETAIL_EXPLICIT) == DETAIL_EXPLICIT){
+            fprintf(stderr, "Received %i bytes audio data\n", next.len);
             std::cerr << "Got a " << F.len << " bytes " << F.tagType() << " FLV tag of time " << F.tagTime() << "." << std::endl;
           }
           if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
@@ -126,10 +126,10 @@ int main(int argc, char ** argv){
         }
         break;
       case 9:
-        fprintf(stderr, "Received %i bytes video data\n", next.len);
         if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
           F.ChunkLoader(next);
           if ((Detail & DETAIL_EXPLICIT) == DETAIL_EXPLICIT){
+            fprintf(stderr, "Received %i bytes video data\n", next.len);
             std::cerr << "Got a " << F.len << " bytes " << F.tagType() << " FLV tag of time " << F.tagTime() << "." << std::endl;
           }
           if ((Detail & DETAIL_RECONSTRUCT) == DETAIL_RECONSTRUCT){
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 dee3f6522810a55e71352bbab2361349b84098a2 Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
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...)

---
 Connector_RTMP/main.cpp |  8 ++++----
 RTMP_Parser/main.cpp    | 18 +++++++++---------
 util/rtmpchunks.cpp     | 14 ++++++++------
 util/rtmpchunks.h       |  1 +
 4 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/Connector_RTMP/main.cpp b/Connector_RTMP/main.cpp
index 7ad9a403..e07b7d29 100644
--- a/Connector_RTMP/main.cpp
+++ b/Connector_RTMP/main.cpp
@@ -372,7 +372,7 @@ void Connector_RTMP::parseChunk(){
               if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
             }
             streamname = "/tmp/shared_socket_" + streamname;
-            //Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
+            Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
             //send a status reply
             AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
             amfreply.addContent(AMF::Object("", "onStatus"));//status reply
@@ -383,7 +383,7 @@ void Connector_RTMP::parseChunk(){
             amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset"));
             amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting..."));
             amfreply.getContentP(3)->addContent(AMF::Object("details", "PLS"));
-            amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1));
+            amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
             #if DEBUG >= 4
             amfreply.Print();
             #endif
@@ -397,7 +397,7 @@ void Connector_RTMP::parseChunk(){
             amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start"));
             amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!"));
             amfreply.getContentP(3)->addContent(AMF::Object("details", "PLS"));
-            amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1));
+            amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
             #if DEBUG >= 4
             amfreply.Print();
             #endif
@@ -528,7 +528,7 @@ void Connector_RTMP::parseChunk(){
             if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
           }
           streamname = "/tmp/shared_socket_" + streamname;
-          //Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
+          Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
           //send a status reply
           AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
           amfreply.addContent(AMF::Object("", "onStatus"));//status reply
diff --git a/RTMP_Parser/main.cpp b/RTMP_Parser/main.cpp
index 09debfb0..9ab21b35 100644
--- a/RTMP_Parser/main.cpp
+++ b/RTMP_Parser/main.cpp
@@ -55,7 +55,7 @@ int main(int argc, char ** argv){
 
   while (next.Parse(inbuffer)){
     if ((Detail & DETAIL_VERBOSE) == DETAIL_VERBOSE){
-      fprintf(stderr, "Chunk info: CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.cs_id, next.timestamp, next.len, next.msg_type_id, next.msg_stream_id);
+      fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp, next.len, next.msg_type_id, next.msg_stream_id);
     }
     switch (next.msg_type_id){
       case 0://does not exist
@@ -78,28 +78,28 @@ int main(int argc, char ** argv){
         short int ucmtype = ntohs(*(short int*)next.data.c_str());
         switch (ucmtype){
           case 0:
-            fprintf(stderr, "CTRL: User control message: stream begin %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: stream begin %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 1:
-            fprintf(stderr, "CTRL: User control message: stream EOF %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: stream EOF %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 2:
-            fprintf(stderr, "CTRL: User control message: stream dry %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: stream dry %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 3:
-            fprintf(stderr, "CTRL: User control message: setbufferlen %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: setbufferlen %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 4:
-            fprintf(stderr, "CTRL: User control message: streamisrecorded %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: streamisrecorded %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 6:
-            fprintf(stderr, "CTRL: User control message: pingrequest %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: pingrequest %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           case 7:
-            fprintf(stderr, "CTRL: User control message: pingresponse %i\n", ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: pingresponse %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
           default:
-            fprintf(stderr, "CTRL: User control message: UNKNOWN %hi - %i\n", ucmtype, ntohl(*(int*)next.data.c_str()+2));
+            fprintf(stderr, "CTRL: User control message: UNKNOWN %hu - %u\n", ucmtype, ntohl(*(unsigned int*)(next.data.c_str()+2)));
             break;
         }
       } break;
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 e503e9789c2b91aa1aff7e308a50adcdacac4c48 Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
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

---
 Connector_RTMP/main.cpp |  8 +++++---
 util/flv_tag.cpp        |  1 +
 util/flv_tag.h          |  6 +++++-
 util/rtmpchunks.cpp     | 40 ++++++++++++++++++++++++++++------------
 util/rtmpchunks.h       |  6 ++++++
 5 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/Connector_RTMP/main.cpp b/Connector_RTMP/main.cpp
index e07b7d29..754a43dc 100644
--- a/Connector_RTMP/main.cpp
+++ b/Connector_RTMP/main.cpp
@@ -121,13 +121,15 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
             }
             if (viddone && auddone && justdone){
               if (viddata.len != 0){
-                Socket.write(RTMPStream::SendMedia((unsigned char)viddata.data[0], (unsigned char *)viddata.data+11, viddata.len-15, 0));
+                viddata.tagTime(RTMPStream::getNowMS());
+                Socket.write(RTMPStream::SendMedia(viddata));
                 #if DEBUG >= 8
                 fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), viddata.tagTime(), viddata.tagType().c_str());
                 #endif
               }
               if (auddata.len != 0){
-                Socket.write(RTMPStream::SendMedia((unsigned char)auddata.data[0], (unsigned char *)auddata.data+11, auddata.len-15, 0));
+                auddata.tagTime(RTMPStream::getNowMS());
+                Socket.write(RTMPStream::SendMedia(auddata));
                 #if DEBUG >= 8
                 fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), auddata.tagTime(), auddata.tagType().c_str());
                 #endif
@@ -137,7 +139,7 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
             //not gotten init yet? cancel this tag
             if (viddata.len == 0 || auddata.len == 0){break;}
             //send tag normally
-            Socket.write(RTMPStream::SendMedia((unsigned char)tag.data[0], (unsigned char *)tag.data+11, tag.len-15, tag.tagTime()));
+            Socket.write(RTMPStream::SendMedia(tag));
             #if DEBUG >= 8
             fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), tag.tagTime(), tag.tagType().c_str());
             #endif
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 <stdio.h> //for Tag::FileLoader
 #include <unistd.h> //for Tag::FileLoader
 #include <fcntl.h> //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 <string>
-#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 <string>
 #include <arpa/inet.h>
 
+//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 a40270c871ea3f310cc49773d8c114634e4b32d0 Mon Sep 17 00:00:00 2001
From: Thulinma <jaron@vietors.com>
Date: Sun, 21 Aug 2011 21:03:21 +0200
Subject: [PATCH 5/5] More timestamp attempts...

---
 Connector_RTMP/main.cpp | 2 --
 util/rtmpchunks.cpp     | 3 ++-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/Connector_RTMP/main.cpp b/Connector_RTMP/main.cpp
index 754a43dc..52d4f0e3 100644
--- a/Connector_RTMP/main.cpp
+++ b/Connector_RTMP/main.cpp
@@ -121,14 +121,12 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
             }
             if (viddone && auddone && justdone){
               if (viddata.len != 0){
-                viddata.tagTime(RTMPStream::getNowMS());
                 Socket.write(RTMPStream::SendMedia(viddata));
                 #if DEBUG >= 8
                 fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), viddata.tagTime(), viddata.tagType().c_str());
                 #endif
               }
               if (auddata.len != 0){
-                auddata.tagTime(RTMPStream::getNowMS());
                 Socket.write(RTMPStream::SendMedia(auddata));
                 #if DEBUG >= 8
                 fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), auddata.tagTime(), auddata.tagType().c_str());
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);