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
		Add a link
		
	
		Reference in a new issue