Added H264/MP4 keysonly mode, MP4 live simplification

This commit is contained in:
Thulinma 2017-12-25 01:48:54 +01:00
parent 6a5549444b
commit 2bbbf51fb3
4 changed files with 58 additions and 31 deletions

View file

@ -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";

View file

@ -11,6 +11,7 @@ namespace Mist{
private:
bool isRecording();
bool keysOnly;
};
}

View file

@ -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;
}

View file

@ -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