Backported various Pro MP4 updates/fixes to free version

This commit is contained in:
Thulinma 2016-11-23 12:51:24 +01:00
parent 7b96407e9b
commit 7df7c04aff
2 changed files with 256 additions and 237 deletions

View file

@ -1,8 +1,9 @@
#include "output_progressive_mp4.h"
#include <mist/defines.h> #include <mist/defines.h>
#include <mist/mp4.h> #include <mist/mp4.h>
#include <mist/mp4_generic.h> #include <mist/mp4_generic.h>
#include <mist/checksum.h> #include <mist/checksum.h>
#include <mist/bitfields.h>
#include "output_progressive_mp4.h"
namespace Mist { namespace Mist {
OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){} OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn){}
@ -33,214 +34,207 @@ namespace Mist {
return retVal * 1.1; return retVal * 1.1;
} }
///\todo This function does not indicate errors anywhere... maybe fix this...
std::string OutProgressiveMP4::DTSCMeta2MP4Header(long long & size){ std::string OutProgressiveMP4::DTSCMeta2MP4Header(long long & size){
std::stringstream header; std::stringstream header;
//ftyp box //Determines whether the outputfile is larger than 4GB, in which case we need to use 64-bit boxes for offsets
MP4::FTYP ftypBox; bool useLargeBoxes = (estimateFileSize() > 0xFFFFFFFFull);
header.write(ftypBox.asBox(),ftypBox.boxedSize()); //Keeps track of the total size of the mdat box
bool biggerThan4G = (estimateFileSize() > 0xFFFFFFFFull);
uint64_t mdatSize = 0; uint64_t mdatSize = 0;
//moov box
//Start actually creating the header
//MP4 Files always start with an FTYP box. Constructor sets default values
MP4::FTYP ftypBox;
header.write(ftypBox.asBox(), ftypBox.boxedSize());
//Start building the moov box. This is the metadata box for an mp4 file, and will contain all metadata.
MP4::MOOV moovBox; MP4::MOOV moovBox;
//Keep track of the current index within the moovBox
unsigned int moovOffset = 0; unsigned int moovOffset = 0;
{ //calculating longest duration
//calculating longest duration long long unsigned firstms = 0xFFFFFFFFFFFFFFull;
long long int firstms = -1; long long unsigned lastms = 0;
long long int lastms = -1; for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) {
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { lastms = std::max(lastms, myMeta.tracks[*it].lastms);
if (lastms == -1 || lastms < (long long)myMeta.tracks[*it].lastms){ firstms = std::min(firstms, myMeta.tracks[*it].firstms);
lastms = myMeta.tracks[*it].lastms;
}
if (firstms == -1 || firstms > (long long)myMeta.tracks[*it].firstms){
firstms = myMeta.tracks[*it].firstms;
}
}
MP4::MVHD mvhdBox(lastms - firstms);
moovBox.setContent(mvhdBox, moovOffset++);
} }
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { MP4::MVHD mvhdBox(lastms - firstms);
//Set the trackid for the first "empty" track within the file.
mvhdBox.setTrackID(selectedTracks.size() + 1);
moovBox.setContent(mvhdBox, moovOffset++);
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) {
DTSC::Track & thisTrack = myMeta.tracks[*it]; DTSC::Track & thisTrack = myMeta.tracks[*it];
MP4::TRAK trakBox; MP4::TRAK trakBox;
{ //Keep track of the current index within the moovBox
{ unsigned int trakOffset = 0;
MP4::TKHD tkhdBox(*it, thisTrack.lastms - thisTrack.firstms, thisTrack.width, thisTrack.height);
trakBox.setContent(tkhdBox, 0); MP4::TKHD tkhdBox(thisTrack, false);
}{ trakBox.setContent(tkhdBox, trakOffset++);
MP4::MDIA mdiaBox;
unsigned int mdiaOffset = 0; //Create an EDTS box, containing an ELST box with default values;
{ ///\todo Figure out if this box is really needed for anything.
MP4::MDHD mdhdBox(thisTrack.lastms - thisTrack.firstms); MP4::EDTS edtsBox;
mdhdBox.setLanguage(thisTrack.lang); MP4::ELST elstBox;
mdiaBox.setContent(mdhdBox, mdiaOffset++); elstBox.setVersion(0);
}//MDHD box elstBox.setFlags(0);
{ elstBox.setCount(1);
MP4::HDLR hdlrBox(thisTrack.type, thisTrack.getIdentifier()); elstBox.setSegmentDuration(thisTrack.lastms - thisTrack.firstms);
mdiaBox.setContent(hdlrBox, mdiaOffset++); elstBox.setMediaTime(0);
}//hdlr box elstBox.setMediaRateInteger(1);
{ elstBox.setMediaRateFraction(0);
MP4::MINF minfBox; edtsBox.setContent(elstBox, 0);
unsigned int minfOffset = 0; trakBox.setContent(edtsBox, trakOffset++);
if (thisTrack.type== "video"){
MP4::VMHD vmhdBox; MP4::MDIA mdiaBox;
vmhdBox.setFlags(1); unsigned int mdiaOffset = 0;
minfBox.setContent(vmhdBox,minfOffset++);
}else if (thisTrack.type == "audio"){ //Add the mandatory MDHD and HDLR boxes to the MDIA
MP4::SMHD smhdBox; MP4::MDHD mdhdBox(thisTrack.lastms - thisTrack.firstms);
minfBox.setContent(smhdBox,minfOffset++); mdhdBox.setLanguage(thisTrack.lang);
}//type box mdiaBox.setContent(mdhdBox, mdiaOffset++);
{ MP4::HDLR hdlrBox(thisTrack.type, thisTrack.getIdentifier());
MP4::DINF dinfBox; mdiaBox.setContent(hdlrBox, mdiaOffset++);
MP4::DREF drefBox;
dinfBox.setContent(drefBox,0); MP4::MINF minfBox;
minfBox.setContent(dinfBox,minfOffset++); unsigned int minfOffset = 0;
}//dinf box
{ //Add a track-type specific box to the MINF box
MP4::STBL stblBox; if (thisTrack.type == "video") {
unsigned int offset = 0; MP4::VMHD vmhdBox;
{ vmhdBox.setFlags(1);
MP4::STSD stsdBox; minfBox.setContent(vmhdBox, minfOffset++);
stsdBox.setVersion(0); } else if (thisTrack.type == "audio") {
if (thisTrack.type == "video"){//boxname = codec MP4::SMHD smhdBox;
MP4::VisualSampleEntry vse; minfBox.setContent(smhdBox, minfOffset++);
if (thisTrack.codec == "H264"){ }
vse.setCodec("avc1");
} //Add the mandatory DREF (dataReference) box
if (thisTrack.codec == "HEVC"){ MP4::DINF dinfBox;
vse.setCodec("hev1"); MP4::DREF drefBox;
} dinfBox.setContent(drefBox, 0);
vse.setDataReferenceIndex(1); minfBox.setContent(dinfBox, minfOffset++);
vse.setWidth(thisTrack.width);
vse.setHeight(thisTrack.height);
if (thisTrack.codec == "H264"){
MP4::AVCC avccBox; MP4::STBL stblBox;
avccBox.setPayload(thisTrack.init); unsigned int stblOffset = 0;
vse.setCLAP(avccBox);
} //Add STSD box
stsdBox.setEntry(vse,0); MP4::STSD stsdBox(0);
}else if(thisTrack.type == "audio"){//boxname = codec if (thisTrack.type == "video") {
MP4::AudioSampleEntry ase; MP4::VisualSampleEntry sampleEntry(thisTrack);
if (thisTrack.codec == "AAC"){ stsdBox.setEntry(sampleEntry, 0);
ase.setCodec("mp4a"); } else if (thisTrack.type == "audio") {
ase.setDataReferenceIndex(1); MP4::AudioSampleEntry sampleEntry(thisTrack);
}else if (thisTrack.codec == "MP3"){ stsdBox.setEntry(sampleEntry, 0);
ase.setCodec("mp4a"); }
ase.setDataReferenceIndex(1); stblBox.setContent(stsdBox, stblOffset++);
}
ase.setSampleRate(thisTrack.rate); //Add STTS Box
ase.setChannelCount(thisTrack.channels); MP4::STTS sttsBox(0);
ase.setSampleSize(thisTrack.size); std::deque<std::pair<int, int> > sttsCounter;
MP4::ESDS esdsBox(thisTrack.init); for (unsigned int part = 0; part < thisTrack.parts.size(); ++part) {
ase.setCodecBox(esdsBox); //Create a new entry with current duration if EITHER there is no entry yet, or this parts duration differs from the previous
stsdBox.setEntry(ase,0); if (!sttsCounter.size() || sttsCounter.rbegin()->second != thisTrack.parts[part].getDuration()){
} //Set the counter to 0, so we don't have to handle this situation diffent when updating
stblBox.setContent(stsdBox,offset++); sttsCounter.push_back(std::pair<int,int>(0, thisTrack.parts[part].getDuration()));
}//stsd box
{
MP4::STTS sttsBox;
sttsBox.setVersion(0);
if (thisTrack.parts.size()){
for (unsigned int part = thisTrack.parts.size(); part > 0; --part){
MP4::STTSEntry newEntry;
newEntry.sampleCount = 1;
newEntry.sampleDelta = thisTrack.parts[part-1].getDuration();
sttsBox.setSTTSEntry(newEntry, part-1);
}
}
stblBox.setContent(sttsBox,offset++);
}//stts box
if (thisTrack.type == "video"){
//STSS Box here
MP4::STSS stssBox;
stssBox.setVersion(0);
int tmpCount = 0;
int tmpItCount = 0;
for ( std::deque< DTSC::Key>::iterator tmpIt = thisTrack.keys.begin(); tmpIt != thisTrack.keys.end(); tmpIt ++) {
stssBox.setSampleNumber(tmpCount,tmpItCount);
tmpCount += tmpIt->getParts();
tmpItCount ++;
}
stblBox.setContent(stssBox,offset++);
}//stss box
{
MP4::STSC stscBox;
stscBox.setVersion(0);
MP4::STSCEntry stscEntry;
stscEntry.firstChunk = 1;
stscEntry.samplesPerChunk = 1;
stscEntry.sampleDescriptionIndex = 1;
stscBox.setSTSCEntry(stscEntry, 0);
stblBox.setContent(stscBox,offset++);
}//stsc box
{
bool makeCTTS = false;
MP4::STSZ stszBox;
stszBox.setVersion(0);
if (thisTrack.parts.size()){
std::deque<DTSC::Part>::reverse_iterator tmpIt = thisTrack.parts.rbegin();
for (unsigned int part = thisTrack.parts.size(); part > 0; --part){
unsigned int partSize = tmpIt->getSize();
stszBox.setEntrySize(partSize, part-1);//in bytes in file
size += partSize;
makeCTTS |= tmpIt->getOffset();
tmpIt++;
}
}
if (makeCTTS){
MP4::CTTS cttsBox;
cttsBox.setVersion(0);
if (thisTrack.parts.size()){
std::deque<DTSC::Part>::iterator tmpIt = thisTrack.parts.begin();
MP4::CTTSEntry tmpEntry;
tmpEntry.sampleCount = 1;
tmpEntry.sampleOffset = tmpIt->getOffset();
unsigned int totalEntries = 0;
tmpIt++;
while (tmpIt != thisTrack.parts.end()){
unsigned int timeOffset = tmpIt->getOffset();
if (timeOffset == tmpEntry.sampleOffset){
tmpEntry.sampleCount++;
}else{
cttsBox.setCTTSEntry(tmpEntry, totalEntries++);
tmpEntry.sampleCount = 1;
tmpEntry.sampleOffset = timeOffset;
}
tmpIt++;
}
cttsBox.setCTTSEntry(tmpEntry, totalEntries++);
//cttsBox.setEntryCount(totalEntries);
}
stblBox.setContent(cttsBox,offset++);
}//ctts
stblBox.setContent(stszBox,offset++);
}//stsz box
{
if (biggerThan4G){
MP4::CO64 CO64Box;
//Inserting empty values on purpose here, will be fixed later.
if (thisTrack.parts.size() != 0){
CO64Box.setChunkOffset(0, thisTrack.parts.size() - 1);//this inserts all empty entries at once
}
stblBox.setContent(CO64Box,offset++);
}else{
MP4::STCO stcoBox;
//Inserting empty values on purpose here, will be fixed later.
if (thisTrack.parts.size() != 0){
stcoBox.setChunkOffset(0, thisTrack.parts.size() - 1);//this inserts all empty entries at once
}
stblBox.setContent(stcoBox,offset++);
}
}//stco box
minfBox.setContent(stblBox,minfOffset++);
}//stbl box
mdiaBox.setContent(minfBox, mdiaOffset++);
}//minf box
trakBox.setContent(mdiaBox, 1);
} }
}//trak Box //Then update the counter
sttsCounter.rbegin()->first++;
}
//Write all entries in reverse
for (unsigned int entry = sttsCounter.size(); entry > 0; --entry){
MP4::STTSEntry newEntry;
newEntry.sampleCount = sttsCounter[entry - 1].first;;
newEntry.sampleDelta = sttsCounter[entry - 1].second;
sttsBox.setSTTSEntry(newEntry, entry - 1);///\todo rewrite for sanity
}
stblBox.setContent(sttsBox, stblOffset++);
//Add STSS Box IF type is video and we are not fragmented
if (thisTrack.type == "video") {
MP4::STSS stssBox(0);
int tmpCount = 0;
for (int i = 0; i < thisTrack.keys.size(); i++){
stssBox.setSampleNumber(tmpCount + 1, i);///\todo PLEASE rewrite this for sanity.... SHOULD be: index FIRST, value SECOND
tmpCount += thisTrack.keys[i].getParts();
}
stblBox.setContent(stssBox, stblOffset++);
}
//Add STSC Box
MP4::STSC stscBox(0);
MP4::STSCEntry stscEntry(1,1,1);
stscBox.setSTSCEntry(stscEntry, 0);
stblBox.setContent(stscBox, stblOffset++);
bool containsOffsets = false;
//Add STSZ Box
MP4::STSZ stszBox(0);
if (thisTrack.parts.size()) {
std::deque<DTSC::Part>::reverse_iterator tmpIt = thisTrack.parts.rbegin();
for (unsigned int part = thisTrack.parts.size(); part > 0; --part) {
///\todo rewrite for sanity
stszBox.setEntrySize(tmpIt->getSize(), part - 1); //in bytes in file
size += tmpIt->getSize();
containsOffsets |= tmpIt->getOffset();
tmpIt++;
}
}
stblBox.setContent(stszBox, stblOffset++);
//Add CTTS Box only if the track contains time offsets
if (containsOffsets) {
MP4::CTTS cttsBox;
cttsBox.setVersion(0);
MP4::CTTSEntry tmpEntry;
tmpEntry.sampleCount = 0;
tmpEntry.sampleOffset = thisTrack.parts[0].getOffset();
unsigned int totalEntries = 0;
for (std::deque<DTSC::Part>::iterator tmpIt = thisTrack.parts.begin(); tmpIt != thisTrack.parts.end(); tmpIt++){
if (tmpIt->getOffset() != tmpEntry.sampleOffset) {
//If the offset of this and previous part differ, write current values and reset
cttsBox.setCTTSEntry(tmpEntry, totalEntries++);///\todo Again, rewrite for sanity. index FIRST, value SECOND
tmpEntry.sampleCount = 0;
tmpEntry.sampleOffset = tmpIt->getOffset();
}
tmpEntry.sampleCount++;
}
//set the last entry
cttsBox.setCTTSEntry(tmpEntry, totalEntries++);
stblBox.setContent(cttsBox, stblOffset++);
}
//Create STCO Box (either stco or co64)
//note: Inserting empty values on purpose here, will be fixed later.
if (useLargeBoxes) {
MP4::CO64 CO64Box;
CO64Box.setChunkOffset(0, thisTrack.parts.size() - 1);
stblBox.setContent(CO64Box, stblOffset++);
} else {
MP4::STCO stcoBox(0);
stcoBox.setChunkOffset(0, thisTrack.parts.size() - 1);
stblBox.setContent(stcoBox, stblOffset++);
}
minfBox.setContent(stblBox, minfOffset++);
mdiaBox.setContent(minfBox, mdiaOffset++);
trakBox.setContent(mdiaBox, 2);
moovBox.setContent(trakBox, moovOffset++); moovBox.setContent(trakBox, moovOffset++);
}//for each selected track }
//initial offset length ftyp, length moov + 8 //initial offset length ftyp, length moov + 8
unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; unsigned long long int dataOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8;
//update all STCO or CO64 from the following maps; //update all STCO or CO64 from the following maps;
std::map <long unsigned, MP4::STCO> checkStcoBoxes; std::map <long unsigned, MP4::STCO> checkStcoBoxes;
std::map <long unsigned, MP4::CO64> checkCO64Boxes; std::map <long unsigned, MP4::CO64> checkCO64Boxes;
@ -288,7 +282,8 @@ namespace Mist {
} }
//inserting right values in the STCO box header //inserting right values in the STCO box header
//total = 0; //total = 0;
long long unsigned int totalByteOffset = 0; //Keep track of the current size of the data within the mdat
long long unsigned int dataSize = 0;
//Current values are actual byte offset without header-sized offset //Current values are actual byte offset without header-sized offset
std::set <keyPart> sortSet;//filling sortset for interleaving parts std::set <keyPart> sortSet;//filling sortset for interleaving parts
for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++) { for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++) {
@ -298,38 +293,41 @@ namespace Mist {
temp.endTime = myMeta.tracks[*subIt].firstms + myMeta.tracks[*subIt].parts[0].getDuration(); temp.endTime = myMeta.tracks[*subIt].firstms + myMeta.tracks[*subIt].parts[0].getDuration();
temp.size = myMeta.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) temp.size = myMeta.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together)
temp.index = 0; temp.index = 0;
INFO_MSG("adding to sortSet: tid %lu time %llu", temp.trackID, temp.time);
sortSet.insert(temp); sortSet.insert(temp);
} }
while (!sortSet.empty()){ while (!sortSet.empty()){
std::set<keyPart>::iterator keyBegin = sortSet.begin(); std::set<keyPart>::iterator keyBegin = sortSet.begin();
//setting the right STCO size in the STCO box //setting the right STCO size in the STCO box
if (checkCO64Boxes.count(keyBegin->trackID)){ if (useLargeBoxes){//Re-using the previously defined boolean for speedup
checkCO64Boxes[keyBegin->trackID].setChunkOffset(totalByteOffset + byteOffset, keyBegin->index); checkCO64Boxes[keyBegin->trackID].setChunkOffset(dataOffset + dataSize, keyBegin->index);
}else{ } else {
checkStcoBoxes[keyBegin->trackID].setChunkOffset(totalByteOffset + byteOffset, keyBegin->index); checkStcoBoxes[keyBegin->trackID].setChunkOffset(dataOffset + dataSize, keyBegin->index);
} }
totalByteOffset += keyBegin->size; dataSize += keyBegin->size;
//add keyPart to sortSet
keyPart temp; //add next keyPart to sortSet
temp.index = keyBegin->index + 1; DTSC::Track & thisTrack = myMeta.tracks[keyBegin->trackID];
temp.trackID = keyBegin->trackID; if (keyBegin->index < thisTrack.parts.size() - 1) {//Only create new element, when there are new elements to be added
DTSC::Track & thisTrack = myMeta.tracks[temp.trackID]; keyPart temp = *keyBegin;
if(temp.index < thisTrack.parts.size() ){//only insert when there are parts left temp.index ++;
temp.time = keyBegin->endTime;//timeplace of frame temp.time = temp.endTime;
temp.endTime = keyBegin->endTime + thisTrack.parts[temp.index].getDuration(); temp.endTime += thisTrack.parts[temp.index].getDuration();
temp.size = thisTrack.parts[temp.index].getSize();//bytesize of frame temp.size = thisTrack.parts[temp.index].getSize();//bytesize of frame
sortSet.insert(temp); sortSet.insert(temp);
} }
//remove highest keyPart //remove highest keyPart
sortSet.erase(keyBegin); sortSet.erase(keyBegin);
} }
mdatSize = totalByteOffset+8; ///\todo Update this thing for boxes >4G?
mdatSize = dataSize + 8;//+8 for mp4 header
header.write(moovBox.asBox(),moovBox.boxedSize()); header << std::string(moovBox.asBox(), moovBox.boxedSize());
header << (char)((mdatSize>>24) & 0xFF) << (char)((mdatSize>>16) & 0xFF) << (char)((mdatSize>>8) & 0xFF) << (char)(mdatSize & 0xFF) << "mdat"; char mdatHeader[8] = {0x00,0x00,0x00,0x00,'m','d','a','t'};
//end of header Bit::htobl(mdatHeader, mdatSize);
header.write(mdatHeader, 8);
size += header.str().size(); size += header.str().size();
return header.str(); return header.str();
@ -340,7 +338,9 @@ namespace Mist {
void OutProgressiveMP4::findSeekPoint(long long byteStart, long long & seekPoint, unsigned int headerSize){ void OutProgressiveMP4::findSeekPoint(long long byteStart, long long & seekPoint, unsigned int headerSize){
seekPoint = 0; seekPoint = 0;
//if we're starting in the header, seekPoint is always zero. //if we're starting in the header, seekPoint is always zero.
if (byteStart <= headerSize){return;} if (byteStart <= headerSize) {
return;
}
//okay, we're past the header. Substract the headersize from the starting postion. //okay, we're past the header. Substract the headersize from the starting postion.
byteStart -= headerSize; byteStart -= headerSize;
//forward through the file by headers, until we reach the point where we need to be //forward through the file by headers, until we reach the point where we need to be
@ -350,7 +350,10 @@ namespace Mist {
//substract the size of this fragment from byteStart //substract the size of this fragment from byteStart
byteStart -= sortSet.begin()->size; byteStart -= sortSet.begin()->size;
//if that put us past the point where we wanted to be, return right now //if that put us past the point where we wanted to be, return right now
if (byteStart < 0){return;} if (byteStart < 0) {
INFO_MSG("We're starting at time %lld, skipping %lld bytes", seekPoint, byteStart+sortSet.begin()->size);
return;
}
//otherwise, set currPos to where we are now and continue //otherwise, set currPos to where we are now and continue
currPos += sortSet.begin()->size; currPos += sortSet.begin()->size;
//find the next part //find the next part
@ -431,7 +434,9 @@ namespace Mist {
} }
break; break;
} }
if (byteEnd > size - 1){byteEnd = size - 1;} if (byteEnd > size - 1) {
byteEnd = size - 1;
}
}else{ }else{
byteEnd = size; byteEnd = size;
} }
@ -450,15 +455,22 @@ namespace Mist {
return; return;
} }
//Always initialize before anything else
initialize(); initialize();
//Make sure we start receiving data after this function
///\todo Should this happen here?
parseData = true; parseData = true;
wantRequest = false; wantRequest = false;
sentHeader = false; sentHeader = false;
fileSize = 0;
//For storing the header.
///\todo Do we really need this though?
std::string headerData = DTSCMeta2MP4Header(fileSize); std::string headerData = DTSCMeta2MP4Header(fileSize);
seekPoint = 0;
byteStart = 0; byteStart = 0;
byteEnd = fileSize - 1; byteEnd = fileSize - 1;
seekPoint = 0;
char rangeType = ' '; char rangeType = ' ';
currPos = 0; currPos = 0;
sortSet.clear(); sortSet.clear();
@ -514,7 +526,6 @@ namespace Mist {
leftOver = byteEnd - byteStart + 1;//add one byte, because range "0-0" = 1 byte of data leftOver = byteEnd - byteStart + 1;//add one byte, because range "0-0" = 1 byte of data
if (byteStart < (long long)headerData.size()){ if (byteStart < (long long)headerData.size()){
/// \todo Switch to chunked? /// \todo Switch to chunked?
//H.Chunkify(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart, conn);//send MP4 header
myConn.SendNow(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart);//send MP4 header myConn.SendNow(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart);//send MP4 header
leftOver -= std::min((long long)headerData.size(), byteEnd) - byteStart; leftOver -= std::min((long long)headerData.size(), byteEnd) - byteStart;
} }
@ -523,11 +534,13 @@ namespace Mist {
void OutProgressiveMP4::sendNext(){ void OutProgressiveMP4::sendNext(){
static bool perfect = true; static bool perfect = true;
//Obtain a pointer to the data of this packet
char * dataPointer = 0; char * dataPointer = 0;
unsigned int len = 0; unsigned int len = 0;
thisPacket.getString("data", dataPointer, len); thisPacket.getString("data", dataPointer, len);
if ((unsigned long)thisPacket.getTrackId() != sortSet.begin()->trackID || thisPacket.getTime() != sortSet.begin()->time){ if ((unsigned long)thisPacket.getTrackId() != sortSet.begin()->trackID || thisPacket.getTime() != sortSet.begin()->time){
if (thisPacket.getTime() >= sortSet.begin()->time || (unsigned long)thisPacket.getTrackId() >= sortSet.begin()->trackID){ if (thisPacket.getTime() > sortSet.begin()->time || (unsigned long)thisPacket.getTrackId() > sortSet.begin()->trackID) {
if (perfect){ if (perfect){
DEBUG_MSG(DLVL_WARN, "Warning: input is inconsistent. Expected %lu:%llu but got %ld:%llu - cancelling playback", sortSet.begin()->trackID, sortSet.begin()->time, thisPacket.getTrackId(), thisPacket.getTime()); DEBUG_MSG(DLVL_WARN, "Warning: input is inconsistent. Expected %lu:%llu but got %ld:%llu - cancelling playback", sortSet.begin()->trackID, sortSet.begin()->time, thisPacket.getTrackId(), thisPacket.getTime());
perfect = false; perfect = false;
@ -538,6 +551,17 @@ namespace Mist {
} }
return; return;
} }
if (currPos >= byteStart) {
myConn.SendNow(dataPointer, std::min(leftOver, (long long)len));
leftOver -= len;
} else {
if (currPos + (long long)len > byteStart) {
myConn.SendNow(dataPointer + (byteStart - currPos), std::min(leftOver, (long long)(len - (byteStart - currPos))));
leftOver -= len - (byteStart - currPos);
}
}
//keep track of where we are //keep track of where we are
if (!sortSet.empty()){ if (!sortSet.empty()){
keyPart temp; keyPart temp;
@ -555,19 +579,9 @@ namespace Mist {
} }
if (currPos >= byteStart){
myConn.SendNow(dataPointer, std::min(leftOver, (long long)len));
//H.Chunkify(Strm.lastData().data(), Strm.lastData().size(), conn);
leftOver -= len;
}else{
if (currPos + (long long)len > byteStart){
myConn.SendNow(dataPointer+(byteStart-currPos), len-(byteStart-currPos));
leftOver -= len-(byteStart-currPos);
currPos = byteStart;
}
}
//sortSet.clear();//we don't need you anymore!
if (leftOver < 1){ if (leftOver < 1){
//stop playback, wait for new request //stop playback, wait for new request
stop(); stop();
wantRequest = true; wantRequest = true;

View file

@ -12,6 +12,9 @@ namespace Mist {
if (trackID < rhs.trackID){ if (trackID < rhs.trackID){
return true; return true;
} }
if (trackID == rhs.trackID){
return index < rhs.index;
}
} }
return false; return false;
} }
@ -40,8 +43,10 @@ namespace Mist {
long long leftOver; long long leftOver;
long long currPos; long long currPos;
long long seekPoint; long long seekPoint;
std::set <keyPart> sortSet;//filling sortset for interleaving parts
//variables for standard MP4
std::set <keyPart> sortSet;//needed for unfragmented MP4, remembers the order of keyparts
long long unsigned estimateFileSize(); long long unsigned estimateFileSize();
}; };
} }