Fixes to better support Safari/MacOS/iOS playback of MP4 output
Co-authored-by: Marco van Dijk <marco@stronk.rocks> Co-authored-by: Thulinma <jaron@vietors.com>
This commit is contained in:
parent
963d7846a8
commit
d63190387a
3 changed files with 42 additions and 18 deletions
|
@ -2007,8 +2007,8 @@ namespace MP4{
|
||||||
TKHD::TKHD(const DTSC::Meta &M, size_t idx){
|
TKHD::TKHD(const DTSC::Meta &M, size_t idx){
|
||||||
initialize();
|
initialize();
|
||||||
setTrackID(idx + 1);
|
setTrackID(idx + 1);
|
||||||
setDuration(-1);
|
setDuration(0);
|
||||||
if (M.getVod()){setDuration(M.getLastms(idx) - M.getFirstms(idx));}
|
if (!M.getLive()){setDuration(M.getLastms(idx) - M.getFirstms(idx));}
|
||||||
if (M.getType(idx) == "video"){
|
if (M.getType(idx) == "video"){
|
||||||
setWidth(M.getWidth(idx));
|
setWidth(M.getWidth(idx));
|
||||||
setHeight(M.getHeight(idx));
|
setHeight(M.getHeight(idx));
|
||||||
|
@ -2757,7 +2757,7 @@ namespace MP4{
|
||||||
setCLAP(avccBox);
|
setCLAP(avccBox);
|
||||||
}
|
}
|
||||||
if (tCodec == "HEVC"){
|
if (tCodec == "HEVC"){
|
||||||
setCodec("hev1");
|
setCodec("hvc1");
|
||||||
MP4::HVCC hvccBox;
|
MP4::HVCC hvccBox;
|
||||||
hvccBox.setPayload(M.getInit(idx));
|
hvccBox.setPayload(M.getInit(idx));
|
||||||
setCLAP(hvccBox);
|
setCLAP(hvccBox);
|
||||||
|
|
|
@ -111,6 +111,7 @@ namespace Mist{
|
||||||
startTime = 0;
|
startTime = 0;
|
||||||
endTime = 0xffffffffffffffffull;
|
endTime = 0xffffffffffffffffull;
|
||||||
realBaseOffset = 1;
|
realBaseOffset = 1;
|
||||||
|
timeOffset = 0;
|
||||||
}
|
}
|
||||||
OutMP4::~OutMP4(){}
|
OutMP4::~OutMP4(){}
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ namespace Mist{
|
||||||
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||||
DTSC::Keys keys = M.getKeys(it->first);
|
DTSC::Keys keys = M.getKeys(it->first);
|
||||||
size_t endKey = keys.getEndValid();
|
size_t endKey = keys.getEndValid();
|
||||||
for (size_t i = 0; i < endKey; i++){
|
for (size_t i = keys.getFirstValid(); i < endKey; i++){
|
||||||
retVal += keys.getSize(i); // Handle number as index, faster for VoD
|
retVal += keys.getSize(i); // Handle number as index, faster for VoD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +177,7 @@ namespace Mist{
|
||||||
|
|
||||||
for (std::map<size_t, Comms::Users>::const_iterator subIt = userSelect.begin();
|
for (std::map<size_t, Comms::Users>::const_iterator subIt = userSelect.begin();
|
||||||
subIt != userSelect.end(); subIt++){
|
subIt != userSelect.end(); subIt++){
|
||||||
tmpRes += 8 + 20; // TRAF + TFHD Box
|
tmpRes += 8 + 20 + 20; // TRAF + TFHD + TFDT Box
|
||||||
|
|
||||||
DTSC::Keys keys = M.getKeys(subIt->first);
|
DTSC::Keys keys = M.getKeys(subIt->first);
|
||||||
DTSC::Parts parts(M.parts(subIt->first));
|
DTSC::Parts parts(M.parts(subIt->first));
|
||||||
|
@ -377,7 +378,7 @@ namespace Mist{
|
||||||
|
|
||||||
uint64_t firstms = 0xFFFFFFFFFFFFFFull;
|
uint64_t firstms = 0xFFFFFFFFFFFFFFull;
|
||||||
// Construct with duration of -1, as this is the default for fragmented
|
// Construct with duration of -1, as this is the default for fragmented
|
||||||
MP4::MVHD mvhdBox(-1);
|
MP4::MVHD mvhdBox(0);
|
||||||
// Then override it when we are not sending a VoD asset
|
// Then override it when we are not sending a VoD asset
|
||||||
if (!M.getLive()){
|
if (!M.getLive()){
|
||||||
// calculating longest duration
|
// calculating longest duration
|
||||||
|
@ -428,7 +429,7 @@ namespace Mist{
|
||||||
elstBox.setMediaRateFraction(1, 0);
|
elstBox.setMediaRateFraction(1, 0);
|
||||||
}else{
|
}else{
|
||||||
elstBox.setCount(1);
|
elstBox.setCount(1);
|
||||||
elstBox.setSegmentDuration(0, fragmented ? -1 : tDuration);
|
elstBox.setSegmentDuration(0, fragmented ? 0 : tDuration);
|
||||||
elstBox.setMediaTime(0, 0);
|
elstBox.setMediaTime(0, 0);
|
||||||
elstBox.setMediaRateInteger(0, 1);
|
elstBox.setMediaRateInteger(0, 1);
|
||||||
elstBox.setMediaRateFraction(0, 0);
|
elstBox.setMediaRateFraction(0, 0);
|
||||||
|
@ -441,7 +442,7 @@ namespace Mist{
|
||||||
|
|
||||||
// Add the mandatory MDHD and HDLR boxes to the MDIA
|
// Add the mandatory MDHD and HDLR boxes to the MDIA
|
||||||
MP4::MDHD mdhdBox(tDuration);
|
MP4::MDHD mdhdBox(tDuration);
|
||||||
if (fragmented){mdhdBox.setDuration(-1);}
|
if (fragmented){mdhdBox.setDuration(0);}
|
||||||
mdhdBox.setLanguage(M.getLang(it->first));
|
mdhdBox.setLanguage(M.getLang(it->first));
|
||||||
mdiaBox.setContent(mdhdBox, mdiaOffset++);
|
mdiaBox.setContent(mdhdBox, mdiaOffset++);
|
||||||
MP4::HDLR hdlrBox(tType, M.getTrackIdentifier(it->first));
|
MP4::HDLR hdlrBox(tType, M.getTrackIdentifier(it->first));
|
||||||
|
@ -661,7 +662,7 @@ namespace Mist{
|
||||||
MP4::MVEX mvexBox;
|
MP4::MVEX mvexBox;
|
||||||
size_t curBox = 0;
|
size_t curBox = 0;
|
||||||
MP4::MEHD mehdBox;
|
MP4::MEHD mehdBox;
|
||||||
mehdBox.setFragmentDuration(M.getDuration(mainTrack));
|
mehdBox.setFragmentDuration(0);
|
||||||
|
|
||||||
mvexBox.setContent(mehdBox, curBox++);
|
mvexBox.setContent(mehdBox, curBox++);
|
||||||
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin();
|
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin();
|
||||||
|
@ -868,7 +869,7 @@ namespace Mist{
|
||||||
trafBox.setContent(tfdtBox, 1);
|
trafBox.setContent(tfdtBox, 1);
|
||||||
|
|
||||||
MP4::TRUN trunBox;
|
MP4::TRUN trunBox;
|
||||||
trunBox.setFirstSampleFlags(MP4::isIPicture | MP4::isKeySample);
|
trunBox.setFirstSampleFlags(thisPacket.getFlag("keyframe") ? (MP4::isIPicture | MP4::isKeySample) : (MP4::noIPicture | MP4::noKeySample));
|
||||||
trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize |
|
trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize |
|
||||||
MP4::trunsampleDuration | MP4::trunsampleOffsets);
|
MP4::trunsampleDuration | MP4::trunsampleOffsets);
|
||||||
|
|
||||||
|
@ -916,6 +917,7 @@ namespace Mist{
|
||||||
sortSet.clear();
|
sortSet.clear();
|
||||||
|
|
||||||
std::set<keyPart> trunOrder;
|
std::set<keyPart> trunOrder;
|
||||||
|
std::set<size_t> keyParts;
|
||||||
std::deque<size_t> sortedTracks;
|
std::deque<size_t> sortedTracks;
|
||||||
|
|
||||||
if (endFragmentTime == 0){
|
if (endFragmentTime == 0){
|
||||||
|
@ -951,6 +953,11 @@ namespace Mist{
|
||||||
temp.time = timeStamp;
|
temp.time = timeStamp;
|
||||||
temp.index = p;
|
temp.index = p;
|
||||||
trunOrder.insert(temp);
|
trunOrder.insert(temp);
|
||||||
|
|
||||||
|
uint64_t keyTime = M.getTimeForKeyIndex(subIt->first, M.getKeyIndexForTime(subIt->first, timeStamp));
|
||||||
|
if (keyTime == timeStamp){
|
||||||
|
keyParts.insert(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timeStamp += parts.getDuration(p);
|
timeStamp += parts.getDuration(p);
|
||||||
|
@ -987,8 +994,6 @@ namespace Mist{
|
||||||
}
|
}
|
||||||
trunOrder.clear(); // erase the trunOrder set, to keep memory usage down
|
trunOrder.clear(); // erase the trunOrder set, to keep memory usage down
|
||||||
|
|
||||||
bool firstSample = true;
|
|
||||||
|
|
||||||
for (std::deque<size_t>::iterator it = sortedTracks.begin(); it != sortedTracks.end(); ++it){
|
for (std::deque<size_t>::iterator it = sortedTracks.begin(); it != sortedTracks.end(); ++it){
|
||||||
size_t tid = *it;
|
size_t tid = *it;
|
||||||
DTSC::Parts parts(M.parts(*it));
|
DTSC::Parts parts(M.parts(*it));
|
||||||
|
@ -1004,9 +1009,14 @@ namespace Mist{
|
||||||
tfhdBox.setDefaultSampleSize(444);
|
tfhdBox.setDefaultSampleSize(444);
|
||||||
tfhdBox.setDefaultSampleFlags(tid == vidTrack ? (MP4::noIPicture | MP4::noKeySample)
|
tfhdBox.setDefaultSampleFlags(tid == vidTrack ? (MP4::noIPicture | MP4::noKeySample)
|
||||||
: (MP4::isIPicture | MP4::isKeySample));
|
: (MP4::isIPicture | MP4::isKeySample));
|
||||||
trafBox.setContent(tfhdBox, 0);
|
unsigned int trafOffset = 0;
|
||||||
|
trafBox.setContent(tfhdBox, trafOffset++);
|
||||||
|
|
||||||
|
MP4::TFDT tfdtBox;
|
||||||
|
tfdtBox.setBaseMediaDecodeTime(startFragmentTime - timeOffset);
|
||||||
|
trafBox.setContent(tfdtBox, trafOffset++);
|
||||||
|
|
||||||
|
|
||||||
unsigned int trafOffset = 1;
|
|
||||||
for (std::set<keyPart>::iterator trunIt = sortSet.begin(); trunIt != sortSet.end(); trunIt++){
|
for (std::set<keyPart>::iterator trunIt = sortSet.begin(); trunIt != sortSet.end(); trunIt++){
|
||||||
if (trunIt->trackID == tid){
|
if (trunIt->trackID == tid){
|
||||||
DTSC::Parts parts(M.parts(trunIt->trackID));
|
DTSC::Parts parts(M.parts(trunIt->trackID));
|
||||||
|
@ -1021,8 +1031,11 @@ namespace Mist{
|
||||||
// The value set here, will be updated afterwards to the correct value
|
// The value set here, will be updated afterwards to the correct value
|
||||||
trunBox.setDataOffset(trunIt->byteOffset);
|
trunBox.setDataOffset(trunIt->byteOffset);
|
||||||
|
|
||||||
trunBox.setFirstSampleFlags(MP4::isIPicture | (firstSample ? MP4::isKeySample : MP4::noKeySample));
|
bool isKeyFrame = keyParts.count(trunIt->index);
|
||||||
firstSample = false;
|
if (M.getType(trunIt->trackID) != "video"){
|
||||||
|
isKeyFrame = true;
|
||||||
|
}
|
||||||
|
trunBox.setFirstSampleFlags(isKeyFrame ? (MP4::isKeySample | MP4::isIPicture) : (MP4::noKeySample | MP4::noIPicture));
|
||||||
|
|
||||||
MP4::trunSampleInformation sampleInfo;
|
MP4::trunSampleInformation sampleInfo;
|
||||||
sampleInfo.sampleSize = partSize;
|
sampleInfo.sampleSize = partSize;
|
||||||
|
@ -1197,8 +1210,17 @@ namespace Mist{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendFragmentHeaderTime(thisPacket.getTime(), thisPacket.getTime() + needsLookAhead);
|
|
||||||
nextHeaderTime = thisPacket.getTime() + needsLookAhead;
|
nextHeaderTime = thisTime + needsLookAhead;
|
||||||
|
if (targetParams.count("recstop")){
|
||||||
|
uint64_t planStop = atoll(targetParams["recstop"].c_str());
|
||||||
|
if (planStop < nextHeaderTime){nextHeaderTime = planStop;}
|
||||||
|
}
|
||||||
|
if (targetParams.count("stop")){
|
||||||
|
uint64_t planStop = atoll(targetParams["stop"].c_str());
|
||||||
|
if (planStop < nextHeaderTime){nextHeaderTime = planStop;}
|
||||||
|
}
|
||||||
|
if (thisTime < nextHeaderTime){sendFragmentHeaderTime(thisTime, nextHeaderTime);}
|
||||||
}else{
|
}else{
|
||||||
if (startTime || endTime != 0xffffffffffffffffull){
|
if (startTime || endTime != 0xffffffffffffffffull){
|
||||||
sendFragmentHeaderTime(startTime, endTime);
|
sendFragmentHeaderTime(startTime, endTime);
|
||||||
|
@ -1336,6 +1358,7 @@ namespace Mist{
|
||||||
INFO_MSG("Increased initial lookAhead of %" PRIu64 "ms", needsLookAhead);
|
INFO_MSG("Increased initial lookAhead of %" PRIu64 "ms", needsLookAhead);
|
||||||
initialSeek();
|
initialSeek();
|
||||||
}
|
}
|
||||||
|
timeOffset = currentTime();
|
||||||
}else{
|
}else{
|
||||||
seek(seekPoint);
|
seek(seekPoint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ namespace Mist{
|
||||||
int64_t leftOver;
|
int64_t leftOver;
|
||||||
uint64_t currPos;
|
uint64_t currPos;
|
||||||
uint64_t seekPoint;
|
uint64_t seekPoint;
|
||||||
|
int64_t timeOffset;
|
||||||
|
|
||||||
uint64_t nextHeaderTime;
|
uint64_t nextHeaderTime;
|
||||||
uint64_t headerSize;
|
uint64_t headerSize;
|
||||||
|
|
Loading…
Add table
Reference in a new issue