Several bugfixes (HTTP dynamic seeking!) and updates to the MP4 lib.
This commit is contained in:
parent
a91e53e0bc
commit
521c19f932
8 changed files with 177 additions and 100 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
29
src/analysers/mp4_analyser.cpp
Normal file
29
src/analysers/mp4_analyser.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Add table
Reference in a new issue