DTSC Pull optimizes and quick-negotiate.

This commit is contained in:
Erik Zandvliet 2016-05-10 14:12:58 +02:00 committed by Thulinma
parent e8eb3a36ee
commit a5a9facc22
16 changed files with 159 additions and 211 deletions

View file

@ -62,6 +62,15 @@ static const char * DBG_LVL_LIST[] = {"NONE", "FAIL", "ERROR", "WARN", "INFO", "
#define SHM_DATASIZE 25 #define SHM_DATASIZE 25
#endif #endif
#ifndef STATS_DELAY
#define STATS_DELAY 15
#endif
#ifndef INPUT_TIMEOUT
#define INPUT_TIMEOUT STATS_DELAY
#endif
/// The size used for stream header pages under Windows, where they cannot be size-detected. /// The size used for stream header pages under Windows, where they cannot be size-detected.
#define DEFAULT_META_PAGE_SIZE 16 * 1024 * 1024 #define DEFAULT_META_PAGE_SIZE 16 * 1024 * 1024

View file

@ -127,10 +127,10 @@ namespace DTSC {
return; return;
} }
if(!src.spool()){ if(!src.spool()){
if (sleepCount++ > 5){ if (sleepCount++ > 60){
return; return;
} }
Util::sleep(500); Util::sleep(100);
} }
} }
} }

View file

