Fixes to CMAF output

This commit is contained in:
Thulinma 2020-02-17 17:02:05 +01:00
parent e217f41f17
commit a8e04e1787
7 changed files with 69 additions and 84 deletions

View file

@ -1,24 +1,13 @@
#include "cmaf.h" #include "cmaf.h"
static uint64_t unixBootDiff = (Util::unixMS() - Util::bootMS());
namespace CMAF{ namespace CMAF{
/// Function to determine the payload size of a CMAF fragment. /// Function to determine the payload size of a CMAF fragment.
/// \parm isKeyIndex indicates whether we are sending DTSC Fragment or DTSC Key based CMAF fragments. size_t payloadSize(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime){
size_t payloadSize(const DTSC::Meta &M, size_t track, size_t index, bool isKeyIndex){
DTSC::Fragments fragments(M.fragments(track));
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track)); DTSC::Parts parts(M.parts(track));
size_t firstPart = M.getPartIndex(startTime, track);
size_t firstKey = (isKeyIndex ? index : fragments.getFirstKey(index)); size_t endPart = M.getPartIndex(endTime, track);
size_t endKey = keys.getEndValid();
if (isKeyIndex) {
if (index + 1 < keys.getEndValid()){endKey = index + 1;}
} else {
if (index + 1 < fragments.getEndValid()){endKey = fragments.getFirstKey(index + 1);}
}
size_t firstPart = keys.getFirstPart(firstKey);
size_t endPart = parts.getEndValid();
if (endKey != keys.getEndValid()){endPart = keys.getFirstPart(endKey);}
size_t payloadSize = 0; size_t payloadSize = 0;
for (size_t i = firstPart; i < endPart; i++){payloadSize += parts.getSize(i);} for (size_t i = firstPart; i < endPart; i++){payloadSize += parts.getSize(i);}
return payloadSize; return payloadSize;
@ -186,7 +175,7 @@ namespace CMAF{
((i + 1 < fragments.getEndValid()) ? fragments.getFirstKey(i + 1) : keys.getEndValid()); ((i + 1 < fragments.getEndValid()) ? fragments.getFirstKey(i + 1) : keys.getEndValid());
MP4::sidxReference refItem; MP4::sidxReference refItem;
refItem.referencedSize = payloadSize(M, track, i) + fragmentHeaderSize(M, track, i) + 8; refItem.referencedSize = payloadSize(M, track, keys.getTime(firstKey), keys.getTime(endKey)) + fragmentHeaderSize(M, track, i) + 8;
refItem.subSegmentDuration = refItem.subSegmentDuration =
(endKey == keys.getEndValid() ? M.getLastms(track) : keys.getTime(endKey)) - keys.getTime(firstKey); (endKey == keys.getEndValid() ? M.getLastms(track) : keys.getTime(endKey)) - keys.getTime(firstKey);
refItem.sapStart = true; refItem.sapStart = true;
@ -304,7 +293,7 @@ namespace CMAF{
if (M.getVod()){ if (M.getVod()){
tfdtBox.setBaseMediaDecodeTime(M.getTimeForFragmentIndex(track, fragment) - M.getFirstms(track)); tfdtBox.setBaseMediaDecodeTime(M.getTimeForFragmentIndex(track, fragment) - M.getFirstms(track));
}else{ }else{
tfdtBox.setBaseMediaDecodeTime((UTCTime ? Util::epoch()*1000 : M.getTimeForFragmentIndex(track, fragment))); tfdtBox.setBaseMediaDecodeTime((UTCTime ? M.getTimeForFragmentIndex(track, fragment) + M.getBootMsOffset() + unixBootDiff : M.getTimeForFragmentIndex(track, fragment)));
} }
trafBox.setContent(tfdtBox, 1); trafBox.setContent(tfdtBox, 1);
@ -337,50 +326,36 @@ namespace CMAF{
/// Calculates the full size of a 'moof' box for a DTSC::Key based fragment. /// Calculates the full size of a 'moof' box for a DTSC::Key based fragment.
/// Used when building the 'moof' box to calculate the relative data offsets. /// Used when building the 'moof' box to calculate the relative data offsets.
size_t keyHeaderSize(const DTSC::Meta &M, size_t track, size_t key){ size_t keyHeaderSize(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime){
uint64_t tmpRes = 8 + 16 + 32 + 20; uint64_t tmpRes = 8 + 16 + 32 + 20;
size_t firstPart = M.getPartIndex(startTime, track);
DTSC::Keys keys(M.keys(track)); size_t endPart = M.getPartIndex(endTime, track);
DTSC::Parts parts(M.parts(track));
size_t firstPart = keys.getFirstPart(key);
size_t endPart = parts.getEndValid();
if (key + 1 < keys.getEndValid()){
endPart = keys.getFirstPart(key + 1);
}
tmpRes += 24 + ((endPart - firstPart) * 12); tmpRes += 24 + ((endPart - firstPart) * 12);
return tmpRes; return tmpRes;
} }
/// Generates the 'moof' box for a DTSC::Key based CMAF fragment. /// Generates the 'moof' box for a DTSC::Key based CMAF fragment.
std::string keyHeader(const DTSC::Meta &M, size_t track, size_t key, bool simplifyTrackIds, bool UTCTime){ std::string keyHeader(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime, uint64_t segmentNum, bool simplifyTrackIds, bool UTCTime){
DTSC::Keys keys(M.keys(track));
DTSC::Parts parts(M.parts(track));
size_t firstPart = keys.getFirstPart(key);
size_t endPart = parts.getEndValid();
if (key + 1 < keys.getEndValid()){
endPart = keys.getFirstPart(key + 1);
}
size_t firstPart = M.getPartIndex(startTime, track);
size_t endPart = M.getPartIndex(endTime, track);
std::stringstream header; std::stringstream header;
MP4::MOOF moofBox; MP4::MOOF moofBox;
MP4::MFHD mfhdBox(key + 1); MP4::MFHD mfhdBox(segmentNum);
moofBox.setContent(mfhdBox, 0); moofBox.setContent(mfhdBox, 0);
std::set<sortPart> trunOrder; std::set<sortPart> trunOrder;
//We use keyHeaderSize here to determine the relative offsets of the data in the 'mdat' box. //We use keyHeaderSize here to determine the relative offsets of the data in the 'mdat' box.
uint64_t relativeOffset = keyHeaderSize(M, track, key) + 8; uint64_t relativeOffset = keyHeaderSize(M, track, startTime, endTime) + 8;
sortPart temp; sortPart temp;
temp.time = keys.getTime(key); temp.time = startTime;
temp.partIndex = firstPart; temp.partIndex = firstPart;
temp.bytePos = relativeOffset; temp.bytePos = relativeOffset;
DTSC::Parts parts(M.parts(track));
for (size_t p = firstPart; p < endPart; p++){ for (size_t p = firstPart; p < endPart; p++){
trunOrder.insert(temp); trunOrder.insert(temp);
temp.time += parts.getDuration(p); temp.time += parts.getDuration(p);
@ -405,9 +380,9 @@ namespace CMAF{
MP4::TFDT tfdtBox; MP4::TFDT tfdtBox;
if (M.getVod()){ if (M.getVod()){
tfdtBox.setBaseMediaDecodeTime(keys.getTime(key) - M.getFirstms(track)); tfdtBox.setBaseMediaDecodeTime(startTime - M.getFirstms(track));
}else{ }else{
tfdtBox.setBaseMediaDecodeTime((UTCTime ? Util::epoch()*1000 : keys.getTime(key) )); tfdtBox.setBaseMediaDecodeTime((UTCTime ? startTime + M.getBootMsOffset() + unixBootDiff : startTime));
} }
trafBox.setContent(tfdtBox, 1); trafBox.setContent(tfdtBox, 1);
@ -421,13 +396,22 @@ namespace CMAF{
size_t trunOffset = 0; size_t trunOffset = 0;
if (trunOrder.size()){
std::set<sortPart>::iterator lastOne = trunOrder.end();
lastOne--;
for (std::set<sortPart>::iterator it = trunOrder.begin(); it != trunOrder.end(); it++){ for (std::set<sortPart>::iterator it = trunOrder.begin(); it != trunOrder.end(); it++){
MP4::trunSampleInformation sampleInfo; MP4::trunSampleInformation sampleInfo;
sampleInfo.sampleSize = parts.getSize(it->partIndex); sampleInfo.sampleSize = parts.getSize(it->partIndex);
sampleInfo.sampleDuration = parts.getDuration(it->partIndex); sampleInfo.sampleDuration = parts.getDuration(it->partIndex);
if (it == lastOne){
sampleInfo.sampleDuration = endTime - it->time;
}
sampleInfo.sampleOffset = parts.getOffset(it->partIndex); sampleInfo.sampleOffset = parts.getOffset(it->partIndex);
trunBox.setSampleInformation(sampleInfo, trunOffset++); trunBox.setSampleInformation(sampleInfo, trunOffset++);
} }
}else{
WARN_MSG("Empty CMAF header for track %zu: %zu-%zu contains no packets (first: %" PRIu64 ", last: %" PRIu64 "), firstPart=%zu, lastPart=%zu", track, startTime, endTime, M.getFirstms(track), M.getLastms(track), firstPart, endPart);
}
trafBox.setContent(trunBox, 2); trafBox.setContent(trunBox, 2);
moofBox.setContent(trafBox, 1); moofBox.setContent(trafBox, 1);

View file

@ -4,11 +4,11 @@
#include <set> #include <set>
namespace CMAF{ namespace CMAF{
size_t payloadSize(const DTSC::Meta &M, size_t track, size_t index, bool isKeyIndex = false); size_t payloadSize(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime);
size_t trackHeaderSize(const DTSC::Meta &M, size_t track); size_t trackHeaderSize(const DTSC::Meta &M, size_t track);
std::string trackHeader(const DTSC::Meta &M, size_t track, bool simplifyTrackIds = false); std::string trackHeader(const DTSC::Meta &M, size_t track, bool simplifyTrackIds = false);
size_t fragmentHeaderSize(const DTSC::Meta &M, size_t track, size_t fragment); size_t fragmentHeaderSize(const DTSC::Meta &M, size_t track, size_t fragment);
std::string fragmentHeader(const DTSC::Meta &M, size_t track, size_t fragment, bool simplifyTrackIds = false, bool UTCTime = false); std::string fragmentHeader(const DTSC::Meta &M, size_t track, size_t fragment, bool simplifyTrackIds = false, bool UTCTime = false);
size_t keyHeaderSize(const DTSC::Meta &M, size_t track, size_t key); size_t keyHeaderSize(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime);
std::string keyHeader(const DTSC::Meta &M, size_t track, size_t key, bool simplifyTrackIds = false, bool UTCTime = false); std::string keyHeader(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime, uint64_t segmentNum, bool simplifyTrackIds = false, bool UTCTime = false);
}// namespace CMAF }// namespace CMAF

View file

@ -2890,27 +2890,27 @@ namespace DTSC{
return keys.getTime(fragments.getFirstKey(fragmentIdx)); return keys.getTime(fragments.getFirstKey(fragmentIdx));
} }
/// Returns the part index for the given DTSC::Packet by timestamp. /// Returns the part index for the given timestamp.
/// Assumes the Packet is for the given track, and assumes the metadata and track data are not out /// Assumes the Packet is for the given track, and assumes the metadata and track data are not out
/// of sync. Works by looking up the key for the Packet's timestamp, then walking through the /// of sync. Works by looking up the key for the Packet's timestamp, then walking through the
/// parts until the time matches or exceeds the time of the Packet. Returns zero if the track /// parts until the time matches or exceeds the time of the Packet. Returns zero if the track
/// index is invalid or if the timestamp cannot be found. /// index is invalid or if the timestamp cannot be found.
uint32_t Meta::getPartIndex(const DTSC::Packet &pack, size_t idx) const{ uint32_t Meta::getPartIndex(uint64_t timestamp, size_t idx) const{
if (idx == INVALID_TRACK_ID){return 0;} if (idx == INVALID_TRACK_ID){return 0;}
uint32_t res = 0; uint32_t res = 0;
uint32_t keyIdx = getKeyIndexForTime(idx, pack.getTime()); uint32_t keyIdx = getKeyIndexForTime(idx, timestamp);
DTSC::Keys Keys(keys(idx)); DTSC::Keys Keys(keys(idx));
DTSC::Parts Parts(parts(idx)); DTSC::Parts Parts(parts(idx));
uint64_t currentTime = Keys.getTime(keyIdx); uint64_t currentTime = Keys.getTime(keyIdx);
res = Keys.getFirstPart(keyIdx); res = Keys.getFirstPart(keyIdx);
size_t endPart = res + Keys.getParts(keyIdx); size_t endPart = Parts.getEndValid();
for (size_t i = res; i < endPart; i++){ for (size_t i = res; i < endPart; i++){
if (currentTime >= pack.getTime()){return res;} if (currentTime >= timestamp){return res;}
currentTime += Parts.getDuration(i); currentTime += Parts.getDuration(i);
res++; res++;
} }
return 0; return res;
} }
/// Given the current page, check if the next page is available. Returns true if it is. /// Given the current page, check if the next page is available. Returns true if it is.

View file

@ -426,7 +426,7 @@ namespace DTSC{
uint64_t getTimeForKeyIndex(uint32_t idx, uint32_t keyIdx) const; uint64_t getTimeForKeyIndex(uint32_t idx, uint32_t keyIdx) const;
uint32_t getKeyIndexForTime(uint32_t idx, uint64_t timestamp) const; uint32_t getKeyIndexForTime(uint32_t idx, uint64_t timestamp) const;
uint32_t getPartIndex(const DTSC::Packet &pack, size_t idx) const; uint32_t getPartIndex(uint64_t timestamp, size_t idx) const;
bool nextPageAvailable(uint32_t idx, size_t currentPage) const; bool nextPageAvailable(uint32_t idx, size_t currentPage) const;
size_t getPageNumberForTime(uint32_t idx, uint64_t time) const; size_t getPageNumberForTime(uint32_t idx, uint64_t time) const;

View file

@ -1262,11 +1262,11 @@ namespace Mist{
} }
if (encryption.substr(0, encryption.find('/')) == "CTR128"){ if (encryption.substr(0, encryption.find('/')) == "CTR128"){
DTSC::Packet encPacket = aesCipher.encryptPacketCTR( DTSC::Packet encPacket = aesCipher.encryptPacketCTR(
M, thisPacket, M.getIvec(idx) + M.getPartIndex(thisPacket, idx), idx); M, thisPacket, M.getIvec(idx) + M.getPartIndex(thisPacket.getTime(), idx), idx);
thisPacket = encPacket; thisPacket = encPacket;
}else if (encryption.substr(0, encryption.find('/')) == "CBC128"){ }else if (encryption.substr(0, encryption.find('/')) == "CBC128"){
char ivec[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; char ivec[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Bit::htobll(ivec + 8, M.getIvec(idx) + M.getPartIndex(thisPacket, idx)); Bit::htobll(ivec + 8, M.getIvec(idx) + M.getPartIndex(thisPacket.getTime(), idx));
DTSC::Packet encPacket = aesCipher.encryptPacketCBC(M, thisPacket, ivec, idx); DTSC::Packet encPacket = aesCipher.encryptPacketCBC(M, thisPacket, ivec, idx);
thisPacket = encPacket; thisPacket = encPacket;
} }

View file

@ -725,7 +725,7 @@ namespace Mist{
if (tmpPack){ if (tmpPack){
HIGH_MSG("Sought to time %" PRIu64 " (yields a packet at %" PRIu64 "ms) in %s@%zu", tmp.time, HIGH_MSG("Sought to time %" PRIu64 " (yields a packet at %" PRIu64 "ms) in %s@%zu", tmp.time,
tmpPack.getTime(), streamName.c_str(), tid); tmpPack.getTime(), streamName.c_str(), tid);
tmp.partIndex = M.getPartIndex(tmpPack, tmp.tid); tmp.partIndex = M.getPartIndex(tmpPack.getTime(), tmp.tid);
buffer.insert(tmp); buffer.insert(tmp);
return true; return true;
} }

View file

@ -237,7 +237,7 @@ namespace Mist{
std::string headerData = CMAF::fragmentHeader(M, idx, fragmentIndex, false, false); std::string headerData = CMAF::fragmentHeader(M, idx, fragmentIndex, false, false);
H.Chunkify(headerData.c_str(), headerData.size(), myConn); H.Chunkify(headerData.c_str(), headerData.size(), myConn);
uint64_t mdatSize = 8 + CMAF::payloadSize(M, idx, fragmentIndex); uint64_t mdatSize = 8 + CMAF::payloadSize(M, idx, targetTime, M.getTimeForFragmentIndex(idx, fragmentIndex+1));
char mdatHeader[] ={0x00, 0x00, 0x00, 0x00, 'm', 'd', 'a', 't'}; char mdatHeader[] ={0x00, 0x00, 0x00, 0x00, 'm', 'd', 'a', 't'};
Bit::htobl(mdatHeader, mdatSize); Bit::htobl(mdatHeader, mdatSize);
@ -777,7 +777,6 @@ namespace Mist{
if (!pushTracks.count(idx) || !pushTracks.at(idx).D.getSocket()){return;} if (!pushTracks.count(idx) || !pushTracks.at(idx).D.getSocket()){return;}
INFO_MSG("Disconnecting track %zu", idx); INFO_MSG("Disconnecting track %zu", idx);
pushTracks[idx].disconnect(); pushTracks[idx].disconnect();
pushTracks.erase(idx); pushTracks.erase(idx);
} }
@ -803,11 +802,13 @@ namespace Mist{
/// Function that waits at most `maxWait` ms (in steps of 100ms) for the next keyframe to become available. /// Function that waits at most `maxWait` ms (in steps of 100ms) for the next keyframe to become available.
/// Uses thisIdx and thisPacket to determine track and current timestamp respectively. /// Uses thisIdx and thisPacket to determine track and current timestamp respectively.
bool OutCMAF::waitForNextKey(uint64_t maxWait){ bool OutCMAF::waitForNextKey(uint64_t maxWait){
size_t currentKey = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); size_t currentKey = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime());
DTSC::Keys keys(M.keys(thisIdx)); DTSC::Keys keys(M.keys(getMainSelectedTrack()));
size_t waitTimes = maxWait / 100; size_t waitTimes = maxWait / 100;
for (size_t i = 0; i < waitTimes; ++i){ for (size_t i = 0; i < waitTimes; ++i){
if (keys.getEndValid() > currentKey + 1){return true;} if (keys.getEndValid() > currentKey + 1 && M.getLastms(thisIdx) > M.getTimeForKeyIndex(getMainSelectedTrack(), currentKey+1)){
return true;
}
Util::wait(100); Util::wait(100);
//Make sure we don't accidentally timeout while waiting - runs approximately every second. //Make sure we don't accidentally timeout while waiting - runs approximately every second.
if (i % 10 == 0){ if (i % 10 == 0){
@ -817,7 +818,7 @@ namespace Mist{
} }
} }
} }
return (keys.getEndValid() > currentKey + 1); return (keys.getEndValid() > currentKey + 1 && M.getLastms(thisIdx) > M.getTimeForKeyIndex(getMainSelectedTrack(), currentKey+1));
} }
//Set up an empty connection to the target to make sure we can push data towards it. //Set up an empty connection to the target to make sure we can push data towards it.
@ -833,14 +834,15 @@ namespace Mist{
void OutCMAF::pushNext() { void OutCMAF::pushNext() {
//Set up a new connection if this is a new track, or if we have been disconnected. //Set up a new connection if this is a new track, or if we have been disconnected.
if (!pushTracks.count(thisIdx) || !pushTracks.at(thisIdx).D.getSocket()){ if (!pushTracks.count(thisIdx) || !pushTracks.at(thisIdx).D.getSocket()){
if (pushTracks.count(thisIdx)){INFO_MSG("Reconnecting existing track: socket was disconnected");}
CMAFPushTrack & track = pushTracks[thisIdx]; CMAFPushTrack & track = pushTracks[thisIdx];
size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime());
track.headerFrom = M.getTimeForKeyIndex(thisIdx, keyIndex); track.headerFrom = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex);
if (track.headerFrom < thisPacket.getTime()){ if (track.headerFrom < thisPacket.getTime()){
track.headerFrom = M.getTimeForKeyIndex(thisIdx, keyIndex + 1); track.headerFrom = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex + 1);
} }
HIGH_MSG("Starting track %zu at %" PRIu64 "ms into the stream, current packet at %" PRIu64 "ms", thisIdx, track.headerFrom, thisPacket.getTime()); INFO_MSG("Starting track %zu at %" PRIu64 "ms into the stream, current packet at %" PRIu64 "ms", thisIdx, track.headerFrom, thisPacket.getTime());
setupTrackObject(thisIdx); setupTrackObject(thisIdx);
track.headerUntil = 0; track.headerUntil = 0;
@ -848,13 +850,13 @@ namespace Mist{
} }
CMAFPushTrack & track = pushTracks[thisIdx]; CMAFPushTrack & track = pushTracks[thisIdx];
if (thisPacket.getTime() < track.headerFrom){return;} if (thisPacket.getTime() < track.headerFrom){return;}
if (thisPacket.getTime() > track.headerUntil || !track.headerUntil){ if (thisPacket.getTime() >= track.headerUntil){
size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime());
uint64_t keyTime = M.getTimeForKeyIndex(thisIdx, keyIndex); uint64_t keyTime = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex);
if (keyTime != thisPacket.getTime()){ if (keyTime > thisPacket.getTime()){
WARN_MSG("Corruption probably occured, initiating reconnect %" PRIu64 " != %" PRIu64, keyTime, thisPacket.getTime()); WARN_MSG("Corruption probably occurred, initiating reconnect %" PRIu64 " != %" PRIu64, keyTime, thisPacket.getTime());
onTrackEnd(thisIdx); onTrackEnd(thisIdx);
track.headerFrom = M.getTimeForKeyIndex(thisIdx, keyIndex + 1); track.headerFrom = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex + 1);
track.headerUntil = 0; track.headerUntil = 0;
pushNext(); pushNext();
return; return;
@ -865,10 +867,9 @@ namespace Mist{
dropTrack(thisIdx, "No next keyframe available"); dropTrack(thisIdx, "No next keyframe available");
return; return;
} }
track.headerUntil = M.getTimeForKeyIndex(thisIdx, keyIndex + 1) - 1; track.headerUntil = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex + 1);
std::string keyHeader = CMAF::keyHeader(M, thisIdx, keyIndex, true, true); std::string keyHeader = CMAF::keyHeader(M, thisIdx, track.headerFrom, track.headerUntil, keyIndex+1, true, true);
uint64_t mdatSize = 8 + CMAF::payloadSize(M, thisIdx, track.headerFrom, track.headerUntil);
uint64_t mdatSize = 8 + CMAF::payloadSize(M, thisIdx, keyIndex, true);
char mdatHeader[] ={0x00, 0x00, 0x00, 0x00, 'm', 'd', 'a', 't'}; char mdatHeader[] ={0x00, 0x00, 0x00, 0x00, 'm', 'd', 'a', 't'};
Bit::htobl(mdatHeader, mdatSize); Bit::htobl(mdatHeader, mdatSize);