Fixes to CMAF output
This commit is contained in:
		
							parent
							
								
									e217f41f17
								
							
						
					
					
						commit
						a8e04e1787
					
				
					 7 changed files with 69 additions and 84 deletions
				
			
		
							
								
								
									
										72
									
								
								lib/cmaf.cpp
									
										
									
									
									
								
							
							
						
						
									
										72
									
								
								lib/cmaf.cpp
									
										
									
									
									
								
							|  | @ -1,24 +1,13 @@ | |||
| #include "cmaf.h" | ||||
| 
 | ||||
| static uint64_t unixBootDiff = (Util::unixMS() - Util::bootMS()); | ||||
| 
 | ||||
| namespace CMAF{ | ||||
|   /// 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, size_t index, bool isKeyIndex){ | ||||
|     DTSC::Fragments fragments(M.fragments(track)); | ||||
|     DTSC::Keys keys(M.keys(track)); | ||||
|   size_t payloadSize(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime){ | ||||
|     DTSC::Parts parts(M.parts(track)); | ||||
| 
 | ||||
|     size_t firstKey = (isKeyIndex ? index : fragments.getFirstKey(index)); | ||||
|     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 firstPart = M.getPartIndex(startTime, track); | ||||
|     size_t endPart = M.getPartIndex(endTime, track); | ||||
|     size_t payloadSize = 0; | ||||
|     for (size_t i = firstPart; i < endPart; i++){payloadSize += parts.getSize(i);} | ||||
|     return payloadSize; | ||||
|  | @ -186,7 +175,7 @@ namespace CMAF{ | |||
|             ((i + 1 < fragments.getEndValid()) ? fragments.getFirstKey(i + 1) : keys.getEndValid()); | ||||
| 
 | ||||
|         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 = | ||||
|             (endKey == keys.getEndValid() ? M.getLastms(track) : keys.getTime(endKey)) - keys.getTime(firstKey); | ||||
|         refItem.sapStart = true; | ||||
|  | @ -304,7 +293,7 @@ namespace CMAF{ | |||
|     if (M.getVod()){ | ||||
|       tfdtBox.setBaseMediaDecodeTime(M.getTimeForFragmentIndex(track, fragment) - M.getFirstms(track)); | ||||
|     }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); | ||||
| 
 | ||||
|  | @ -337,50 +326,36 @@ namespace CMAF{ | |||
| 
 | ||||
|   /// 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.
 | ||||
|   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; | ||||
| 
 | ||||
|     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); | ||||
|     tmpRes += 24 + ((endPart - firstPart) * 12); | ||||
|     return tmpRes; | ||||
|   } | ||||
| 
 | ||||
|   /// 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){ | ||||
|     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); | ||||
|     } | ||||
|   std::string keyHeader(const DTSC::Meta &M, size_t track, uint64_t startTime, uint64_t endTime, uint64_t segmentNum, bool simplifyTrackIds, bool UTCTime){ | ||||
| 
 | ||||
|     size_t firstPart = M.getPartIndex(startTime, track); | ||||
|     size_t endPart = M.getPartIndex(endTime, track); | ||||
|     std::stringstream header; | ||||
| 
 | ||||
|     MP4::MOOF moofBox; | ||||
|     MP4::MFHD mfhdBox(key + 1); | ||||
|     MP4::MFHD mfhdBox(segmentNum); | ||||
|     moofBox.setContent(mfhdBox, 0); | ||||
| 
 | ||||
| 
 | ||||
|     std::set<sortPart> trunOrder; | ||||
| 
 | ||||
|     //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; | ||||
|     temp.time = keys.getTime(key); | ||||
|     temp.time = startTime; | ||||
|     temp.partIndex = firstPart; | ||||
|     temp.bytePos = relativeOffset; | ||||
| 
 | ||||
|     DTSC::Parts parts(M.parts(track)); | ||||
|     for (size_t p = firstPart; p < endPart; p++){ | ||||
|       trunOrder.insert(temp); | ||||
|       temp.time += parts.getDuration(p); | ||||
|  | @ -405,9 +380,9 @@ namespace CMAF{ | |||
| 
 | ||||
|     MP4::TFDT tfdtBox; | ||||
|     if (M.getVod()){ | ||||
|       tfdtBox.setBaseMediaDecodeTime(keys.getTime(key) - M.getFirstms(track)); | ||||
|       tfdtBox.setBaseMediaDecodeTime(startTime - M.getFirstms(track)); | ||||
|     }else{ | ||||
|       tfdtBox.setBaseMediaDecodeTime((UTCTime ? Util::epoch()*1000 : keys.getTime(key) )); | ||||
|       tfdtBox.setBaseMediaDecodeTime((UTCTime ? startTime + M.getBootMsOffset() + unixBootDiff : startTime)); | ||||
|     } | ||||
|     trafBox.setContent(tfdtBox, 1); | ||||
| 
 | ||||
|  | @ -421,13 +396,22 @@ namespace CMAF{ | |||
| 
 | ||||
|     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++){ | ||||
|         MP4::trunSampleInformation sampleInfo; | ||||
|         sampleInfo.sampleSize = parts.getSize(it->partIndex); | ||||
|         sampleInfo.sampleDuration = parts.getDuration(it->partIndex); | ||||
|         if (it == lastOne){ | ||||
|           sampleInfo.sampleDuration = endTime - it->time; | ||||
|         } | ||||
|         sampleInfo.sampleOffset = parts.getOffset(it->partIndex); | ||||
|         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); | ||||
| 
 | ||||
|     moofBox.setContent(trafBox, 1); | ||||
|  |  | |||
|  | @ -4,11 +4,11 @@ | |||
| #include <set> | ||||
| 
 | ||||
| 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); | ||||
|   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); | ||||
|   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); | ||||
|   std::string keyHeader(const DTSC::Meta &M, size_t track, size_t key, bool simplifyTrackIds = false, bool UTCTime = false); | ||||
|   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, uint64_t startTime, uint64_t endTime, uint64_t segmentNum, bool simplifyTrackIds = false, bool UTCTime = false); | ||||
| }// namespace CMAF
 | ||||
