From b54ee2dcd242087f0219ffcf41a329cc19991ffb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 17:04:43 +0200 Subject: [PATCH] Converted to new libmist JSON support, added dtscfix for metadata rewriting. --- src/analysers/dtsc_analyser.cpp | 6 +- src/conn_http_dynamic.cpp | 28 +++++--- src/conn_rtmp.cpp | 32 ++++++--- src/converters/Makefile.am | 3 +- src/converters/dtscfix.cpp | 117 ++++++++++++++++++++++++++++++++ src/converters/flv2dtsc.cpp | 36 ++++++---- 6 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 src/converters/dtscfix.cpp diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp index d2d94cda..38ce0043 100644 --- a/src/analysers/dtsc_analyser.cpp +++ b/src/analysers/dtsc_analyser.cpp @@ -10,7 +10,8 @@ #include #include #include -#include //DTSC support +#include +#include #include /// Reads DTSC from stdin and outputs human-readable information to stderr. @@ -50,6 +51,9 @@ int main(int argc, char ** argv) { if (!doneheader){ doneheader = true; Strm.metadata.Print(); + JSON::Value mdata = JSON::fromDTMI(Strm.metadata.Pack(false)); + std::cout << mdata.toString() << std::endl; + std::cout << mdata.toPrettyString() << std::endl; } Strm.getPacket().Print(); //get current timestamp diff --git a/src/conn_http_dynamic.cpp b/src/conn_http_dynamic.cpp index beeb7e1d..9db9c556 100644 --- a/src/conn_http_dynamic.cpp +++ b/src/conn_http_dynamic.cpp @@ -25,14 +25,14 @@ namespace Connector_HTTP{ /// Returns a F4M-format manifest file - std::string BuildManifest(std::string MovieId) { + std::string BuildManifest(std::string MovieId, JSON::Value & metadata) { std::string Result="\n" "\n" "" + MovieId + "\n" "video/mp4\n" "live\n" "streaming\n" - "" + Base64::encode(MP4::GenerateLiveBootstrap(1)) + "\n" + "" + Base64::encode(MP4::GenerateLiveBootstrap(metadata)) + "\n" "\n" "\n" "\n"; @@ -49,6 +49,7 @@ namespace Connector_HTTP{ HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. bool ready4data = false;//Set to true when streaming is to begin. + bool pending_manifest = false; bool inited = false; Socket::Connection ss(-1); std::string streamname; @@ -84,15 +85,7 @@ namespace Connector_HTTP{ Flash_RequestPending++; }else{ streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1); - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - std::string manifest = BuildManifest(Movie); - HTTP_S.SetBody(manifest); - conn.Send(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 - printf("Sent manifest\n"); - #endif + pending_manifest = true; } ready4data = true; HTTP_R.Clean(); //clean for any possible next requests @@ -141,6 +134,19 @@ namespace Connector_HTTP{ if (ss.spool() || ss.Received() != ""){ if (Strm.parsePacket(ss.Received())){ tag.DTSCLoader(Strm); + if (pending_manifest){ + JSON::Value meta = JSON::fromDTMI(Strm.metadata.Pack(false)); + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type","text/xml"); + HTTP_S.SetHeader("Cache-Control","no-cache"); + std::string manifest = BuildManifest(Movie, meta); + HTTP_S.SetBody(manifest); + conn.Send(HTTP_S.BuildResponse("200", "OK")); + #if DEBUG >= 3 + printf("Sent manifest\n"); + #endif + pending_manifest = false; + } if (Strm.getPacket(0).getContentP("keyframe")){ if (FlashBuf != ""){ Flash_FragBuffer.push(FlashBuf); diff --git a/src/conn_rtmp.cpp b/src/conn_rtmp.cpp index f03b69cb..a580b303 100644 --- a/src/conn_rtmp.cpp +++ b/src/conn_rtmp.cpp @@ -140,7 +140,7 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ /// Tries to get and parse one RTMP chunk at a time. void Connector_RTMP::parseChunk(std::string & inbuffer){ //for DTSC conversion - static DTSC::DTMI meta_out; + static JSON::Value meta_out; static std::stringstream prebuffer; // Temporary buffer before sending real data static bool sending = false; static unsigned int counter = 0; @@ -222,23 +222,37 @@ void Connector_RTMP::parseChunk(std::string & inbuffer){ case 18://meta data if (SS.connected()){ F.ChunkLoader(next); - DTSC::DTMI pack_out = F.toDTSC(meta_out); - if (!pack_out.isEmpty()){ + JSON::Value pack_out = F.toJSON(meta_out); + if (!pack_out.isNull()){ if (!sending){ counter++; if (counter > 8){ sending = true; - meta_out.Pack(true);//pack metadata - meta_out.packed.replace(0, 4, DTSC::Magic_Header);//prepare proper header - SS.write(meta_out.packed);//write header/metadata + std::string packed = meta_out.toPacked(); + unsigned int size = htonl(packed.size()); + SS.write(std::string(DTSC::Magic_Header, 4)); + SS.write(std::string((char*)&size, 4)); + SS.write(packed); SS.write(prebuffer.str());//write buffer prebuffer.str("");//clear buffer - SS.write(pack_out.Pack(true));//simply write + packed = pack_out.toPacked(); + size = htonl(packed.size()); + SS.write(std::string(DTSC::Magic_Packet, 4)); + SS.write(std::string((char*)&size, 4)); + SS.write(packed); }else{ - prebuffer << pack_out.Pack(true);//buffer + std::string packed = pack_out.toPacked(); + unsigned int size = htonl(packed.size()); + prebuffer << std::string(DTSC::Magic_Packet, 4); + prebuffer << std::string((char*)&size, 4); + prebuffer << packed; } }else{ - SS.write(pack_out.Pack(true));//simple write + std::string packed = pack_out.toPacked(); + unsigned int size = htonl(packed.size()); + SS.write(std::string(DTSC::Magic_Packet, 4)); + SS.write(std::string((char*)&size, 4)); + SS.write(packed); } } }else{ diff --git a/src/converters/Makefile.am b/src/converters/Makefile.am index 4bd17aee..37553573 100644 --- a/src/converters/Makefile.am +++ b/src/converters/Makefile.am @@ -1,5 +1,6 @@ AM_CPPFLAGS = $(MIST_CFLAGS) LDADD = $(MIST_LIBS) -bin_PROGRAMS=MistDTSC2FLV MistFLV2DTSC +bin_PROGRAMS=MistDTSC2FLV MistFLV2DTSC MistDTSCFix MistDTSC2FLV_SOURCES=dtsc2flv.cpp MistFLV2DTSC_SOURCES=flv2dtsc.cpp +MistDTSCFix_SOURCES=dtscfix.cpp diff --git a/src/converters/dtscfix.cpp b/src/converters/dtscfix.cpp new file mode 100644 index 00000000..589eb506 --- /dev/null +++ b/src/converters/dtscfix.cpp @@ -0,0 +1,117 @@ +/// \file dtscfix.cpp +/// Contains the code that will attempt to fix the metadata contained in an DTSC file. + +#include +#include +#include +#include + +/// Holds all code that converts filetypes to/from to DTSC. +namespace Converters{ + + /// Reads an DTSC file and attempts to fix the metadata in it. + int DTSCFix(Util::Config & conf) { + DTSC::File F(conf.getString("filename")); + std::string loader = F.getHeader(); + JSON::Value meta = JSON::fromDTMI(loader); + JSON::Value pack; + + long long unsigned int firstpack = 0; + long long unsigned int nowpack = 0; + long long unsigned int lastaudio = 0; + long long unsigned int lastvideo = 0; + long long unsigned int lastkey = 0; + long long unsigned int totalvideo = 0; + long long unsigned int totalaudio = 0; + long long unsigned int keyframes = 0; + long long unsigned int key_min = 0xffffffff; + long long unsigned int key_max = 0; + long long unsigned int vid_min = 0xffffffff; + long long unsigned int vid_max = 0; + long long unsigned int aud_min = 0xffffffff; + long long unsigned int aud_max = 0; + long long unsigned int bfrm_min = 0xffffffff; + long long unsigned int bfrm_max = 0; + long long unsigned int bps = 0; + + while (loader.size() != 0){ + loader = F.getPacket(); + if (loader.size() != 0){ + pack = JSON::fromDTMI(loader); + nowpack = pack["time"].asInt(); + if (firstpack == 0){firstpack = nowpack;} + if (pack["datatype"].asString() == "audio"){ + if (lastaudio != 0 && (nowpack - lastaudio) != 0){ + bps = pack["data"].asString().size() / (nowpack - lastaudio); + if (bps < aud_min){aud_min = bps;} + if (bps > aud_max){aud_max = bps;} + } + totalaudio += pack["data"].asString().size(); + lastaudio = nowpack; + } + if (pack["datatype"].asString() == "video"){ + if (lastvideo != 0 && (nowpack - lastvideo) != 0){ + bps = pack["data"].asString().size() / (nowpack - lastvideo); + if (bps < vid_min){vid_min = bps;} + if (bps > vid_max){vid_max = bps;} + } + if (pack["keyframe"].asInt() != 0){ + if (lastkey != 0){ + bps = nowpack - lastkey; + if (bps < key_min){key_min = bps;} + if (bps > key_max){key_max = bps;} + } + keyframes++; + lastkey = nowpack; + } + if (pack["offset"].asInt() != 0){ + bps = pack["offset"].asInt(); + if (bps < bfrm_min){bfrm_min = bps;} + if (bps > bfrm_max){bfrm_max = bps;} + } + totalvideo += pack["data"].asString().size(); + lastvideo = nowpack; + } + } + } + + std::cout << std::endl << "Summary:" << std::endl; + meta["length"] = (long long int)((nowpack - firstpack)/1000); + if (meta.isMember("audio")){ + meta["audio"]["bps"] = (long long int)(totalaudio / ((lastaudio - firstpack) / 1000)); + std::cout << " Audio: " << meta["audio"]["codec"].asString() << std::endl; + std::cout << " Bitrate: " << meta["audio"]["bps"].asInt() << std::endl; + } + if (meta.isMember("video")){ + meta["video"]["bps"] = (long long int)(totalvideo / ((lastvideo - firstpack) / 1000)); + meta["video"]["keyms"] = (long long int)((lastvideo - firstpack) / keyframes); + if (meta["video"]["keyms"].asInt() - key_min > key_max - meta["video"]["keyms"].asInt()){ + meta["video"]["keyvar"] = (long long int)(meta["video"]["keyms"].asInt() - key_min); + }else{ + meta["video"]["keyvar"] = (long long int)(key_max - meta["video"]["keyms"].asInt()); + } + std::cout << " Video: " << meta["video"]["codec"].asString() << std::endl; + std::cout << " Bitrate: " << meta["video"]["bps"].asInt() << std::endl; + std::cout << " Keyframes: " << meta["video"]["keyms"].asInt() << "~" << meta["video"]["keyvar"].asInt() << std::endl; + std::cout << " B-frames: " << bfrm_min << " - " << bfrm_max << std::endl; + } + + std::cerr << "Re-writing header..." << std::endl; + + loader = meta.toPacked(); + if (F.writeHeader(loader)){ + return 0; + }else{ + return -1; + } + }//DTSCFix + +}; + +/// Entry point for FLV2DTSC, simply calls Converters::FLV2DTSC(). +int main(int argc, char ** argv){ + Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); + conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Filename of the file to attempt to fix.\"}")); + conf.parseArgs(argc, argv); + return Converters::DTSCFix(conf); +}//main diff --git a/src/converters/flv2dtsc.cpp b/src/converters/flv2dtsc.cpp index b5fc1931..d8d7f04e 100644 --- a/src/converters/flv2dtsc.cpp +++ b/src/converters/flv2dtsc.cpp @@ -10,9 +10,10 @@ #include #include #include -#include //FLV support -#include //DTSC support -#include //AMF support +#include +#include +#include +#include #include /// Holds all code that converts filetypes to/from to DTSC. @@ -21,41 +22,46 @@ namespace Converters{ /// Reads FLV from STDIN, outputs DTSC to STDOUT. int FLV2DTSC() { FLV::Tag FLV_in; // Temporary storage for incoming FLV data. - DTSC::DTMI meta_out; // Storage for outgoing DTMI header data. - DTSC::DTMI pack_out; // Storage for outgoing DTMI data. + JSON::Value meta_out; // Storage for outgoing header data. + JSON::Value pack_out; // Storage for outgoing data. std::stringstream prebuffer; // Temporary buffer before sending real data bool sending = false; unsigned int counter = 0; while (!feof(stdin)){ if (FLV_in.FileLoader(stdin)){ - pack_out = FLV_in.toDTSC(meta_out); - if (pack_out.isEmpty()){continue;} + pack_out = FLV_in.toJSON(meta_out); + if (pack_out.isNull()){continue;} if (!sending){ counter++; if (counter > 8){ sending = true; - meta_out.Pack(true); - meta_out.packed.replace(0, 4, DTSC::Magic_Header); - std::cout << meta_out.packed; + std::string packed_header = meta_out.toPacked(); + unsigned int size = htonl(packed_header.size()); + std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; std::cout << prebuffer.rdbuf(); prebuffer.str(""); std::cerr << "Buffer done, starting real-time output..." << std::endl; }else{ - prebuffer << pack_out.Pack(true);//buffer + std::string packed_out = pack_out.toPacked(); + unsigned int size = htonl(packed_out.size()); + prebuffer << std::string(DTSC::Magic_Packet, 4) << std::string((char*)&size, 4) << packed_out; continue;//don't also write } } - std::cout << pack_out.Pack(true);//simply write + //simply write + std::string packed_out = pack_out.toPacked(); + unsigned int size = htonl(packed_out.size()); + std::cout << std::string(DTSC::Magic_Packet, 4) << std::string((char*)&size, 4) << packed_out; } } // if the FLV input is very short, do output it correctly... if (!sending){ std::cerr << "EOF - outputting buffer..." << std::endl; - meta_out.Pack(true); - meta_out.packed.replace(0, 4, DTSC::Magic_Header); - std::cout << meta_out.packed; + std::string packed_header = meta_out.toPacked(); + unsigned int size = htonl(packed_header.size()); + std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; std::cout << prebuffer.rdbuf(); } std::cerr << "Done!" << std::endl;