setstreamVodField and streamLiveField no longer mutually exclusive
Removed curPage map from IO. bufferFrame now creates this variable locally and passes it to bufferStart, bufferFinalize and bufferNext Fix keyNum selection with mixed live & VoD data Fix bufferframe to handle mixed VoD and live Added check to bufferFrame to not start the countdown timer for removing live pages Fixed countdown timer being set using keyNum rather than pageNumber, which resulted in the wrong pages being deleted livePage variable moved from static to private variable to correctly handle multithreaded inputs # Conflicts: # src/io.cpp # src/output/output.cpp
This commit is contained in:
parent
d1358400f7
commit
3d9ed39396
13 changed files with 241 additions and 150 deletions
|
@ -108,6 +108,8 @@ namespace Mist{
|
|||
firstData = true;
|
||||
newUA = true;
|
||||
lastPushUpdate = 0;
|
||||
previousFile = "";
|
||||
currentFile = "";
|
||||
|
||||
lastRecv = Util::bootSecs();
|
||||
if (myConn){
|
||||
|
@ -418,9 +420,9 @@ namespace Mist{
|
|||
statComm.reload();
|
||||
stats(true);
|
||||
if (isPushing()){return;}
|
||||
if (!isRecording() && !M.getVod() && !isReadyForPlay()){
|
||||
if (!isRecording() && M.getLive() && !isReadyForPlay()){
|
||||
uint64_t waitUntil = Util::bootSecs() + 45;
|
||||
while (!M.getVod() && !isReadyForPlay()){
|
||||
while (M.getLive() && !isReadyForPlay()){
|
||||
if (Util::bootSecs() > waitUntil || (!userSelect.size() && Util::bootSecs() > waitUntil)){
|
||||
INFO_MSG("Giving up waiting for playable tracks. IP: %s", getConnectedHost().c_str());
|
||||
break;
|
||||
|
@ -537,6 +539,7 @@ namespace Mist{
|
|||
if (!keys.getValidCount()){return 0;}
|
||||
//Get the key for the current time
|
||||
size_t keyNum = M.getKeyNumForTime(trk, lastPacketTime);
|
||||
if (keyNum == INVALID_KEY_NUM){return 0;}
|
||||
if (keys.getEndValid() <= keyNum+1){return 0;}
|
||||
//Return the next key
|
||||
return keys.getTime(keyNum+1);
|
||||
|
@ -546,7 +549,7 @@ namespace Mist{
|
|||
const Util::RelAccX &tPages = M.pages(trackId);
|
||||
for (uint64_t i = tPages.getDeleted(); i < tPages.getEndPos(); i++){
|
||||
uint64_t pageNum = tPages.getInt("firstkey", i);
|
||||
if (pageNum > keyNum) break;
|
||||
if (pageNum > keyNum) continue;
|
||||
uint64_t pageKeys = tPages.getInt("keycount", i);
|
||||
if (keyNum > pageNum + pageKeys - 1) continue;
|
||||
uint64_t pageAvail = tPages.getInt("avail", i);
|
||||
|
@ -581,7 +584,7 @@ namespace Mist{
|
|||
return;
|
||||
}
|
||||
size_t lastAvailKey = keys.getEndValid() - 1;
|
||||
if (meta.getVod() && keyNum > lastAvailKey){
|
||||
if (!meta.getLive() && keyNum > lastAvailKey){
|
||||
INFO_MSG("Load for track %zu key %zu aborted, is > %zu", trackId, keyNum, lastAvailKey);
|
||||
curPage.erase(trackId);
|
||||
currentPage.erase(trackId);
|
||||
|
@ -729,6 +732,10 @@ namespace Mist{
|
|||
if (M.getType(mainTrack) == "video"){
|
||||
DTSC::Keys keys(M.keys(mainTrack));
|
||||
uint32_t keyNum = M.getKeyNumForTime(mainTrack, pos);
|
||||
if (keyNum == INVALID_KEY_NUM){
|
||||
FAIL_MSG("Attempted seek on empty track %zu", mainTrack);
|
||||
return false;
|
||||
}
|
||||
pos = keys.getTime(keyNum);
|
||||
}
|
||||
}
|
||||
|
@ -774,13 +781,15 @@ namespace Mist{
|
|||
}
|
||||
DTSC::Keys keys(M.keys(tid));
|
||||
uint32_t keyNum = M.getKeyNumForTime(tid, pos);
|
||||
if (keyNum == INVALID_KEY_NUM){
|
||||
FAIL_MSG("Attempted seek on empty track %zu", tid);
|
||||
return false;
|
||||
}
|
||||
uint64_t actualKeyTime = keys.getTime(keyNum);
|
||||
HIGH_MSG("Seeking to track %zu key %" PRIu32 " => time %" PRIu64, tid, keyNum, pos);
|
||||
if (actualKeyTime > pos){
|
||||
if (M.getLive()){
|
||||
pos = actualKeyTime;
|
||||
userSelect[tid].setKeyNum(keyNum);
|
||||
}
|
||||
pos = actualKeyTime;
|
||||
userSelect[tid].setKeyNum(keyNum);
|
||||
}
|
||||
loadPageForKey(tid, keyNum + (getNextKey ? 1 : 0));
|
||||
if (!curPage.count(tid) || !curPage[tid].mapped){
|
||||
|
@ -818,7 +827,7 @@ namespace Mist{
|
|||
VERYHIGH_MSG("Track %zu no data (key %" PRIu32 " @ %" PRIu64 ") - waiting...", tid,
|
||||
keyNum + (getNextKey ? 1 : 0), tmp.offset);
|
||||
uint32_t i = 0;
|
||||
while (!meta.getLive() && curPage[tid].mapped[tmp.offset] == 0 && ++i <= 10){
|
||||
while (meta.getVod() && curPage[tid].mapped[tmp.offset] == 0 && ++i <= 10){
|
||||
Util::wait(100 * i);
|
||||
stats();
|
||||
}
|
||||
|
@ -917,7 +926,7 @@ namespace Mist{
|
|||
if (targetParams.count("recstart") && atoll(targetParams["recstart"].c_str()) != 0){
|
||||
uint64_t startRec = atoll(targetParams["recstart"].c_str());
|
||||
if (startRec > endTime()){
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
onFail("Recording start past end of non-live source", true);
|
||||
return;
|
||||
}
|
||||
|
@ -1028,7 +1037,7 @@ namespace Mist{
|
|||
size_t mainTrack = getMainSelectedTrack();
|
||||
int64_t startRec = atoll(targetParams["start"].c_str());
|
||||
if (startRec > M.getLastms(mainTrack)){
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
onFail("Playback start past end of non-live source", true);
|
||||
return;
|
||||
}
|
||||
|
@ -1360,6 +1369,9 @@ namespace Mist{
|
|||
if (newTarget.rfind('?') != std::string::npos){
|
||||
newTarget.erase(newTarget.rfind('?'));
|
||||
}
|
||||
// Keep track of filenames written, so that they can be added to the playlist file
|
||||
previousFile = currentFile;
|
||||
currentFile = newTarget;
|
||||
INFO_MSG("Switching to next push target filename: %s", newTarget.c_str());
|
||||
if (!connectToFile(newTarget)){
|
||||
FAIL_MSG("Failed to open file, aborting: %s", newTarget.c_str());
|
||||
|
@ -1486,11 +1498,16 @@ namespace Mist{
|
|||
// now, seek to the exact timestamp of the keyframe
|
||||
DTSC::Keys keys(M.keys(mainTrack));
|
||||
uint32_t targetKey = M.getKeyNumForTime(mainTrack, currTime);
|
||||
seek(keys.getTime(targetKey));
|
||||
// attempt to load the key into thisPacket
|
||||
bool ret = prepareNext();
|
||||
if (!ret){
|
||||
WARN_MSG("Failed to load keyframe for %" PRIu64 "ms - continuing without it", currTime);
|
||||
bool ret = false;
|
||||
if (targetKey == INVALID_KEY_NUM){
|
||||
FAIL_MSG("No keyframes available on track %zu", mainTrack);
|
||||
}else{
|
||||
seek(keys.getTime(targetKey));
|
||||
// attempt to load the key into thisPacket
|
||||
ret = prepareNext();
|
||||
if (!ret){
|
||||
WARN_MSG("Failed to load keyframe for %" PRIu64 "ms - continuing without it", currTime);
|
||||
}
|
||||
}
|
||||
|
||||
// restore state to before the seek/load
|
||||
|
@ -1547,7 +1564,39 @@ namespace Mist{
|
|||
return false;
|
||||
}
|
||||
|
||||
Util::sortedPageInfo nxt;
|
||||
Util::sortedPageInfo nxt = *(buffer.begin());
|
||||
|
||||
if (meta.reloadReplacedPagesIfNeeded()){return false;}
|
||||
if (!M.getValidTracks().count(nxt.tid)){
|
||||
dropTrack(nxt.tid, "disappeared from metadata");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're going to read past the end of the data page, load the next page
|
||||
// this only happens for VoD
|
||||
if (nxt.offset >= curPage[nxt.tid].len ||
|
||||
(!memcmp(curPage[nxt.tid].mapped + nxt.offset, "\000\000\000\000", 4))){
|
||||
if (!M.getLive() && nxt.time >= M.getLastms(nxt.tid)){
|
||||
dropTrack(nxt.tid, "end of non-live track reached", false);
|
||||
return false;
|
||||
}
|
||||
if (M.getPageNumberForTime(nxt.tid, nxt.time) != currentPage[nxt.tid]){
|
||||
loadPageForKey(nxt.tid, M.getPageNumberForTime(nxt.tid, nxt.time));
|
||||
nxt.offset = 0;
|
||||
//Only read the next time if the page load succeeded and there is a packet to read from
|
||||
if (curPage[nxt.tid].mapped && curPage[nxt.tid].mapped[0] == 'D'){
|
||||
nxt.time = getDTSCTime(curPage[nxt.tid].mapped, 0);
|
||||
}
|
||||
buffer.replaceFirst(nxt);
|
||||
return false;
|
||||
}
|
||||
dropTrack(nxt.tid, "VoD page load failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We know this packet will be valid, pre-load it so we know its length
|
||||
DTSC::Packet preLoad(curPage[nxt.tid].mapped + nxt.offset, 0, true);
|
||||
|
||||
uint64_t nextTime = 0;
|
||||
//In case we're not in sync mode, we might have to retry a few times
|
||||
for (size_t trackTries = 0; trackTries < buffer.size(); ++trackTries){
|
||||
|
@ -1582,7 +1631,6 @@ namespace Mist{
|
|||
dropTrack(nxt.tid, "VoD page load failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We know this packet will be valid, pre-load it so we know its length
|
||||
DTSC::Packet preLoad(curPage[nxt.tid].mapped + nxt.offset, 0, true);
|
||||
|
||||
|
@ -1605,10 +1653,10 @@ namespace Mist{
|
|||
}else{
|
||||
//no next packet yet!
|
||||
//Check if this is the last packet of a VoD stream. Return success and drop the track.
|
||||
if (M.getVod() && nxt.time >= M.getLastms(nxt.tid)){
|
||||
if (!M.getLive() && nxt.time >= M.getLastms(nxt.tid)){
|
||||
thisPacket.reInit(curPage[nxt.tid].mapped + nxt.offset, 0, true);
|
||||
thisIdx = nxt.tid;
|
||||
dropTrack(nxt.tid, "end of VoD track reached", false);
|
||||
dropTrack(nxt.tid, "end of non-live track reached", false);
|
||||
return true;
|
||||
}
|
||||
uint32_t thisKey = M.getKeyNumForTime(nxt.tid, nxt.time);
|
||||
|
|
|
@ -31,6 +31,8 @@ namespace Mist{
|
|||
/*LTS-START*/
|
||||
std::string reqUrl;
|
||||
/*LTS-END*/
|
||||
std::string previousFile;
|
||||
std::string currentFile;
|
||||
// non-virtual generic functions
|
||||
virtual int run();
|
||||
virtual void stats(bool force = false);
|
||||
|
@ -155,6 +157,8 @@ namespace Mist{
|
|||
|
||||
size_t thisIdx;
|
||||
uint64_t thisTime;
|
||||
|
||||
std::map<size_t, IPC::sharedPage> curPage; ///< For each track, holds the page that is currently being written.
|
||||
};
|
||||
|
||||
}// namespace Mist
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Mist{
|
|||
if (config->getString("target").size()){
|
||||
if (config->getString("target").find(".webm") != std::string::npos){doctype = "webm";}
|
||||
initialize();
|
||||
if (M.getVod()){calcVodSizes();}
|
||||
if (!M.getLive()){calcVodSizes();}
|
||||
if (!streamName.size()){
|
||||
WARN_MSG("Recording unconnected EBML output to file! Cancelled.");
|
||||
conn.close();
|
||||
|
@ -139,7 +139,7 @@ namespace Mist{
|
|||
if (thisPacket.getTime() >= newClusterTime){
|
||||
if (liveSeek()){return;}
|
||||
currentClusterTime = thisPacket.getTime();
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
// In case of VoD, clusters are aligned with the main track fragments
|
||||
// EXCEPT when they are more than 30 seconds long, because clusters are limited to -32 to 32
|
||||
// seconds.
|
||||
|
@ -318,7 +318,7 @@ namespace Mist{
|
|||
void OutEBML::sendHeader(){
|
||||
double duration = 0;
|
||||
size_t idx = getMainSelectedTrack();
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
duration = M.getLastms(idx) - M.getFirstms(idx);
|
||||
}else{
|
||||
needsLookAhead = 420;
|
||||
|
@ -326,7 +326,7 @@ namespace Mist{
|
|||
// EBML header and Segment
|
||||
EBML::sendElemEBML(myConn, doctype);
|
||||
EBML::sendElemHead(myConn, EBML::EID_SEGMENT, segmentSize); // Default = Unknown size
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
// SeekHead
|
||||
EBML::sendElemHead(myConn, EBML::EID_SEEKHEAD, seekSize);
|
||||
EBML::sendElemSeek(myConn, EBML::EID_INFO, seekheadSize);
|
||||
|
@ -344,7 +344,7 @@ namespace Mist{
|
|||
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||
sendElemTrackEntry(it->first);
|
||||
}
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
EBML::sendElemHead(myConn, EBML::EID_CUES, cuesSize);
|
||||
uint64_t tmpsegSize = infoSize + tracksSize + seekheadSize + cuesSize +
|
||||
EBML::sizeElemHead(EBML::EID_CUES, cuesSize);
|
||||
|
@ -407,7 +407,7 @@ namespace Mist{
|
|||
|
||||
// Calculate the sizes of various parts, if we're VoD.
|
||||
size_t totalSize = 0;
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
calcVodSizes();
|
||||
// We now know the full size of the segment, thus can calculate the total size
|
||||
totalSize = EBML::sizeElemEBML(doctype) + EBML::sizeElemHead(EBML::EID_SEGMENT, segmentSize) + segmentSize;
|
||||
|
|
|
@ -125,7 +125,7 @@ namespace Mist{
|
|||
}
|
||||
size_t skippedLines = 0;
|
||||
if (M.getLive() && lines.size()){
|
||||
// only print the last segment when VoD
|
||||
// only print the last segment when non-live
|
||||
lines.pop_back();
|
||||
totalDuration -= durations.back();
|
||||
durations.pop_back();
|
||||
|
|
|
@ -503,7 +503,7 @@ namespace Mist{
|
|||
json_resp["width"] = 640;
|
||||
json_resp["height"] = (hasVideo ? 480 : 20);
|
||||
}
|
||||
json_resp["type"] = (M.getVod() ? "vod" : "live");
|
||||
json_resp["type"] = (M.getLive() ? "live" : "vod");
|
||||
if (M.getLive()){
|
||||
json_resp["unixoffset"] = M.getBootMsOffset() + (Util::unixMS() - Util::bootMS());
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ namespace Mist{
|
|||
+ 8 // MINF Box
|
||||
+ 36 // DINF Box
|
||||
+ 8; // STBL Box
|
||||
if (M.getVod() && M.getFirstms(it->first) != firstms){
|
||||
if (!M.getLive() && M.getFirstms(it->first) != firstms){
|
||||
tmpRes += 12; // EDTS entry extra
|
||||
}
|
||||
|
||||
|
@ -379,7 +379,7 @@ namespace Mist{
|
|||
// Construct with duration of -1, as this is the default for fragmented
|
||||
MP4::MVHD mvhdBox(-1);
|
||||
// Then override it when we are not sending a VoD asset
|
||||
if (M.getVod()){
|
||||
if (!M.getLive()){
|
||||
// calculating longest duration
|
||||
uint64_t lastms = 0;
|
||||
for (std::map<size_t, Comms::Users>::const_iterator it = userSelect.begin();
|
||||
|
@ -413,7 +413,7 @@ namespace Mist{
|
|||
MP4::ELST elstBox;
|
||||
elstBox.setVersion(0);
|
||||
elstBox.setFlags(0);
|
||||
if (M.getVod() && M.getFirstms(it->first) != firstms){
|
||||
if (!M.getLive() && M.getFirstms(it->first) != firstms){
|
||||
elstBox.setCount(2);
|
||||
|
||||
elstBox.setSegmentDuration(0, M.getFirstms(it->first) - firstms);
|
||||
|
@ -1161,7 +1161,7 @@ namespace Mist{
|
|||
H.StartResponse("206", "Partial content", req, myConn);
|
||||
}
|
||||
}else{
|
||||
if (M.getVod()){H.SetHeader("Content-Length", byteEnd - byteStart + 1);}
|
||||
if (!M.getLive()){H.SetHeader("Content-Length", byteEnd - byteStart + 1);}
|
||||
H.StartResponse("200", "OK", req, myConn);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue