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
This commit is contained in:
parent
3b7af0b95a
commit
b240874ede
4 changed files with 100 additions and 5 deletions
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue