From a53fd5fa632ee546d0b146f441a67ed45776abae Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 9 Oct 2018 17:33:17 +0200 Subject: [PATCH 1/3] EBML fix for non-static GOP sizes --- src/input/input_ebml.cpp | 80 +++++++++++++++++++++++++--------------- src/input/input_ebml.h | 24 +++++++++--- 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/input/input_ebml.cpp b/src/input/input_ebml.cpp index 8401c0c8..5b44caf4 100644 --- a/src/input/input_ebml.cpp +++ b/src/input/input_ebml.cpp @@ -40,6 +40,7 @@ namespace Mist{ lastClusterBPos = 0; lastClusterTime = 0; bufferedPacks = 0; + wantBlocks = true; } std::string ASStoSRT(const char * ptr, uint32_t len){ @@ -349,6 +350,15 @@ namespace Mist{ bool isVideo = (Trk.type == "video"); bool isAudio = (Trk.type == "audio"); bool isASS = (Trk.codec == "subtitle" && Trk.init.size()); + //If this is a new video keyframe, flush the corresponding trackPredictor + if (isVideo && B.isKeyframe()){ + while (TP.hasPackets(true)){ + packetData &C = TP.getPacketData(true); + myMeta.update(C.time, C.offset, C.track, C.dsize, C.bpos, C.key); + TP.remove(); + } + TP.flush(); + } for (uint64_t frameNo = 0; frameNo < B.getFrameCount(); ++frameNo){ if (frameNo){ if (Trk.codec == "AAC"){ @@ -463,28 +473,32 @@ namespace Mist{ } } EBML::Block B; - do{ - if (!readElement()){ - // Make sure we empty our buffer first - if (bufferedPacks && packBuf.size()){ - for (std::map::iterator it = packBuf.begin(); - it != packBuf.end(); ++it){ - trackPredictor &TP = it->second; - if (TP.hasPackets(true)){ - packetData &C = TP.getPacketData(myMeta.tracks[it->first].type == "video"); - fillPacket(C); - TP.remove(); - --bufferedPacks; - return; + if (wantBlocks){ + do{ + if (!readElement()){ + // Make sure we empty our buffer first + if (bufferedPacks && packBuf.size()){ + for (std::map::iterator it = packBuf.begin(); + it != packBuf.end(); ++it){ + trackPredictor &TP = it->second; + if (TP.hasPackets(true)){ + packetData &C = TP.getPacketData(myMeta.tracks[it->first].type == "video"); + fillPacket(C); + TP.remove(); + --bufferedPacks; + return; + } } } + // No more buffer? Set to empty + thisPacket.null(); + return; } - // No more buffer? Set to empty - thisPacket.null(); - return; - } + B = EBML::Block(ptr); + }while (!B || B.getType() != EBML::ELEM_BLOCK || !selectedTracks.count(B.getTrackNum())); + }else{ B = EBML::Block(ptr); - }while (!B || B.getType() != EBML::ELEM_BLOCK || !selectedTracks.count(B.getTrackNum())); + } uint64_t tNum = B.getTrackNum(); uint64_t newTime = lastClusterTime + B.getTimecode(); @@ -493,6 +507,24 @@ namespace Mist{ bool isVideo = (Trk.type == "video"); bool isAudio = (Trk.type == "audio"); bool isASS = (Trk.codec == "subtitle" && Trk.init.size()); + + //If this is a new video keyframe, flush the corresponding trackPredictor + if (isVideo && B.isKeyframe() && bufferedPacks){ + if (TP.hasPackets(true)){ + wantBlocks = false; + packetData &C = TP.getPacketData(true); + fillPacket(C); + TP.remove(); + --bufferedPacks; + return; + } + } + if (isVideo && B.isKeyframe()){ + TP.flush(); + } + wantBlocks = true; + + for (uint64_t frameNo = 0; frameNo < B.getFrameCount(); ++frameNo){ if (frameNo){ if (Trk.codec == "AAC"){ @@ -531,6 +563,7 @@ namespace Mist{ } void InputEBML::seek(int seekTime){ + wantBlocks = true; packBuf.clear(); bufferedPacks = 0; uint64_t mainTrack = getMainSelectedTrack(); @@ -541,17 +574,6 @@ namespace Mist{ uint64_t partCount = 0; for (unsigned int i = 0; i < Trk.keys.size(); i++){ if (Trk.keys[i].getTime() > seekTime){ - if (i > 1){ - partCount -= Trk.keys[i-1].getParts() + Trk.keys[i-2].getParts(); - uint64_t partEnd = partCount + Trk.keys[i-2].getParts(); - uint64_t partTime = Trk.keys[i-2].getTime(); - for (uint64_t prt = partCount; prt < partEnd; ++prt){ - INSANE_MSG("Replay part %llu, timestamp: %llu+%llu", prt, partTime, Trk.parts[prt].getOffset()); - packBuf[mainTrack].add(partTime, Trk.parts[prt].getOffset(), mainTrack, 0, 0, false, isVideo, (void *)0); - packBuf[mainTrack].remove(); - partTime += Trk.parts[prt].getDuration(); - } - } break; } partCount += Trk.keys[i].getParts(); diff --git a/src/input/input_ebml.h b/src/input/input_ebml.h index 057bcd8c..4d4af639 100644 --- a/src/input/input_ebml.h +++ b/src/input/input_ebml.h @@ -40,15 +40,13 @@ namespace Mist{ class trackPredictor{ public: packetData pkts[PKT_COUNT]; + uint64_t frameOffset; uint16_t smallestFrame; uint64_t lastTime; uint64_t ctr; uint64_t rem; trackPredictor(){ - smallestFrame = 0; - lastTime = 0; - ctr = 0; - rem = 0; + flush(); } bool hasPackets(bool finished = false){ if (finished){ @@ -57,6 +55,14 @@ namespace Mist{ return (ctr - rem > 12); } } + /// Clears all internal values, for reuse as-new. + void flush(){ + frameOffset = 0; + smallestFrame = 0; + lastTime = 0; + ctr = 0; + rem = 0; + } packetData & getPacketData(bool mustCalcOffsets){ frameOffsetKnown = true; //grab the next packet to output @@ -74,13 +80,20 @@ namespace Mist{ //we assume it's just time stamp drift due to lack of precision. p.time = (lastTime + smallestFrame); }else{ - p.offset = maxEBMLFrameOffset; + p.time -= frameOffset?frameOffset + smallestFrame:0; + p.offset = maxEBMLFrameOffset + frameOffset + smallestFrame; } } lastTime = p.time; + DONTEVEN_MSG("Outputting%s %llu + %llu, offset %llu (%llu -> %llu), display at %llu", (p.key?" (KEY)":""), p.time, frameOffset, p.offset, rem, rem % PKT_COUNT, p.time+p.offset); return p; } void add(uint64_t packTime, uint64_t packOffset, uint64_t packTrack, uint64_t packDataSize, uint64_t packBytePos, bool isKeyframe, bool isVideo, void * dataPtr = 0){ + //If no packets have been removed yet and there is more than one packet, calculate frameOffset + if (!rem && ctr && packTime < pkts[0].time){ + frameOffset = pkts[0].time - packTime; + INSANE_MSG("Setting frameOffset to %llu", frameOffset); + } if (isVideo && ctr && ctr >= rem){ int32_t currOffset = packTime - pkts[(ctr-1)%PKT_COUNT].time; if (currOffset < 0){currOffset *= -1;} @@ -131,6 +144,7 @@ namespace Mist{ bool openStreamSource(){return true;} bool needHeader(){return needsLock() && !readExistingHeader();} double timeScale; + bool wantBlocks; }; } From ab39cca5f476a9dc40476c7a4549eb6c20123014 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 10 Oct 2018 11:37:07 +0200 Subject: [PATCH 2/3] Fixed EBML input cutting off zero bytes in places where it's not appropriate --- lib/ebml.cpp | 6 ++++++ lib/ebml.h | 1 + src/input/input_ebml.cpp | 16 ++++++++-------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/ebml.cpp b/lib/ebml.cpp index 0670b6f1..45a7abad 100644 --- a/lib/ebml.cpp +++ b/lib/ebml.cpp @@ -516,6 +516,12 @@ namespace EBML{ return std::string(strPtr, strLen); } + std::string Element::getValStringUntrimmed() const{ + uint64_t strLen = getPayloadLen(); + const char * strPtr = getPayload(); + return std::string(strPtr, strLen); + } + uint64_t Block::getTrackNum() const{return UniInt::readInt(getPayload());} int16_t Block::getTimecode() const{ diff --git a/lib/ebml.h b/lib/ebml.h index 972fe071..ff1668b1 100644 --- a/lib/ebml.h +++ b/lib/ebml.h @@ -97,6 +97,7 @@ namespace EBML{ int64_t getValInt() const; double getValFloat() const; std::string getValString() const; + std::string getValStringUntrimmed() const; const Element findChild(uint32_t id) const; private: diff --git a/src/input/input_ebml.cpp b/src/input/input_ebml.cpp index 5b44caf4..2e430074 100644 --- a/src/input/input_ebml.cpp +++ b/src/input/input_ebml.cpp @@ -191,13 +191,13 @@ namespace Mist{ trueCodec = "H264"; trueType = "video"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "V_MPEGH/ISO/HEVC"){ trueCodec = "HEVC"; trueType = "video"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "V_AV1"){ trueCodec = "AV1"; @@ -215,25 +215,25 @@ namespace Mist{ trueCodec = "opus"; trueType = "audio"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "A_VORBIS"){ trueCodec = "vorbis"; trueType = "audio"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "V_THEORA"){ trueCodec = "theora"; trueType = "video"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "A_AAC"){ trueCodec = "AAC"; trueType = "audio"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "A_PCM/INT/BIG"){ trueCodec = "PCM"; @@ -275,12 +275,12 @@ namespace Mist{ trueCodec = "subtitle"; trueType = "meta"; tmpElem = E.findChild(EBML::EID_CODECPRIVATE); - if (tmpElem){init = tmpElem.getValString();} + if (tmpElem){init = tmpElem.getValStringUntrimmed();} } if (codec == "A_MS/ACM"){ tmpElem = E.findChild(EBML::EID_CODECPRIVATE); if (tmpElem){ - std::string WAVEFORMATEX = tmpElem.getValString(); + std::string WAVEFORMATEX = tmpElem.getValStringUntrimmed(); unsigned int formatTag = Bit::btohs_le(WAVEFORMATEX.data()); switch (formatTag){ case 3: From 8afad6d5743d274e7f3cc741c89d13d41bcdfee1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 10 Oct 2018 15:05:36 +0200 Subject: [PATCH 3/3] Added DTS audio support to EBML input/output --- src/input/input_ebml.cpp | 17 +++++++++++++++++ src/output/output_ebml.cpp | 3 +++ 2 files changed, 20 insertions(+) diff --git a/src/input/input_ebml.cpp b/src/input/input_ebml.cpp index 2e430074..ae5fe267 100644 --- a/src/input/input_ebml.cpp +++ b/src/input/input_ebml.cpp @@ -35,6 +35,7 @@ namespace Mist{ capa["codecs"].append("MP3"); capa["codecs"].append("AC3"); capa["codecs"].append("FLOAT"); + capa["codecs"].append("DTS"); capa["codecs"].append("JSON"); capa["codecs"].append("subtitle"); lastClusterBPos = 0; @@ -235,6 +236,10 @@ namespace Mist{ tmpElem = E.findChild(EBML::EID_CODECPRIVATE); if (tmpElem){init = tmpElem.getValStringUntrimmed();} } + if (codec == "A_DTS"){ + trueCodec = "DTS"; + trueType = "audio"; + } if (codec == "A_PCM/INT/BIG"){ trueCodec = "PCM"; trueType = "audio"; @@ -365,6 +370,12 @@ namespace Mist{ newTime += (1000000 / Trk.rate)/timeScale;//assume ~1000 samples per frame } else if (Trk.codec == "MP3"){ newTime += (1152000 / Trk.rate)/timeScale;//1152 samples per frame + } else if (Trk.codec == "DTS"){ + //Assume 512 samples per frame (DVD default) + //actual amount can be calculated from data, but data + //is not available during header generation... + //See: http://www.stnsoft.com/DVD/dtshdr.html + newTime += (512000 / Trk.rate)/timeScale; }else{ newTime += 1/timeScale; ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!", Trk.codec.c_str()); @@ -531,6 +542,12 @@ namespace Mist{ newTime += (1000000 / Trk.rate)/timeScale;//assume ~1000 samples per frame } else if (Trk.codec == "MP3"){ newTime += (1152000 / Trk.rate)/timeScale;//1152 samples per frame + } else if (Trk.codec == "DTS"){ + //Assume 512 samples per frame (DVD default) + //actual amount can be calculated from data, but data + //is not available during header generation... + //See: http://www.stnsoft.com/DVD/dtshdr.html + newTime += (512000 / Trk.rate)/timeScale; }else{ ERROR_MSG("Unknown frame duration for codec %s - timestamps WILL be wrong!", Trk.codec.c_str()); } diff --git a/src/output/output_ebml.cpp b/src/output/output_ebml.cpp index e353202a..4ea52ac4 100644 --- a/src/output/output_ebml.cpp +++ b/src/output/output_ebml.cpp @@ -39,6 +39,7 @@ namespace Mist{ capa["codecs"][0u][1u].append("MP3"); capa["codecs"][0u][1u].append("FLOAT"); capa["codecs"][0u][1u].append("AC3"); + capa["codecs"][0u][1u].append("DTS"); capa["codecs"][0u][2u].append("+JSON"); capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/video/webm"; @@ -58,6 +59,7 @@ namespace Mist{ capa["exceptions"]["codec:MP3"] = blacklistNonChrome; capa["exceptions"]["codec:FLOAT"] = blacklistNonChrome; capa["exceptions"]["codec:AC3"] = blacklistNonChrome; + capa["exceptions"]["codec:DTS"] = blacklistNonChrome; } /// Calculates the size of a Cluster (contents only) and returns it. @@ -134,6 +136,7 @@ namespace Mist{ if (Trk.codec == "ALAW"){return "A_MS/ACM";} if (Trk.codec == "ULAW"){return "A_MS/ACM";} if (Trk.codec == "FLOAT"){return "A_PCM/FLOAT/IEEE";} + if (Trk.codec == "DTS"){return "A_DTS";} if (Trk.codec == "JSON"){return "M_JSON";} return "E_UNKNOWN"; }