@ -772,6 +772,7 @@ namespace IPC {
///\brief The deconstructor ///\brief The deconstructor
sharedServer::~sharedServer() { sharedServer::~sharedServer() {
finishEach();
mySemaphore.close(); mySemaphore.close();
mySemaphore.unlink(); mySemaphore.unlink();
} }
@ -828,6 +829,23 @@ namespace IPC {
return false; return false;
} }
///Disconnect all connected users
void sharedServer::finishEach(){
if (!hasCounter){
return;
}
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
if (!it->mapped || !it->len) {
break;
}
unsigned int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len) {
it->mapped[offset] = 126;
offset += payLen + (hasCounter ? 1 : 0);
}
}
}
///\brief Parse each of the possible payload pieces, and runs a callback on it if in use. ///\brief Parse each of the possible payload pieces, and runs a callback on it if in use.
void sharedServer::parseEach(void (*callback)(char * data, size_t len, unsigned int id)) { void sharedServer::parseEach(void (*callback)(char * data, size_t len, unsigned int id)) {
char * empty = 0; char * empty = 0;
@ -839,6 +857,7 @@ namespace IPC {
unsigned int id = 0; unsigned int id = 0;
unsigned int userCount = 0; unsigned int userCount = 0;
unsigned int emptyCount = 0; unsigned int emptyCount = 0;
connectedUsers = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) { for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
if (!it->mapped || !it->len) { if (!it->mapped || !it->len) {
DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?"); DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?");
@ -852,28 +871,25 @@ namespace IPC {
char * counter = it->mapped + offset; char * counter = it->mapped + offset;
//increase the count if needed //increase the count if needed
++userCount; ++userCount;
if (*counter & 0x80){
connectedUsers++;
}
if (id >= amount) { if (id >= amount) {
amount = id + 1; amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
unsigned short tmpPID = *((unsigned short *)(it->mapped + 1 + offset + payLen - 2)); unsigned short tmpPID = *((unsigned short *)(it->mapped + 1 + offset + payLen - 2));
if (!Util::Procs::isRunning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)) { if (!Util::Procs::isRunning(tmpPID) && !(*counter == 126 || *counter == 127)){
WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID); WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID);
*counter = 126; //if process is already dead, instant timeout. *counter = 126; //if process is already dead, instant timeout.
} }
callback(it->mapped + offset + 1, payLen, id); callback(it->mapped + offset + 1, payLen, id);
switch (*counter) { switch (*counter) {
case 127: case 127:
DEBUG_MSG(DLVL_HIGH, "Client %u requested disconnect", id); HIGH_MSG("Client %u requested disconnect", id);
break; break;
case 126: case 126:
DEBUG_MSG(DLVL_WARN, "Client %u timed out", id); HIGH_MSG("Client %u timed out", id);
break;
case 255:
DEBUG_MSG(DLVL_HIGH, "Client %u disconnected on request", id);
break;
case 254:
DEBUG_MSG(DLVL_WARN, "Client %u disconnect timed out", id);
break; break;
default: default:
#ifndef NOCRASHCHECK #ifndef NOCRASHCHECK
@ -893,7 +909,7 @@ namespace IPC {
#endif #endif
break; break;
} }
if (*counter == 127 || *counter == 126 || *counter == 255 || *counter == 254) { if (*counter == 127 || *counter == 126){
memset(it->mapped + offset + 1, 0, payLen); memset(it->mapped + offset + 1, 0, payLen);
it->mapped[offset] = 0; it->mapped[offset] = 0;
} else { } else {
@ -905,7 +921,7 @@ namespace IPC {
//bring the counter down if this was the last element //bring the counter down if this was the last element
if (id == amount - 1) { if (id == amount - 1) {
amount = id; amount = id;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
//stop, we're guaranteed no more pages are full at this point //stop, we're guaranteed no more pages are full at this point
break; break;
@ -917,7 +933,7 @@ namespace IPC {
//increase the count if needed //increase the count if needed
if (id >= amount) { if (id >= amount) {
amount = id + 1; amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
callback(it->mapped + offset, payLen, id); callback(it->mapped + offset, payLen, id);
} else { } else {
@ -926,7 +942,7 @@ namespace IPC {
//bring the counter down if this was the last element //bring the counter down if this was the last element
if (id == amount - 1) { if (id == amount - 1) {
amount = id; amount = id;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); VERYHIGH_MSG("Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
//stop, we're guaranteed no more pages are full at this point //stop, we're guaranteed no more pages are full at this point
if (empty) { if (empty) {
@ -962,12 +978,14 @@ namespace IPC {
hasCounter = 0; hasCounter = 0;
payLen = 0; payLen = 0;
offsetOnPage = 0; offsetOnPage = 0;
countAsViewer= true;
} }
///\brief Copy constructor for sharedClients ///\brief Copy constructor for sharedClients
///\param rhs The client ro copy ///\param rhs The client ro copy
sharedClient::sharedClient(const sharedClient & rhs) { sharedClient::sharedClient(const sharedClient & rhs) {
countAsViewer = rhs.countAsViewer;
baseName = rhs.baseName; baseName = rhs.baseName;
payLen = rhs.payLen; payLen = rhs.payLen;
hasCounter = rhs.hasCounter; hasCounter = rhs.hasCounter;
@ -988,6 +1006,7 @@ namespace IPC {
///\brief Assignment operator ///\brief Assignment operator
void sharedClient::operator =(const sharedClient & rhs) { void sharedClient::operator =(const sharedClient & rhs) {
countAsViewer = rhs.countAsViewer;
baseName = rhs.baseName; baseName = rhs.baseName;
payLen = rhs.payLen; payLen = rhs.payLen;
hasCounter = rhs.hasCounter; hasCounter = rhs.hasCounter;
@ -1011,6 +1030,7 @@ namespace IPC {
///\param len The size of the payload to allocate ///\param len The size of the payload to allocate
///\param withCounter Whether or not this payload has a counter ///\param withCounter Whether or not this payload has a counter
sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName("/" + name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) { sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName("/" + name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) {
countAsViewer = true;
#ifdef __APPLE__ #ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably //note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0); mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
@ -1072,52 +1092,6 @@ namespace IPC {
} }
bool sharedClient::isSingleEntry() {
semaphore tmpSem(baseName.c_str(), O_RDWR);
if (!tmpSem) {
HIGH_MSG("Creating semaphore %s failed: %s, assuming we're alone", baseName.c_str(), strerror(errno));
return true;
}
//Empty is used to compare for emptyness. This is not needed when the page uses a counter
char * empty = 0;
if (!hasCounter) {
empty = (char *)malloc(payLen * sizeof(char));
if (!empty) {
HIGH_MSG("Failed to allocate %u bytes for empty payload, assuming we're not alone", payLen);
return false;
}
memset(empty, 0, payLen);
}
bool result = true;
{
semGuard tmpGuard(&tmpSem);
for (char i = 'A'; i <= 'Z'; i++) {
sharedPage tmpPage(baseName.substr(1) + i, (4096 << (i - 'A')), false, false);
if (!tmpPage.mapped) {
break;
}
int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= tmpPage.len) {
//Skip our own entry
if (tmpPage.name == myPage.name && offset == offsetOnPage){
offset += payLen + (hasCounter ? 1 : 0);
continue;
}
if (!((hasCounter && tmpPage.mapped[offset] == 0) || (!hasCounter && !memcmp(tmpPage.mapped + offset, empty, payLen)))) {
result = false;
break;
}
offset += payLen + (hasCounter ? 1 : 0);
}
}
}
if (empty) {
free(empty);
}
return result;
}
///\brief Writes data to the shared data ///\brief Writes data to the shared data
void sharedClient::write(char * data, int len) { void sharedClient::write(char * data, int len) {
if (hasCounter) { if (hasCounter) {
@ -1137,7 +1111,7 @@ namespace IPC {
} }
if (myPage.mapped) { if (myPage.mapped) {
semGuard tmpGuard(&mySemaphore); semGuard tmpGuard(&mySemaphore);
myPage.mapped[offsetOnPage] = 127; myPage.mapped[offsetOnPage] = 126;
} }
} }
@ -1147,13 +1121,20 @@ namespace IPC {
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters"); DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters");
return; return;
} }
if (myPage.mapped[offsetOnPage] < 128) { if ((myPage.mapped[offsetOnPage] & 0x7F) < 126) {
myPage.mapped[offsetOnPage] = 1; myPage.mapped[offsetOnPage] = (countAsViewer ? 0x81 : 0x01);
} else { } else {
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element that needs to timeout, ignoring"); DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element that needs to timeout, ignoring");
} }
} }
bool sharedClient::isAlive() {
if (!hasCounter) {
return true;
}
return (myPage.mapped[offsetOnPage] & 0x7F) < 126;
}
///\brief Get a pointer to the data of this client ///\brief Get a pointer to the data of this client
char * sharedClient::getData() { char * sharedClient::getData() {
if (!myPage.mapped) { if (!myPage.mapped) {

View file

@ -184,6 +184,7 @@ namespace IPC {
operator bool() const; operator bool() const;
///\brief The amount of connected clients ///\brief The amount of connected clients
unsigned int amount; unsigned int amount;
unsigned int connectedUsers;
private: private:
bool isInUse(unsigned int id); bool isInUse(unsigned int id);
void newPage(); void newPage();
@ -198,6 +199,7 @@ namespace IPC {
semaphore mySemaphore; semaphore mySemaphore;
///\brief Whether the payload has a counter, if so, it is added in front of the payload ///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter; bool hasCounter;
void finishEach();
}; };
///\brief The client part of a server/client model for shared memory. ///\brief The client part of a server/client model for shared memory.
@ -219,9 +221,10 @@ namespace IPC {
void write(char * data, int len); void write(char * data, int len);
void finish(); void finish();
void keepAlive(); void keepAlive();
bool isAlive();
char * getData(); char * getData();
int getCounter(); int getCounter();
bool isSingleEntry(); bool countAsViewer;
private: private:
///\brief The basename of the shared pages. ///\brief The basename of the shared pages.
std::string baseName; std::string baseName;

View file

@ -32,10 +32,6 @@
#define COUNTABLE_BYTES 128*1024 #define COUNTABLE_BYTES 128*1024
#ifndef STATS_DELAY
#define STATS_DELAY 15
#endif
std::map<Controller::sessIndex, Controller::statSession> Controller::sessions; ///< list of sessions that have statistics data available std::map<Controller::sessIndex, Controller::statSession> Controller::sessions; ///< list of sessions that have statistics data available
std::map<unsigned long, Controller::sessIndex> Controller::connToSession; ///< Map of socket IDs to session info. std::map<unsigned long, Controller::sessIndex> Controller::connToSession; ///< Map of socket IDs to session info.
@ -595,7 +591,7 @@ void Controller::parseStatistics(char * data, size_t len, unsigned int id){
sessions[idx].update(id, tmpEx); sessions[idx].update(id, tmpEx);
//check validity of stats data //check validity of stats data
char counter = (*(data - 1)); char counter = (*(data - 1));
if (counter == 126 || counter == 127 || counter == 254 || counter == 255){ if (counter == 126 || counter == 127){
//the data is no longer valid - connection has gone away, store for later //the data is no longer valid - connection has gone away, store for later
sessions[idx].finish(id); sessions[idx].finish(id);
connToSession.erase(id); connToSession.erase(id);

View file

@ -215,17 +215,19 @@ namespace Mist {
DEBUG_MSG(DLVL_DEVEL, "Input for stream %s started", streamName.c_str()); DEBUG_MSG(DLVL_DEVEL, "Input for stream %s started", streamName.c_str());
long long int activityCounter = Util::bootSecs(); long long int activityCounter = Util::bootSecs();
while ((Util::bootSecs() - activityCounter) < 10 && config->is_active) { //10 second timeout while ((Util::bootSecs() - activityCounter) < INPUT_TIMEOUT && config->is_active) { //15 second timeout
userPage.parseEach(callbackWrapper); userPage.parseEach(callbackWrapper);
removeUnused(); removeUnused();
if (userPage.amount) { if (userPage.connectedUsers) {
activityCounter = Util::bootSecs(); if (myMeta.tracks.size()){
DEBUG_MSG(DLVL_INSANE, "Connected users: %d", userPage.amount); activityCounter = Util::bootSecs();
}
DEBUG_MSG(DLVL_INSANE, "Connected users: %d", userPage.connectedUsers);
} else { } else {
DEBUG_MSG(DLVL_INSANE, "Timer running"); DEBUG_MSG(DLVL_INSANE, "Timer running");
} }
/*LTS-START*/ /*LTS-START*/
if ((Util::bootSecs() - activityCounter) >= 10 || !config->is_active){//10 second timeout if ((Util::bootSecs() - activityCounter) >= INPUT_TIMEOUT || !config->is_active){//15 second timeout
if(Triggers::shouldTrigger("STREAM_UNLOAD", config->getString("streamname"))){ if(Triggers::shouldTrigger("STREAM_UNLOAD", config->getString("streamname"))){
std::string payload = config->getString("streamname")+"\n" +capa["name"].asStringRef()+"\n"; std::string payload = config->getString("streamname")+"\n" +capa["name"].asStringRef()+"\n";
if (!Triggers::doTrigger("STREAM_UNLOAD", payload, config->getString("streamname"))){ if (!Triggers::doTrigger("STREAM_UNLOAD", payload, config->getString("streamname"))){
@ -251,16 +253,19 @@ namespace Mist {
pullLock.open(std::string("/MstPull_" + streamName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); pullLock.open(std::string("/MstPull_" + streamName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
if (!pullLock.tryWait()){ if (!pullLock.tryWait()){
DEBUG_MSG(DLVL_DEVEL, "A pull process for stream %s is already running", streamName.c_str()); DEBUG_MSG(DLVL_DEVEL, "A pull process for stream %s is already running", streamName.c_str());
pullLock.close();
return; return;
} }
if (Util::streamAlive(streamName)){ if (Util::streamAlive(streamName)){
pullLock.post(); pullLock.post();
pullLock.close(); pullLock.close();
pullLock.unlink();
return; return;
} }
if (!Util::startInput(streamName, "push://")) {//manually override stream url to start the buffer if (!Util::startInput(streamName, "push://")) {//manually override stream url to start the buffer
pullLock.post(); pullLock.post();
pullLock.close(); pullLock.close();
pullLock.unlink();
return; return;
} }
@ -283,8 +288,10 @@ namespace Mist {
finish(); finish();
pullLock.post(); pullLock.post();
pullLock.close(); pullLock.close();
pullLock.unlink();
return; return;
} }
nProxy.userClient.countAsViewer = false;
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
it->second.firstms = 0; it->second.firstms = 0;
@ -294,44 +301,20 @@ namespace Mist {
getNext(); getNext();
unsigned long long lastTime = Util::getMS(); unsigned long long lastTime = Util::getMS();
unsigned long long lastActive = Util::getMS(); unsigned long long lastActive = Util::getMS();
while (thisPacket && config->is_active){ while (thisPacket && config->is_active && nProxy.userClient.isAlive()){
nProxy.bufferLivePacket(thisPacket, myMeta); nProxy.bufferLivePacket(thisPacket, myMeta);
getNext(); getNext();
nProxy.userClient.keepAlive(); nProxy.userClient.keepAlive();
if (Util::getMS() - lastTime >= 1000){
lastTime = Util::getMS();
if (nProxy.userClient.isSingleEntry()){
if (lastTime - lastActive >= 10000){//10sec timeout
config->is_active = false;
}
}else{
lastActive = lastTime;
}
}
} }
closeStreamSource(); closeStreamSource();
while (config->is_active){
Util::sleep(500);
nProxy.userClient.keepAlive();
if (Util::getMS() - lastTime >= 1000){
lastTime = Util::getMS();
if (nProxy.userClient.isSingleEntry()){
if (lastTime - lastActive >= 10000){//10sec timeout
config->is_active = false;
}
}else{
lastActive = lastTime;
}
}
}
nProxy.userClient.finish(); nProxy.userClient.finish();
finish(); finish();
pullLock.post(); pullLock.post();
pullLock.close(); pullLock.close();
pullLock.unlink();
DEBUG_MSG(DLVL_DEVEL, "Pull input for stream %s closing clean", streamName.c_str());
return; return;
} }

View file

@ -530,7 +530,7 @@ namespace Mist {
//If the current value indicates a valid trackid, and it is pushed from this user //If the current value indicates a valid trackid, and it is pushed from this user
if (pushLocation[value] == data) { if (pushLocation[value] == data) {
//Check for timeouts, and erase the track if necessary //Check for timeouts, and erase the track if necessary
if (counter == 126 || counter == 127 || counter == 254 || counter == 255) { if (counter == 126 || counter == 127){
pushLocation.erase(value); pushLocation.erase(value);
if (negotiatingTracks.count(value)) { if (negotiatingTracks.count(value)) {
negotiatingTracks.erase(value); negotiatingTracks.erase(value);
@ -594,11 +594,9 @@ namespace Mist {
char firstPage[NAME_BUFFER_SIZE]; char firstPage[NAME_BUFFER_SIZE];
snprintf(firstPage, NAME_BUFFER_SIZE, SHM_TRACK_INDEX, config->getString("streamname").c_str(), finalMap); snprintf(firstPage, NAME_BUFFER_SIZE, SHM_TRACK_INDEX, config->getString("streamname").c_str(), finalMap);
nProxy.metaPages[finalMap].init(firstPage, 8192, false); nProxy.metaPages[finalMap].init(firstPage, 8192, false);
INFO_MSG("Meh %d", finalMap);
//Update the metadata for this track //Update the metadata for this track
updateTrackMeta(finalMap); updateTrackMeta(finalMap);
INFO_MSG("Setting hasPush to true, quickNegotiate");
hasPush = true; hasPush = true;
} }
//Write the final mapped track number and keyframe number to the user page element //Write the final mapped track number and keyframe number to the user page element

View file

@ -621,10 +621,8 @@ namespace Mist {
if (!trackState.count(tid)) { if (!trackState.count(tid)) {
memset(tmp + offset, 0, 4); memset(tmp + offset, 0, 4);
if (quickNegotiate){ if (quickNegotiate){
unsigned long finalTid = tid;
unsigned long finalTid = getpid() + tid;
unsigned short firstPage = 1; unsigned short firstPage = 1;
INFO_MSG("HANDLING quick negotiation for track %d ~> %d", tid, finalTid)
MEDIUM_MSG("Buffer has indicated that incoming track %lu should start writing on track %lu, page %lu", tid, finalTid, firstPage); MEDIUM_MSG("Buffer has indicated that incoming track %lu should start writing on track %lu, page %lu", tid, finalTid, firstPage);
trackMap[tid] = finalTid; trackMap[tid] = finalTid;
if (myMeta.tracks.count(finalTid) && myMeta.tracks[finalTid].lastms){ if (myMeta.tracks.count(finalTid) && myMeta.tracks[finalTid].lastms){

View file

@ -184,6 +184,16 @@ namespace Mist {
return myConn.getBinHost(); return myConn.getBinHost();
} }
bool Output::isReadyForPlay() {
if (myMeta.tracks.size()){
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.keys.size() >= 2){
return true;
}
}
}
return false;
}
/// Connects or reconnects to the stream. /// Connects or reconnects to the stream.
/// Assumes streamName class member has been set already. /// Assumes streamName class member has been set already.
/// Will start input if not currently active, calls onFail() if this does not succeed. /// Will start input if not currently active, calls onFail() if this does not succeed.
@ -215,27 +225,15 @@ namespace Mist {
return; return;
} }
updateMeta(); updateMeta();
if (myMeta.live && needsPlayableKeys()){ if (myMeta.live && !isReadyForPlay()){
bool waitALittleLonger = true;
unsigned int maxWaits = 15; unsigned int maxWaits = 15;
while (waitALittleLonger){ while (!isReadyForPlay()){
waitALittleLonger = true; Util::sleep(1000);
if (myMeta.tracks.size()){ if (--maxWaits == 0){
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ FAIL_MSG("Giving up waiting for playable tracks");
if (it->second.keys.size() >= needsPlayableKeys()){ break;
waitALittleLonger = false;
break;
}
}
}
if (waitALittleLonger){
Util::sleep(1000);
if (--maxWaits == 0){
FAIL_MSG("Giving up waiting for playable tracks");
waitALittleLonger = false;
}
updateMeta();
} }
updateMeta();
} }
} }
} }
@ -435,6 +433,7 @@ namespace Mist {
return; return;
} }
DEBUG_MSG(DLVL_VERYHIGH, "Loading track %lu, containing key %lld", trackId, keyNum); DEBUG_MSG(DLVL_VERYHIGH, "Loading track %lu, containing key %lld", trackId, keyNum);
INFO_MSG("Loading track %lu, containing key %lld", trackId, keyNum);
unsigned int timeout = 0; unsigned int timeout = 0;
unsigned long pageNum = pageNumForKey(trackId, keyNum); unsigned long pageNum = pageNumForKey(trackId, keyNum);
while (pageNum == -1){ while (pageNum == -1){
@ -482,6 +481,7 @@ namespace Mist {
return; return;
} }
currKeyOpen[trackId] = pageNum; currKeyOpen[trackId] = pageNum;
INFO_MSG("page %s loaded", id);
} }
/// Prepares all tracks from selectedTracks for seeking to the specified ms position. /// Prepares all tracks from selectedTracks for seeking to the specified ms position.
@ -507,7 +507,14 @@ namespace Mist {
INFO_MSG("Aborting seek to %llums in track %u: past end of track.", pos, tid); INFO_MSG("Aborting seek to %llums in track %u: past end of track.", pos, tid);
return false; return false;
} }
loadPageForKey(tid, getKeyForTime(tid, pos) + (getNextKey?1:0)); unsigned int keyNum = getKeyForTime(tid, pos);
if (myMeta.tracks[tid].getKey(keyNum).getTime() > pos){
if (myMeta.live){
INFO_MSG("Actually seeking to %d, for %d is not available anymore", myMeta.tracks[tid].getKey(keyNum).getTime(), pos);
pos = myMeta.tracks[tid].getKey(keyNum).getTime();
}
}
loadPageForKey(tid, keyNum + (getNextKey?1:0));
if (!nProxy.curPage.count(tid) || !nProxy.curPage[tid].mapped){ if (!nProxy.curPage.count(tid) || !nProxy.curPage[tid].mapped){
INFO_MSG("Aborting seek to %llums in track %u: not available.", pos, tid); INFO_MSG("Aborting seek to %llums in track %u: not available.", pos, tid);
return false; return false;
@ -524,6 +531,7 @@ namespace Mist {
tmpPack.reInit(mpd + tmp.offset, 0, true); tmpPack.reInit(mpd + tmp.offset, 0, true);
tmp.time = tmpPack.getTime(); tmp.time = tmpPack.getTime();
} }
INFO_MSG("Found time %d", tmp.time);
if (tmpPack){ if (tmpPack){
buffer.insert(tmp); buffer.insert(tmp);
return true; return true;
@ -1022,9 +1030,26 @@ namespace Mist {
if (thisPacket.getTime() != nxt.time && nxt.time){ if (thisPacket.getTime() != nxt.time && nxt.time){
WARN_MSG("Loaded track %ld@%llu instead of %ld@%llu", thisPacket.getTrackId(), thisPacket.getTime(), nxt.tid, nxt.time); WARN_MSG("Loaded track %ld@%llu instead of %ld@%llu", thisPacket.getTrackId(), thisPacket.getTime(), nxt.tid, nxt.time);
} }
if ((myMeta.tracks[nxt.tid].type == "video" && thisPacket.getFlag("keyframe")) || (++nonVideoCount % 30 == 0)){ bool isVideoTrack = (myMeta.tracks[nxt.tid].type == "video");
if ((isVideoTrack && thisPacket.getFlag("keyframe")) || (!isVideoTrack && (++nonVideoCount % 30 == 0))){
if (myMeta.live){ if (myMeta.live){
updateMeta(); if (myMeta.tracks[nxt.tid].type == "video"){
//Check whether returned keyframe is correct. If not, wait for approximately 5 seconds while checking.
//Failure here will cause tracks to drop due to inconsistent internal state.
nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, thisPacket.getTime());
int counter = 0;
while(counter < 10 && myMeta.tracks[nxt.tid].getKey(nxtKeyNum[nxt.tid]).getTime() != thisPacket.getTime()){
if (counter++){
//Only sleep 500ms if this is not the first updatemeta try
Util::sleep(500);
}
updateMeta();
nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, thisPacket.getTime());
}
}else{
//On non-video tracks, just update metadata and assume everything else is correct
updateMeta();
}
} }
nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, thisPacket.getTime()); nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, thisPacket.getTime());
DEBUG_MSG(DLVL_VERYHIGH, "Track %u @ %llums = key %lu", nxt.tid, thisPacket.getTime(), nxtKeyNum[nxt.tid]); DEBUG_MSG(DLVL_VERYHIGH, "Track %u @ %llums = key %lu", nxt.tid, thisPacket.getTime(), nxtKeyNum[nxt.tid]);
@ -1081,6 +1106,12 @@ namespace Mist {
if (nProxy.curPage[nxt.tid]){ if (nProxy.curPage[nxt.tid]){
if (nxt.offset < nProxy.curPage[nxt.tid].len){ if (nxt.offset < nProxy.curPage[nxt.tid].len){
unsigned long long nextTime = getDTSCTime(nProxy.curPage[nxt.tid].mapped, nxt.offset); unsigned long long nextTime = getDTSCTime(nProxy.curPage[nxt.tid].mapped, nxt.offset);
int ctr = 0;
//sleep at most half a second for new data.
while (!nextTime && ++ctr < 5){
Util::sleep(1000);
nextTime = getDTSCTime(nProxy.curPage[nxt.tid].mapped, nxt.offset);
}
if (nextTime){ if (nextTime){
nxt.time = nextTime; nxt.time = nextTime;
}else{ }else{
@ -1100,7 +1131,7 @@ namespace Mist {
unsigned long long int now = Util::epoch(); unsigned long long int now = Util::epoch();
if (now != lastStats){ if (now != lastStats){
/*LTS-START*/ /*LTS-START*/
if (statsPage.getData()[-1] > 127){ if (!statsPage.isAlive()){
myConn.close(); myConn.close();
return; return;
} }
@ -1136,7 +1167,7 @@ namespace Mist {
return; return;
} }
} }
if (nProxy.userClient.getData()[-1] > 127){ if (!nProxy.userClient.isAlive()){
myConn.close(); myConn.close();
return; return;
} }

View file

@ -64,7 +64,7 @@ namespace Mist {
void selectDefaultTracks(); void selectDefaultTracks();
bool connectToFile(std::string file); bool connectToFile(std::string file);
static bool listenMode(){return true;} static bool listenMode(){return true;}
virtual unsigned int needsPlayableKeys(){return 2;} virtual bool isReadyForPlay();
//virtuals. The optional virtuals have default implementations that do as little as possible. //virtuals. The optional virtuals have default implementations that do as little as possible.
virtual void sendNext() {}//REQUIRED! Others are optional. virtual void sendNext() {}//REQUIRED! Others are optional.
virtual void prepareNext(); virtual void prepareNext();

View file

@ -29,7 +29,6 @@ namespace Mist {
myConn.SendNow(sSize, 4); myConn.SendNow(sSize, 4);
prep.sendTo(myConn); prep.sendTo(myConn);
pushing = false; pushing = false;
fastAsPossibleTime = 0;
} }
OutDTSC::~OutDTSC() {} OutDTSC::~OutDTSC() {}
@ -45,29 +44,6 @@ namespace Mist {
} }
void OutDTSC::sendNext(){ void OutDTSC::sendNext(){
if (!realTime && thisPacket.getTime() >= fastAsPossibleTime){
realTime = 1000;
}
if (thisPacket.getFlag("keyframe")){
std::set<unsigned long> availableTracks;
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.type == "video" || it->second.type == "audio"){
availableTracks.insert(it->first);
}
}
if (availableTracks != selectedTracks){
//reset, resendheader
JSON::Value prep;
prep["cmd"] = "reset";
/// \todo Make this securererer.
unsigned long sendSize = prep.packedSize();
myConn.SendNow("DTCM");
char sSize[4] = {0, 0, 0, 0};
Bit::htobl(sSize, prep.packedSize());
myConn.SendNow(sSize, 4);
prep.sendTo(myConn);
}
}
myConn.SendNow(thisPacket.getData(), thisPacket.getDataLen()); myConn.SendNow(thisPacket.getData(), thisPacket.getDataLen());
} }
@ -81,15 +57,9 @@ namespace Mist {
} }
myMeta.send(myConn, true, selectedTracks); myMeta.send(myConn, true, selectedTracks);
if (myMeta.live){ if (myMeta.live){
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ realTime = 0;
if (!fastAsPossibleTime || it->second.lastms < fastAsPossibleTime){
fastAsPossibleTime = it->second.lastms;
realTime = 0;
}
}
}else{
realTime = 1000;
} }
seek(0);
} }
void OutDTSC::onRequest(){ void OutDTSC::onRequest(){

View file

@ -3,30 +3,20 @@
#include <unistd.h> #include <unistd.h>
namespace Mist { namespace Mist {
bool OutHLS::isReadyForPlay() {
if (myMeta.tracks.size()){
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.fragments.size() >= 3){
return true;
}
}
}
return false;
}
///\brief Builds an index file for HTTP Live streaming. ///\brief Builds an index file for HTTP Live streaming.
///\return The index file for HTTP Live Streaming. ///\return The index file for HTTP Live Streaming.
std::string OutHLS::liveIndex() { std::string OutHLS::liveIndex() {
static int timer = 0;
bool checkWait = true;
while (checkWait && ++timer < 10){
checkWait = false;
if (!myMeta.tracks.size()){
checkWait = true;
}
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.keys.size() <= 3){
checkWait = true;
break;
}
}
if (checkWait){
Util::sleep(500);
INFO_MSG("SLeeping timer %d", timer);
updateMeta();
}
}
std::stringstream result; std::stringstream result;
result << "#EXTM3U\r\n"; result << "#EXTM3U\r\n";
int audioId = -1; int audioId = -1;

View file

@ -9,6 +9,7 @@ namespace Mist {
static void init(Util::Config * cfg); static void init(Util::Config * cfg);
void sendTS(const char * tsData, unsigned int len=188); void sendTS(const char * tsData, unsigned int len=188);
void onHTTP(); void onHTTP();
bool isReadyForPlay();
protected: protected:
std::string liveIndex(); std::string liveIndex();
std::string liveIndex(int tid, std::string & sessId); std::string liveIndex(int tid, std::string & sessId);

View file

@ -9,6 +9,7 @@ namespace Mist {
OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn) { OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : HTTPOutput(conn) {
completeKeysOnly = false; completeKeysOnly = false;
} }
OutProgressiveMP4::~OutProgressiveMP4() {} OutProgressiveMP4::~OutProgressiveMP4() {}
void OutProgressiveMP4::init(Util::Config * cfg) { void OutProgressiveMP4::init(Util::Config * cfg) {
@ -747,25 +748,6 @@ namespace Mist {
void OutProgressiveMP4::setvidTrack() { void OutProgressiveMP4::setvidTrack() {
vidTrack = 0; vidTrack = 0;
static int timer = 0;
bool checkWait = true;
while (checkWait && ++timer < 10){
checkWait = false;
if (!myMeta.tracks.size()){
checkWait = true;
}
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (!it->second.keys.size()){
checkWait = true;
break;
}
}
if (checkWait){
Util::sleep(500);
updateMeta();
}
}
if (!selectedTracks.size()){ if (!selectedTracks.size()){
selectDefaultTracks(); selectDefaultTracks();
} }

View file

@ -153,12 +153,18 @@ namespace Mist {
return !(config->getString("target").size()); return !(config->getString("target").size());
} }
unsigned int OutRTMP::needsPlayableKeys(){ bool OutRTMP::isReadyForPlay(){
if (isPushing){ if (isPushing){
return 0; return true;
}else{
return 2;
} }
if (myMeta.tracks.size()){
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.keys.size() >= 2){
return true;
}
}
}
return false;
} }
void OutRTMP::parseVars(std::string data){ void OutRTMP::parseVars(std::string data){

View file

@ -14,7 +14,7 @@ namespace Mist {
void onRequest(); void onRequest();
void sendNext(); void sendNext();
void sendHeader(); void sendHeader();
unsigned int needsPlayableKeys(); bool isReadyForPlay();
static bool listenMode(); static bool listenMode();
protected: protected:
bool isPushing; bool isPushing;