Added H264/MP4 keysonly mode, MP4 live simplification
This commit is contained in:
		
							parent
							
								
									6a5549444b
								
							
						
					
					
						commit
						2bbbf51fb3
					
				
					 4 changed files with 58 additions and 31 deletions
				
			
		|  | @ -4,6 +4,9 @@ | |||
| 
 | ||||
| namespace Mist{ | ||||
|   OutH264::OutH264(Socket::Connection &conn) : HTTPOutput(conn){ | ||||
|     if (targetParams.count("keysonly")){ | ||||
|       keysOnly = 1; | ||||
|     } | ||||
|     if (config->getString("target").size()){ | ||||
|       if (!streamName.size()){ | ||||
|         WARN_MSG("Recording unconnected H264 output to file! Cancelled."); | ||||
|  | @ -45,6 +48,7 @@ namespace Mist{ | |||
|   bool OutH264::isRecording(){return config->getString("target").size();} | ||||
| 
 | ||||
|   void OutH264::sendNext(){ | ||||
|     if (keysOnly && !thisPacket.getFlag("keyframe")){return;} | ||||
|     char *dataPointer = 0; | ||||
|     unsigned int len = 0; | ||||
|     thisPacket.getString("data", dataPointer, len); | ||||
|  | @ -70,6 +74,8 @@ namespace Mist{ | |||
| 
 | ||||
|   void OutH264::onHTTP(){ | ||||
|     std::string method = H.method; | ||||
|     //Set mode to key frames only
 | ||||
|     keysOnly = (H.GetVar("keysonly") != ""); | ||||
|     H.Clean(); | ||||
|     H.SetHeader("Content-Type", "video/H264"); | ||||
|     H.protocol = "HTTP/1.0"; | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ namespace Mist{ | |||
| 
 | ||||
|   private: | ||||
|     bool isRecording(); | ||||
|     bool keysOnly; | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,7 +9,9 @@ | |||
| #include <inttypes.h> | ||||
| 
 | ||||
| namespace Mist{ | ||||
|   OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){} | ||||
|   OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){ | ||||
|     keysOnly = false; | ||||
|   } | ||||
|   OutProgressiveMP4::~OutProgressiveMP4(){} | ||||
| 
 | ||||
|   void OutProgressiveMP4::init(Util::Config * cfg){ | ||||
|  | @ -566,6 +568,22 @@ namespace Mist{ | |||
|       uint64_t timeStamp = it->second.firstTime; | ||||
|       DTSC::Track & thisTrack = myMeta.tracks[it->first]; | ||||
|       for (uint32_t i = it->second.firstPart; i <= it->second.lastPart; i++){ | ||||
|         //If we're only sending keyframes, check if we have one.
 | ||||
|         if (keysOnly && thisTrack.type == "video"){ | ||||
|           uint32_t kParts = 0; | ||||
|           //If kParts equals the part number, we have a keyframe.
 | ||||
|           for (std::deque<DTSC::Key>::iterator ki = thisTrack.keys.begin(); ki != thisTrack.keys.end(); ++ki){ | ||||
|             timeStamp = ki->getTime(); | ||||
|             if (kParts >= i){break;} | ||||
|             kParts += ki->getParts(); | ||||
|           } | ||||
|           //Non keys are skipped completely.
 | ||||
|           if (kParts != i){ | ||||
|             //Skip forward to the next key
 | ||||
|             if (kParts-1 > i){i = kParts-1;} | ||||
|             continue; | ||||
|           } | ||||
|         } | ||||
|         keyPart temp; | ||||
|         temp.trackID = it->first; | ||||
|         temp.time = timeStamp; | ||||
|  | @ -632,6 +650,9 @@ namespace Mist{ | |||
|           uint64_t partOffset = thisTrack.parts[trunIt->index].getOffset(); | ||||
|           uint64_t partSize = thisTrack.parts[trunIt->index].getSize(); | ||||
|           uint64_t partDur = thisTrack.parts[trunIt->index].getDuration(); | ||||
|           if (keysOnly){ | ||||
|             partDur = keysOnly; | ||||
|           } | ||||
| 
 | ||||
|           MP4::TRUN trunBox; | ||||
|           trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize | MP4::trunsampleDuration | (partOffset ? MP4::trunsampleOffsets : 0)); | ||||
|  | @ -744,6 +765,8 @@ namespace Mist{ | |||
|         realTime = 0; | ||||
|       } | ||||
|     } | ||||
|     //Set mode to key frames only for video tracks
 | ||||
|     keysOnly = atoi(H.GetVar("keysonly").c_str()); | ||||
| 
 | ||||
|     /*LTS-END*/ | ||||
| 
 | ||||
|  | @ -760,27 +783,22 @@ namespace Mist{ | |||
|     uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live); | ||||
| 
 | ||||
|     seekPoint = 0; | ||||
|     if (myMeta.live){ | ||||
|       //for live we use fragmented mode
 | ||||
|       fragSeqNum = 0; | ||||
|       partListSent = 0; | ||||
|       partListLength = 0; | ||||
|     } | ||||
|     fragSeqNum = 0; | ||||
|     byteStart = 0; | ||||
|     byteEnd = fileSize - 1; | ||||
|     char rangeType = ' '; | ||||
|     currPos = 0; | ||||
|     sortSet.clear(); | ||||
|     for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++){ | ||||
|       DTSC::Track & thisTrack = myMeta.tracks[*subIt]; | ||||
|       keyPart temp; | ||||
|       temp.trackID = *subIt; | ||||
|       temp.time = thisTrack.firstms;//timeplace of frame
 | ||||
|       temp.index = 0; | ||||
|       temp.size = thisTrack.parts[temp.index].getSize(); | ||||
|       sortSet.insert(temp); | ||||
|     } | ||||
|     if (!myMeta.live){ | ||||
|       for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++){ | ||||
|         DTSC::Track & thisTrack = myMeta.tracks[*subIt]; | ||||
|         keyPart temp; | ||||
|         temp.trackID = *subIt; | ||||
|         temp.time = thisTrack.firstms;//timeplace of frame
 | ||||
|         temp.index = 0; | ||||
|         temp.size = thisTrack.parts[temp.index].getSize(); | ||||
|         sortSet.insert(temp); | ||||
|       } | ||||
|       if (H.GetHeader("Range") != ""){ | ||||
|         if (parseRange(byteStart, byteEnd)){ | ||||
|           findSeekPoint(byteStart, seekPoint, headerSize); | ||||
|  | @ -904,9 +922,15 @@ namespace Mist{ | |||
|         currentPartSet[*it] = thisRange; | ||||
|       } | ||||
|     } | ||||
|     for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ | ||||
|       MEDIUM_MSG("Track %lu: %llu-%llu", *it, currentPartSet[*it].firstTime, currentPartSet[*it].lastTime); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void OutProgressiveMP4::sendNext(){ | ||||
|     if (keysOnly && thisPacket.getTrackId() == vidTrack && !thisPacket.getFlag("keyframe")){ | ||||
|       return;//skip non-keyframes
 | ||||
|     } | ||||
|     static bool perfect = true; | ||||
|      | ||||
|     //Obtain a pointer to the data of this packet
 | ||||
|  | @ -917,25 +941,20 @@ namespace Mist{ | |||
| 
 | ||||
|     if (myMeta.live){ | ||||
|       //if header needed
 | ||||
|       if (!partListLength || partListSent >= partListLength){ | ||||
|         if (fragSeqNum > 10){ | ||||
|       if (!sortSet.size()){ | ||||
|         if (fragSeqNum > 10 && !targetParams.count("start")){ | ||||
|           if (liveSeek()){return;} | ||||
|         } | ||||
|         //building set first
 | ||||
|         buildFragment();//map with metadata for keyframe
 | ||||
|         sendFragmentHeader(); | ||||
|         partListSent = 0; | ||||
|         partListLength = 0; | ||||
|         for (std::map<size_t, fragSet>::iterator it = currentPartSet.begin(); it != currentPartSet.end(); it++){ | ||||
|           partListLength += it->second.lastPart - it->second.firstPart + 1; | ||||
|         } | ||||
|       } | ||||
|       //generate content in mdat, meaning: send right parts
 | ||||
|       DONTEVEN_MSG("Sending tid: %ld size: %u", thisPacket.getTrackId() , len); | ||||
|       myConn.SendNow(dataPointer, len); | ||||
|       partListSent++; | ||||
|     } | ||||
| 
 | ||||
|     if (!sortSet.size()){ | ||||
|       FAIL_MSG("Sortset is empty!"); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
|     keyPart thisPart = *sortSet.begin(); | ||||
|     if ((unsigned long)thisPacket.getTrackId() != thisPart.trackID || thisPacket.getTime() != thisPart.time || len != thisPart.size){ | ||||
|  | @ -953,6 +972,10 @@ namespace Mist{ | |||
| 
 | ||||
|        //The remainder of this function handles non-live situations
 | ||||
|     if (myMeta.live){ | ||||
|       //generate content in mdat, meaning: send right parts
 | ||||
|       DONTEVEN_MSG("Sending %ld:%lld,  size: %u", thisPacket.getTrackId(), thisPacket.getTime(), len); | ||||
|       myConn.SendNow(dataPointer, len); | ||||
|       //delete from sortset
 | ||||
|       sortSet.erase(sortSet.begin()); | ||||
|       return; | ||||
|     } | ||||
|  |  | |||
|  | @ -60,13 +60,10 @@ namespace Mist { | |||
|       size_t vidTrack;//the video track we use as fragmenting base
 | ||||
|       uint64_t realBaseOffset;//base offset for every moof packet
 | ||||
|       //from sendnext
 | ||||
|       size_t partListSent;//parts of current fragSet sent
 | ||||
|       size_t partListLength;//amount of packets in current fragment
 | ||||
|       int64_t fragKeyNumberShift;//the difference between the first fragment Number and the first keyframe number
 | ||||
| 
 | ||||
|        | ||||
|       bool sending3GP; | ||||
|       bool chromeWorkaround; | ||||
|       int keysOnly; | ||||
|       uint64_t estimateFileSize(); | ||||
| 
 | ||||
|       //This is a dirty solution... but it prevents copying and copying and copying again
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma