Several bugfixes (HTTP dynamic seeking!) and updates to the MP4 lib.

This commit is contained in:
Thulinma 2012-09-28 18:56:15 +02:00
parent a91e53e0bc
commit 521c19f932
8 changed files with 177 additions and 100 deletions

View file

@ -1,7 +1,8 @@
AM_CPPFLAGS = $(global_CFLAGS) $(MIST_CFLAGS) AM_CPPFLAGS = $(global_CFLAGS) $(MIST_CFLAGS)
LDADD = $(MIST_LIBS) LDADD = $(MIST_LIBS)
bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF MistAnalyserMP4
MistAnalyserRTMP_SOURCES=rtmp_analyser.cpp MistAnalyserRTMP_SOURCES=rtmp_analyser.cpp
MistAnalyserFLV_SOURCES=flv_analyser.cpp MistAnalyserFLV_SOURCES=flv_analyser.cpp
MistAnalyserDTSC_SOURCES=dtsc_analyser.cpp MistAnalyserDTSC_SOURCES=dtsc_analyser.cpp
MistAnalyserAMF_SOURCES=amf_analyser.cpp MistAnalyserAMF_SOURCES=amf_analyser.cpp
MistAnalyserMP4_SOURCES=mp4_analyser.cpp

View file

@ -13,8 +13,7 @@ int main(int argc, char ** argv){
conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Filename of the DTSC file to analyse.\"}")); conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Filename of the DTSC file to analyse.\"}"));
conf.parseArgs(argc, argv); conf.parseArgs(argc, argv);
DTSC::File F(conf.getString("filename")); DTSC::File F(conf.getString("filename"));
std::string loader = F.getHeader(); JSON::Value meta = F.getMeta();
JSON::Value meta = JSON::fromDTMI(loader);
std::cout << meta.toPrettyString() << std::endl; std::cout << meta.toPrettyString() << std::endl;
JSON::Value pack; JSON::Value pack;

View file

@ -0,0 +1,29 @@
/// \file mp4_analyser.cpp
/// Debugging tool for MP4 data.
/// Expects MP4 data through stdin, outputs human-readable information to stderr.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <string.h>
#include <mist/mp4.h>
#include <mist/config.h>
/// Debugging tool for MP4 data.
/// Expects MP4 data through stdin, outputs human-readable information to stderr.
int main(int argc, char ** argv) {
Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION);
conf.parseArgs(argc, argv);
std::string temp;
while (std::cin.good()){temp += std::cin.get();}//read all of std::cin to temp
temp.erase(temp.size()-1, 1);//strip the invalid last character
MP4::Box mp4data;
while (mp4data.read(temp)){
std::cerr << mp4data.toPrettyString(0) << std::endl;
}
return 0;
}

View file

@ -26,64 +26,94 @@
/// Holds everything unique to HTTP Dynamic Connector. /// Holds everything unique to HTTP Dynamic Connector.
namespace Connector_HTTP{ namespace Connector_HTTP{
std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum, int starttime){ std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum, int starttime, int endtime){
MP4::AFRT afrt; std::string empty;
if (starttime == 0){
afrt.SetUpdate(false);
}else{
afrt.SetUpdate(true);
}
afrt.SetTimeScale(1000);
afrt.AddQualityEntry("");
if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
//metadata["lasttime"].asInt()?
afrt.AddFragmentRunEntry(fragnum, starttime, 2000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
}else{
afrt.AddFragmentRunEntry(fragnum, starttime, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
}
afrt.WriteContent();
MP4::ASRT asrt; MP4::ASRT asrt;
if (starttime == 0){ if (starttime == 0){
asrt.SetUpdate(false); asrt.setUpdate(false);
}else{ }else{
asrt.SetUpdate(true); asrt.setUpdate(true);
}
asrt.setVersion(1);
asrt.setQualityEntry(empty, 0);
if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){
asrt.setSegmentRun(1, 20000, 0);
}else{
asrt.setSegmentRun(1, metadata["keytime"].size(), 0);
}
MP4::AFRT afrt;
if (starttime == 0){
afrt.setUpdate(false);
}else{
afrt.setUpdate(true);
}
afrt.setVersion(1);
afrt.setTimeScale(1000);
afrt.setQualityEntry(empty, 0);
MP4::afrt_runtable afrtrun;
if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){
afrtrun.firstFragment = 1;
afrtrun.firstTimestamp = 0;
if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
afrtrun.duration = 2000;
}else{
afrtrun.duration = metadata["video"]["keyms"].asInt();
}
afrt.setFragmentRun(afrtrun, 0);
}else{
for (int i = 0; i < metadata["keytime"].size(); i++){
afrtrun.firstFragment = i+1;
afrtrun.firstTimestamp = metadata["keytime"][i].asInt();
if (i+1 < metadata["keytime"].size()){
afrtrun.duration = metadata["keytime"][i+1].asInt() - metadata["keytime"][i].asInt();
}else{
if (metadata["lastms"].asInt()){
afrtrun.duration = metadata["lastms"].asInt() - metadata["keytime"][i].asInt();
}else{
afrtrun.duration = 3000;//guess 3 seconds if unknown
}
}
afrt.setFragmentRun(afrtrun, i);
}
} }
asrt.AddQualityEntry("");
/// \todo Actually use correct number of fragments.
asrt.AddSegmentRunEntry(1, 20000);//1 Segment, 20000 Fragments
asrt.WriteContent();
MP4::ABST abst; MP4::ABST abst;
abst.AddFragmentRunTable(&afrt); abst.setVersion(1);
abst.AddSegmentRunTable(&asrt); abst.setBootstrapinfoVersion(1);
abst.SetBootstrapVersion(1); abst.setProfile(0);
abst.SetProfile(0);
if (metadata.isMember("length") && metadata["length"].asInt() > 0){
abst.SetLive(false);
abst.SetMediaTime(1000*metadata["length"].asInt());
}else{
abst.SetLive(true);
abst.SetMediaTime(0xFFFFFFFF);//metadata["lasttime"].asInt()?
}
if (starttime == 0){ if (starttime == 0){
abst.SetUpdate(false); abst.setUpdate(false);
}else{ }else{
abst.SetUpdate(true); abst.setUpdate(true);
} }
abst.SetTimeScale(1000); abst.setTimeScale(1000);
abst.SetSMPTE(0); if (metadata.isMember("length") && metadata["length"].asInt() > 0){
abst.SetMovieIdentifier(MovieId); abst.setLive(false);
abst.SetDRM(""); if (metadata["lastms"].asInt()){
abst.SetMetaData(""); abst.setCurrentMediaTime(metadata["lastms"].asInt());
abst.AddServerEntry(""); }else{
abst.AddQualityEntry(""); abst.setCurrentMediaTime(1000*metadata["length"].asInt());
abst.WriteContent(); }
}else{
abst.setLive(true);
abst.setCurrentMediaTime(0xFFFFFFFF);
}
abst.setSmpteTimeCodeOffset(0);
abst.setMovieIdentifier(MovieId);
abst.setServerEntry(empty, 0);
abst.setQualityEntry(empty, 0);
abst.setDrmData(empty);
abst.setMetaData(empty);
abst.setSegmentRunTable(asrt, 0);
abst.setFragmentRunTable(afrt, 0);
//#if DEBUG >= 8 //#if DEBUG >= 8
std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl; std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl;
//#endif //#endif
return std::string((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize()); return std::string((char*)abst.asBox(), (int)abst.boxedSize());
} }
@ -91,18 +121,19 @@ namespace Connector_HTTP{
std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){ std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
std::string Result; std::string Result;
if (metadata.isMember("length") && metadata["length"].asInt() > 0){ if (metadata.isMember("length") && metadata["length"].asInt() > 0){
std::stringstream st;
st << ((double)metadata["video"]["keyms"].asInt() / 1000);
Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n" "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
"<id>" + MovieId + "</id>\n" "<id>" + MovieId + "</id>\n"
"<duration>" + metadata["length"].asString() + "</duration>\n" "<width>" + metadata["video"]["width"].asString() + "</width>\n"
"<height>" + metadata["video"]["height"].asString() + "</height>\n"
"<duration>" + metadata["length"].asString() + ".000</duration>\n"
"<mimeType>video/mp4</mimeType>\n" "<mimeType>video/mp4</mimeType>\n"
"<streamType>recorded</streamType>\n" "<streamType>recorded</streamType>\n"
"<deliveryType>streaming</deliveryType>\n" "<deliveryType>streaming</deliveryType>\n"
"<bestEffortFetchInfo segmentDuration=\""+metadata["length"].asString()+".000\" fragmentDuration=\""+st.str()+"\" />\n" "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "</bootstrapInfo>\n"
"<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0)) + "</bootstrapInfo>\n" "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\">\n"
"<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n" "<metadata>AgAKb25NZXRhRGF0YQgAAAAAAAl0cmFja2luZm8KAAAAAgMACXRpbWVzY2FsZQBA+GoAAAAAAAAGbGVuZ3RoAEGMcHoQAAAAAAhsYW5ndWFnZQIAA2VuZwARc2FtcGxlZGVzY3JpcHRpb24KAAAAAQMACnNhbXBsZXR5cGUCAARhdmMxAAAJAAAJAwAJdGltZXNjYWxlAEDncAAAAAAAAAZsZW5ndGgAQXtNvTAAAAAACGxhbmd1YWdlAgADZW5nABFzYW1wbGVkZXNjcmlwdGlvbgoAAAABAwAKc2FtcGxldHlwZQIABG1wNGEAAAkAAAkADWF1ZGlvY2hhbm5lbHMAQAAAAAAAAAAAD2F1ZGlvc2FtcGxlcmF0ZQBA53AAAAAAAAAOdmlkZW9mcmFtZXJhdGUAQDf/gi5SciUABmFhY2FvdABAAAAAAAAAAAAIYXZjbGV2ZWwAQD8AAAAAAAAACmF2Y3Byb2ZpbGUAQFNAAAAAAAAADGF1ZGlvY29kZWNpZAIABG1wNGEADHZpZGVvY29kZWNpZAIABGF2YzEABXdpZHRoAECQ4AAAAAAAAAZoZWlnaHQAQIMAAAAAAAAACmZyYW1lV2lkdGgAQJDgAAAAAAAAC2ZyYW1lSGVpZ2h0AECDAAAAAAAAAAxkaXNwbGF5V2lkdGgAQJDgAAAAAAAADWRpc3BsYXlIZWlnaHQAQIMAAAAAAAAADG1vb3Zwb3NpdGlvbgBBmxq2uAAAAAAIZHVyYXRpb24AQIKjqW3oyhIAAAk=</metadata>\n"
"</media>\n"
"</manifest>\n"; "</manifest>\n";
}else{ }else{
Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@ -111,7 +142,7 @@ namespace Connector_HTTP{
"<mimeType>video/mp4</mimeType>\n" "<mimeType>video/mp4</mimeType>\n"
"<streamType>live</streamType>\n" "<streamType>live</streamType>\n"
"<deliveryType>streaming</deliveryType>\n" "<deliveryType>streaming</deliveryType>\n"
"<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0)) + "</bootstrapInfo>\n" "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "</bootstrapInfo>\n"
"<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n" "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
"</manifest>\n"; "</manifest>\n";
} }
@ -273,26 +304,26 @@ namespace Connector_HTTP{
#if DEBUG >= 3 #if DEBUG >= 3
fprintf(stderr, "Sending a fragment..."); fprintf(stderr, "Sending a fragment...");
#endif #endif
static std::string btstrp; //static std::string btstrp;
btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime); //btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime, Strm.getPacket(0)["time"]);
HTTP_S.Clean(); HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "video/mp4"); HTTP_S.SetHeader("Content-Type", "video/mp4");
HTTP_S.SetBody(""); HTTP_S.SetBody("");
HTTP_S.SetHeader("Content-Length", FlashBufSize+32+33+btstrp.size()); HTTP_S.SetHeader("Content-Length", FlashBufSize+8);//32+33+btstrp.size());
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
conn.SendNow("\x00\x00\x00\x21" "afra\x00\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x01", 21); //conn.SendNow("\x00\x00\x00\x21" "afra\x00\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x01", 21);
unsigned long tmptime = htonl(FlashBufTime << 32); //unsigned long tmptime = htonl(FlashBufTime << 32);
conn.SendNow((char*)&tmptime, 4); //conn.SendNow((char*)&tmptime, 4);
tmptime = htonl(FlashBufTime & 0xFFFFFFFF); //tmptime = htonl(FlashBufTime & 0xFFFFFFFF);
conn.SendNow((char*)&tmptime, 4); //conn.SendNow((char*)&tmptime, 4);
tmptime = htonl(65); //tmptime = htonl(65);
conn.SendNow((char*)&tmptime, 4); //conn.SendNow((char*)&tmptime, 4);
conn.SendNow(btstrp); //conn.SendNow(btstrp);
conn.SendNow("\x00\x00\x00\x18moof\x00\x00\x00\x10mfhd\x00\x00\x00\x00", 20); //conn.SendNow("\x00\x00\x00\x18moof\x00\x00\x00\x10mfhd\x00\x00\x00\x00", 20);
unsigned long fragno = htonl(ReqFragment); //unsigned long fragno = htonl(ReqFragment);
conn.SendNow((char*)&fragno, 4); //conn.SendNow((char*)&fragno, 4);
unsigned long size = htonl(FlashBufSize+8); unsigned long size = htonl(FlashBufSize+8);
conn.SendNow((char*)&size, 4); conn.SendNow((char*)&size, 4);
conn.SendNow("mdat", 4); conn.SendNow("mdat", 4);
@ -313,11 +344,13 @@ namespace Connector_HTTP{
//fill buffer with init data, if needed. //fill buffer with init data, if needed.
if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){ if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){
tmp.DTSCAudioInit(Strm); tmp.DTSCAudioInit(Strm);
tmp.tagTime(Strm.getPacket(0)["time"].asInt());
FlashBuf.push_back(std::string(tmp.data, tmp.len)); FlashBuf.push_back(std::string(tmp.data, tmp.len));
FlashBufSize += tmp.len; FlashBufSize += tmp.len;
} }
if (Strm.metadata.isMember("video") && Strm.metadata["video"].isMember("init")){ if (Strm.metadata.isMember("video") && Strm.metadata["video"].isMember("init")){
tmp.DTSCVideoInit(Strm); tmp.DTSCVideoInit(Strm);
tmp.tagTime(Strm.getPacket(0)["time"].asInt());
FlashBuf.push_back(std::string(tmp.data, tmp.len)); FlashBuf.push_back(std::string(tmp.data, tmp.len));
FlashBufSize += tmp.len; FlashBufSize += tmp.len;
} }

View file

@ -215,7 +215,6 @@ void CheckConfig(JSON::Value & in, JSON::Value & out){
} }
} }
out = in; out = in;
out["version"] = PACKAGE_VERSION;
} }
bool streamsEqual(JSON::Value & one, JSON::Value & two){ bool streamsEqual(JSON::Value & one, JSON::Value & two){
@ -558,6 +557,7 @@ int main(int argc, char ** argv){
//sent current configuration, no matter if it was changed or not //sent current configuration, no matter if it was changed or not
//Response["streams"] = Storage["streams"]; //Response["streams"] = Storage["streams"];
Response["config"] = Connector::Storage["config"]; Response["config"] = Connector::Storage["config"];
Response["config"]["version"] = PACKAGE_VERSION "/" + Util::Config::libver;
Response["streams"] = Connector::Storage["streams"]; Response["streams"] = Connector::Storage["streams"];
//add required data to the current unix time to the config, for syncing reasons //add required data to the current unix time to the config, for syncing reasons
Response["config"]["time"] = Util::epoch(); Response["config"]["time"] = Util::epoch();

View file

@ -12,10 +12,21 @@ namespace Converters{
/// Reads an DTSC file and attempts to fix the metadata in it. /// Reads an DTSC file and attempts to fix the metadata in it.
int DTSCFix(Util::Config & conf) { int DTSCFix(Util::Config & conf) {
DTSC::File F(conf.getString("filename")); DTSC::File F(conf.getString("filename"));
std::string loader = F.getHeader(); JSON::Value oriheader = F.getMeta();
JSON::Value meta = JSON::fromDTMI(loader); JSON::Value meta = oriheader;
JSON::Value pack; JSON::Value pack;
if (!oriheader.isMember("moreheader")){
std::cerr << "This file is not DTSCFix'able. Please re-convert and try again." << std::endl;
return 1;
}
if (oriheader["moreheader"].asInt() > 0){
std::cerr << "Warning: This file has already been DTSCFix'ed. Doing this multiple times makes the file larger for no reason." << std::endl;
}
meta.removeMember("keytime");
meta.removeMember("keybpos");
meta.removeMember("moreheader");
long long unsigned int firstpack = 0; long long unsigned int firstpack = 0;
long long unsigned int nowpack = 0; long long unsigned int nowpack = 0;
long long unsigned int lastaudio = 0; long long unsigned int lastaudio = 0;
@ -54,6 +65,8 @@ namespace Converters{
if (bps > vid_max){vid_max = bps;} if (bps > vid_max){vid_max = bps;}
} }
if (F.getJSON()["keyframe"].asInt() != 0){ if (F.getJSON()["keyframe"].asInt() != 0){
meta["keytime"].append(F.getJSON()["time"]);
meta["keybpos"].append(F.getLastReadPos());
if (lastkey != 0){ if (lastkey != 0){
bps = nowpack - lastkey; bps = nowpack - lastkey;
if (bps < key_min){key_min = bps;} if (bps < key_min){key_min = bps;}
@ -73,12 +86,10 @@ namespace Converters{
F.seekNext(); F.seekNext();
} }
std::cout << std::endl << "Summary:" << std::endl;
meta["length"] = (long long int)((nowpack - firstpack)/1000); meta["length"] = (long long int)((nowpack - firstpack)/1000);
meta["lastms"] = (long long int)nowpack;
if (meta.isMember("audio")){ if (meta.isMember("audio")){
meta["audio"]["bps"] = (long long int)(totalaudio / ((lastaudio - firstpack) / 1000)); 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")){ if (meta.isMember("video")){
meta["video"]["bps"] = (long long int)(totalvideo / ((lastvideo - firstpack) / 1000)); meta["video"]["bps"] = (long long int)(totalvideo / ((lastvideo - firstpack) / 1000));
@ -88,16 +99,20 @@ namespace Converters{
}else{ }else{
meta["video"]["keyvar"] = (long long int)(key_max - meta["video"]["keyms"].asInt()); 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 << "Appending new header..." << std::endl;
std::string loader = meta.toPacked();
long long int newHPos = F.addHeader(loader);
if (!newHPos){
std::cerr << "Failure appending new header. Cancelling." << std::endl;
return 1;
}
std::cerr << "Re-writing header..." << std::endl; std::cerr << "Re-writing header..." << std::endl;
oriheader["moreheader"] = newHPos;
loader = meta.toPacked(); loader = oriheader.toPacked();
if (F.writeHeader(loader)){ if (F.writeHeader(loader)){
std::cerr << "Metadata is now: " << meta.toPrettyString(0) << std::endl;
return 0; return 0;
}else{ }else{
return -1; return -1;

View file

@ -36,6 +36,7 @@ namespace Converters{
counter++; counter++;
if (counter > 8){ if (counter > 8){
sending = true; sending = true;
meta_out["moreheader"] = 0LL;
std::string packed_header = meta_out.toPacked(); std::string packed_header = meta_out.toPacked();
unsigned int size = htonl(packed_header.size()); unsigned int size = htonl(packed_header.size());
std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header;
@ -59,12 +60,13 @@ namespace Converters{
// if the FLV input is very short, do output it correctly... // if the FLV input is very short, do output it correctly...
if (!sending){ if (!sending){
std::cerr << "EOF - outputting buffer..." << std::endl; std::cerr << "EOF - outputting buffer..." << std::endl;
meta_out["moreheader"] = 0LL;
std::string packed_header = meta_out.toPacked(); std::string packed_header = meta_out.toPacked();
unsigned int size = htonl(packed_header.size()); unsigned int size = htonl(packed_header.size());
std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header;
std::cout << prebuffer.rdbuf(); std::cout << prebuffer.rdbuf();
} }
std::cerr << "Done!" << std::endl; std::cerr << "Done! If you output this data to a file, don't forget to run MistDTSCFix next." << std::endl;
return 0; return 0;
}//FLV2DTSC }//FLV2DTSC

View file

@ -69,7 +69,7 @@ int main(int argc, char** argv){
DTSC::File source = DTSC::File(conf.getString("filename")); DTSC::File source = DTSC::File(conf.getString("filename"));
Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin)); Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
std::string meta_str = source.getHeader(); JSON::Value meta = source.getMeta();
JSON::Value pausemark; JSON::Value pausemark;
pausemark["datatype"] = "pause_marker"; pausemark["datatype"] = "pause_marker";
pausemark["time"] = (long long int)0; pausemark["time"] = (long long int)0;
@ -78,14 +78,9 @@ int main(int argc, char** argv){
int lasttime = Util::epoch();//time last packet was sent int lasttime = Util::epoch();//time last packet was sent
//send the header //send the header
{ std::string meta_str = meta.toNetPacked();
in_out.Send("DTSC"); in_out.Send(meta_str);
unsigned int size = htonl(meta_str.size());
in_out.Send((char*)&size, 4);
in_out.Send(meta_str);
}
JSON::Value meta = JSON::fromDTMI(meta_str);
if (meta["video"]["keyms"].asInt() < 11){ if (meta["video"]["keyms"].asInt() < 11){
meta["video"]["keyms"] = (long long int)1000; meta["video"]["keyms"] = (long long int)1000;
} }
@ -132,6 +127,10 @@ int main(int argc, char** argv){
json_sts["vod"]["start"] = Util::epoch() - sts.conntime; json_sts["vod"]["start"] = Util::epoch() - sts.conntime;
if (!meta_sent){ if (!meta_sent){
json_sts["vod"]["meta"] = meta; json_sts["vod"]["meta"] = meta;
json_sts["vod"]["meta"]["audio"].removeMember("init");
json_sts["vod"]["meta"]["video"].removeMember("init");
json_sts["vod"]["meta"].removeMember("keytime");
json_sts["vod"]["meta"].removeMember("keybpos");
meta_sent = true; meta_sent = true;
} }
StatsSocket.Send(json_sts.toString().c_str()); StatsSocket.Send(json_sts.toString().c_str());
@ -181,17 +180,16 @@ int main(int argc, char** argv){
} }
lastTime = now; lastTime = now;
if (playing > 0){--playing;} if (playing > 0){--playing;}
if (playing == 0){
#if DEBUG >= 4
std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl;
#endif
pausemark["time"] = (long long int)now;
pausemark.toPacked();
in_out.SendNow(pausemark.toNetPacked());
in_out.setBlocking(true);
}
} }
if (playing != 0){ if (playing == 0){
#if DEBUG >= 4
std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl;
#endif
pausemark["time"] = source.getJSON()["time"];
pausemark.toPacked();
in_out.SendNow(pausemark.toNetPacked());
in_out.setBlocking(true);
}else{
lasttime = Util::epoch(); lasttime = Util::epoch();
//insert proper header for this type of data //insert proper header for this type of data
in_out.Send("DTPD"); in_out.Send("DTPD");