From b240874ede22c6d4dd9cd9243a48e50b36ed618d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 00:35:01 +0200 Subject: [PATCH] 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 #include #include #include +#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 //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); ///