|  |  | |||
							
								
								
									
										12
									
								
								lib/dtsc.cpp
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								lib/dtsc.cpp
									
										
									
									
									
								
							|  | @ -2890,27 +2890,27 @@ namespace DTSC{ | |||
|     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
 | ||||
|   /// 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
 | ||||
|   /// 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;} | ||||
| 
 | ||||
|     uint32_t res = 0; | ||||
|     uint32_t keyIdx = getKeyIndexForTime(idx, pack.getTime()); | ||||
|     uint32_t keyIdx = getKeyIndexForTime(idx, timestamp); | ||||
|     DTSC::Keys Keys(keys(idx)); | ||||
|     DTSC::Parts Parts(parts(idx)); | ||||
|     uint64_t currentTime = Keys.getTime(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++){ | ||||
|       if (currentTime >= pack.getTime()){return res;} | ||||
|       if (currentTime >= timestamp){return res;} | ||||
|       currentTime += Parts.getDuration(i); | ||||
|       res++; | ||||
|     } | ||||
|     return 0; | ||||
|     return res; | ||||
|   } | ||||
| 
 | ||||
|   /// Given the current page, check if the next page is available. Returns true if it is.
 | ||||
|  |  | |||
|  | @ -426,7 +426,7 @@ namespace DTSC{ | |||
|     uint64_t getTimeForKeyIndex(uint32_t idx, uint32_t keyIdx) 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; | ||||
|     size_t getPageNumberForTime(uint32_t idx, uint64_t time) const; | ||||
|  |  | |||
|  | @ -1262,11 +1262,11 @@ namespace Mist{ | |||
|               } | ||||
|               if (encryption.substr(0, encryption.find('/')) == "CTR128"){ | ||||
|                 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; | ||||
|               }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}; | ||||
|                 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); | ||||
|                 thisPacket = encPacket; | ||||
|               } | ||||
|  |  | |||
|  | @ -725,7 +725,7 @@ namespace Mist{ | |||
|     if (tmpPack){ | ||||
|       HIGH_MSG("Sought to time %" PRIu64 " (yields a packet at %" PRIu64 "ms) in %s@%zu", tmp.time, | ||||
|                tmpPack.getTime(), streamName.c_str(), tid); | ||||
|       tmp.partIndex = M.getPartIndex(tmpPack, tmp.tid); | ||||
|       tmp.partIndex = M.getPartIndex(tmpPack.getTime(), tmp.tid); | ||||
|       buffer.insert(tmp); | ||||
|       return true; | ||||
|     } | ||||
|  |  | |||
|  | @ -237,7 +237,7 @@ namespace Mist{ | |||
|     std::string headerData = CMAF::fragmentHeader(M, idx, fragmentIndex, false, false); | ||||
|     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'}; | ||||
|     Bit::htobl(mdatHeader, mdatSize); | ||||
| 
 | ||||
|  | @ -777,7 +777,6 @@ namespace Mist{ | |||
|     if (!pushTracks.count(idx) || !pushTracks.at(idx).D.getSocket()){return;} | ||||
|     INFO_MSG("Disconnecting track %zu", idx); | ||||
|     pushTracks[idx].disconnect();  | ||||
|      | ||||
|     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.
 | ||||
|   /// Uses thisIdx and thisPacket to determine track and current timestamp respectively.
 | ||||
|   bool OutCMAF::waitForNextKey(uint64_t maxWait){ | ||||
|     size_t currentKey = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); | ||||
|     DTSC::Keys keys(M.keys(thisIdx)); | ||||
|     size_t currentKey = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime()); | ||||
|     DTSC::Keys keys(M.keys(getMainSelectedTrack())); | ||||
|     size_t waitTimes = maxWait / 100;  | ||||
|     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); | ||||
|       //Make sure we don't accidentally timeout while waiting - runs approximately every second.
 | ||||
|       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.
 | ||||
|  | @ -833,14 +834,15 @@ namespace Mist{ | |||
|   void OutCMAF::pushNext() { | ||||
|     //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)){INFO_MSG("Reconnecting existing track: socket was disconnected");} | ||||
|       CMAFPushTrack & track = pushTracks[thisIdx]; | ||||
|       size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); | ||||
|       track.headerFrom = M.getTimeForKeyIndex(thisIdx, keyIndex); | ||||
|       size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime()); | ||||
|       track.headerFrom = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex); | ||||
|       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); | ||||
|       track.headerUntil = 0; | ||||
|  | @ -848,13 +850,13 @@ namespace Mist{ | |||
|     } | ||||
|     CMAFPushTrack & track = pushTracks[thisIdx]; | ||||
|     if (thisPacket.getTime() < track.headerFrom){return;} | ||||
|     if (thisPacket.getTime() > track.headerUntil || !track.headerUntil){ | ||||
|       size_t keyIndex = M.getKeyIndexForTime(thisIdx, thisPacket.getTime()); | ||||
|       uint64_t keyTime = M.getTimeForKeyIndex(thisIdx, keyIndex); | ||||
|       if (keyTime != thisPacket.getTime()){ | ||||
|         WARN_MSG("Corruption probably occured, initiating reconnect %" PRIu64 " != %" PRIu64, keyTime, thisPacket.getTime()); | ||||
|     if (thisPacket.getTime() >= track.headerUntil){ | ||||
|       size_t keyIndex = M.getKeyIndexForTime(getMainSelectedTrack(), thisPacket.getTime()); | ||||
|       uint64_t keyTime = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex); | ||||
|       if (keyTime > thisPacket.getTime()){ | ||||
|         WARN_MSG("Corruption probably occurred, initiating reconnect %" PRIu64 " != %" PRIu64, keyTime, thisPacket.getTime()); | ||||
|         onTrackEnd(thisIdx); | ||||
|         track.headerFrom = M.getTimeForKeyIndex(thisIdx, keyIndex + 1); | ||||
|         track.headerFrom = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex + 1); | ||||
|         track.headerUntil = 0; | ||||
|         pushNext(); | ||||
|         return; | ||||
|  | @ -865,10 +867,9 @@ namespace Mist{ | |||
|         dropTrack(thisIdx, "No next keyframe available"); | ||||
|         return; | ||||
|       } | ||||
|       track.headerUntil = M.getTimeForKeyIndex(thisIdx, keyIndex + 1) - 1; | ||||
|       std::string keyHeader = CMAF::keyHeader(M, thisIdx, keyIndex, true, true); | ||||
| 
 | ||||
|       uint64_t mdatSize = 8 + CMAF::payloadSize(M, thisIdx, keyIndex, true); | ||||
|       track.headerUntil = M.getTimeForKeyIndex(getMainSelectedTrack(), keyIndex + 1); | ||||
|       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); | ||||
|       char mdatHeader[] ={0x00, 0x00, 0x00, 0x00, 'm', 'd', 'a', 't'}; | ||||
|       Bit::htobl(mdatHeader, mdatSize); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma