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{
|
namespace Mist{
|
||||||
OutH264::OutH264(Socket::Connection &conn) : HTTPOutput(conn){
|
OutH264::OutH264(Socket::Connection &conn) : HTTPOutput(conn){
|
||||||
|
if (targetParams.count("keysonly")){
|
||||||
|
keysOnly = 1;
|
||||||
|
}
|
||||||
if (config->getString("target").size()){
|
if (config->getString("target").size()){
|
||||||
if (!streamName.size()){
|
if (!streamName.size()){
|
||||||
WARN_MSG("Recording unconnected H264 output to file! Cancelled.");
|
WARN_MSG("Recording unconnected H264 output to file! Cancelled.");
|
||||||
|
@ -45,6 +48,7 @@ namespace Mist{
|
||||||
bool OutH264::isRecording(){return config->getString("target").size();}
|
bool OutH264::isRecording(){return config->getString("target").size();}
|
||||||
|
|
||||||
void OutH264::sendNext(){
|
void OutH264::sendNext(){
|
||||||
|
if (keysOnly && !thisPacket.getFlag("keyframe")){return;}
|
||||||
char *dataPointer = 0;
|
char *dataPointer = 0;
|
||||||
unsigned int len = 0;
|
unsigned int len = 0;
|
||||||
thisPacket.getString("data", dataPointer, len);
|
thisPacket.getString("data", dataPointer, len);
|
||||||
|
@ -70,6 +74,8 @@ namespace Mist{
|
||||||
|
|
||||||
void OutH264::onHTTP(){
|
void OutH264::onHTTP(){
|
||||||
std::string method = H.method;
|
std::string method = H.method;
|
||||||
|
//Set mode to key frames only
|
||||||
|
keysOnly = (H.GetVar("keysonly") != "");
|
||||||
H.Clean();
|
H.Clean();
|
||||||
H.SetHeader("Content-Type", "video/H264");
|
H.SetHeader("Content-Type", "video/H264");
|
||||||
H.protocol = "HTTP/1.0";
|
H.protocol = "HTTP/1.0";
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace Mist{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isRecording();
|
bool isRecording();
|
||||||
|
bool keysOnly;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
namespace Mist{
|
namespace Mist{
|
||||||
OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){}
|
OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){
|
||||||
|
keysOnly = false;
|
||||||
|
}
|
||||||
OutProgressiveMP4::~OutProgressiveMP4(){}
|
OutProgressiveMP4::~OutProgressiveMP4(){}
|
||||||
|
|
||||||
void OutProgressiveMP4::init(Util::Config * cfg){
|
void OutProgressiveMP4::init(Util::Config * cfg){
|
||||||
|
@ -566,6 +568,22 @@ namespace Mist{
|
||||||
uint64_t timeStamp = it->second.firstTime;
|
uint64_t timeStamp = it->second.firstTime;
|
||||||
DTSC::Track & thisTrack = myMeta.tracks[it->first];
|
DTSC::Track & thisTrack = myMeta.tracks[it->first];
|
||||||
for (uint32_t i = it->second.firstPart; i <= it->second.lastPart; i++){
|
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;
|
keyPart temp;
|
||||||
temp.trackID = it->first;
|
temp.trackID = it->first;
|
||||||
temp.time = timeStamp;
|
temp.time = timeStamp;
|
||||||
|
@ -632,6 +650,9 @@ namespace Mist{
|
||||||
uint64_t partOffset = thisTrack.parts[trunIt->index].getOffset();
|
uint64_t partOffset = thisTrack.parts[trunIt->index].getOffset();
|
||||||
uint64_t partSize = thisTrack.parts[trunIt->index].getSize();
|
uint64_t partSize = thisTrack.parts[trunIt->index].getSize();
|
||||||
uint64_t partDur = thisTrack.parts[trunIt->index].getDuration();
|
uint64_t partDur = thisTrack.parts[trunIt->index].getDuration();
|
||||||
|
if (keysOnly){
|
||||||
|
partDur = keysOnly;
|
||||||
|
}
|
||||||
|
|
||||||
MP4::TRUN trunBox;
|
MP4::TRUN trunBox;
|
||||||
trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize | MP4::trunsampleDuration | (partOffset ? MP4::trunsampleOffsets : 0));
|
trunBox.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleSize | MP4::trunsampleDuration | (partOffset ? MP4::trunsampleOffsets : 0));
|
||||||
|
@ -744,6 +765,8 @@ namespace Mist{
|
||||||
realTime = 0;
|
realTime = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Set mode to key frames only for video tracks
|
||||||
|
keysOnly = atoi(H.GetVar("keysonly").c_str());
|
||||||
|
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
|
|
||||||
|
@ -760,27 +783,22 @@ namespace Mist{
|
||||||
uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live);
|
uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live);
|
||||||
|
|
||||||
seekPoint = 0;
|
seekPoint = 0;
|
||||||
if (myMeta.live){
|
fragSeqNum = 0;
|
||||||
//for live we use fragmented mode
|
|
||||||
fragSeqNum = 0;
|
|
||||||
partListSent = 0;
|
|
||||||
partListLength = 0;
|
|
||||||
}
|
|
||||||
byteStart = 0;
|
byteStart = 0;
|
||||||
byteEnd = fileSize - 1;
|
byteEnd = fileSize - 1;
|
||||||
char rangeType = ' ';
|
char rangeType = ' ';
|
||||||
currPos = 0;
|
currPos = 0;
|
||||||
sortSet.clear();
|
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){
|
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 (H.GetHeader("Range") != ""){
|
||||||
if (parseRange(byteStart, byteEnd)){
|
if (parseRange(byteStart, byteEnd)){
|
||||||
findSeekPoint(byteStart, seekPoint, headerSize);
|
findSeekPoint(byteStart, seekPoint, headerSize);
|
||||||
|
@ -904,9 +922,15 @@ namespace Mist{
|
||||||
currentPartSet[*it] = thisRange;
|
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(){
|
void OutProgressiveMP4::sendNext(){
|
||||||
|
if (keysOnly && thisPacket.getTrackId() == vidTrack && !thisPacket.getFlag("keyframe")){
|
||||||
|
return;//skip non-keyframes
|
||||||
|
}
|
||||||
static bool perfect = true;
|
static bool perfect = true;
|
||||||
|
|
||||||
//Obtain a pointer to the data of this packet
|
//Obtain a pointer to the data of this packet
|
||||||
|
@ -917,25 +941,20 @@ namespace Mist{
|
||||||
|
|
||||||
if (myMeta.live){
|
if (myMeta.live){
|
||||||
//if header needed
|
//if header needed
|
||||||
if (!partListLength || partListSent >= partListLength){
|
if (!sortSet.size()){
|
||||||
if (fragSeqNum > 10){
|
if (fragSeqNum > 10 && !targetParams.count("start")){
|
||||||
if (liveSeek()){return;}
|
if (liveSeek()){return;}
|
||||||
}
|
}
|
||||||
//building set first
|
//building set first
|
||||||
buildFragment();//map with metadata for keyframe
|
buildFragment();//map with metadata for keyframe
|
||||||
sendFragmentHeader();
|
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();
|
keyPart thisPart = *sortSet.begin();
|
||||||
if ((unsigned long)thisPacket.getTrackId() != thisPart.trackID || thisPacket.getTime() != thisPart.time || len != thisPart.size){
|
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
|
//The remainder of this function handles non-live situations
|
||||||
if (myMeta.live){
|
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());
|
sortSet.erase(sortSet.begin());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,13 +60,10 @@ namespace Mist {
|
||||||
size_t vidTrack;//the video track we use as fragmenting base
|
size_t vidTrack;//the video track we use as fragmenting base
|
||||||
uint64_t realBaseOffset;//base offset for every moof packet
|
uint64_t realBaseOffset;//base offset for every moof packet
|
||||||
//from sendnext
|
//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 sending3GP;
|
||||||
bool chromeWorkaround;
|
bool chromeWorkaround;
|
||||||
|
int keysOnly;
|
||||||
uint64_t estimateFileSize();
|
uint64_t estimateFileSize();
|
||||||
|
|
||||||
//This is a dirty solution... but it prevents copying and copying and copying again
|
//This is a dirty solution... but it prevents copying and copying and copying again
|
||||||
|
|
Loading…
Add table
Reference in a new issue