Sessions rework
This commit is contained in:
parent
3e85da2afd
commit
074e757028
27 changed files with 1222 additions and 1183 deletions
|
@ -7,7 +7,7 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "output.h"
|
||||
#include <mist/bitfields.h>
|
||||
#include <mist/defines.h>
|
||||
#include <mist/h264.h>
|
||||
|
@ -92,7 +92,7 @@ namespace Mist{
|
|||
firstTime = 0;
|
||||
firstPacketTime = 0xFFFFFFFFFFFFFFFFull;
|
||||
lastPacketTime = 0;
|
||||
crc = getpid();
|
||||
sid = "";
|
||||
parseData = false;
|
||||
wantRequest = true;
|
||||
sought = false;
|
||||
|
@ -100,7 +100,7 @@ namespace Mist{
|
|||
isBlocking = false;
|
||||
needsLookAhead = 0;
|
||||
extraKeepAway = 0;
|
||||
lastStats = 0;
|
||||
lastStats = 0xFFFFFFFFFFFFFFFFull;
|
||||
maxSkipAhead = 7500;
|
||||
uaDelay = 10;
|
||||
realTime = 1000;
|
||||
|
@ -111,6 +111,7 @@ namespace Mist{
|
|||
lastPushUpdate = 0;
|
||||
previousFile = "";
|
||||
currentFile = "";
|
||||
sessionMode = 0xFFFFFFFFFFFFFFFFull;
|
||||
|
||||
lastRecv = Util::bootSecs();
|
||||
if (myConn){
|
||||
|
@ -211,95 +212,9 @@ namespace Mist{
|
|||
onFail("Not allowed to play (CONN_PLAY)");
|
||||
}
|
||||
}
|
||||
doSync(true);
|
||||
/*LTS-END*/
|
||||
}
|
||||
|
||||
/// If called with force set to true and a USER_NEW trigger enabled, forces a sync immediately.
|
||||
/// Otherwise, does nothing unless the sync byte is set to 2, in which case it forces a sync as well.
|
||||
/// May be called recursively because it calls stats() which calls this function.
|
||||
/// If this happens, the extra calls to the function return instantly.
|
||||
void Output::doSync(bool force){
|
||||
if (!statComm){return;}
|
||||
if (recursingSync){return;}
|
||||
recursingSync = true;
|
||||
if (statComm.getSync() == 2 || force){
|
||||
if (getStatsName() == capa["name"].asStringRef() && Triggers::shouldTrigger("USER_NEW", streamName)){
|
||||
// sync byte 0 = no sync yet, wait for sync from controller...
|
||||
char initialSync = 0;
|
||||
// attempt to load sync status from session cache in shm
|
||||
{
|
||||
IPC::semaphore cacheLock(SEM_SESSCACHE, O_RDWR, ACCESSPERMS, 16);
|
||||
if (cacheLock){cacheLock.wait();}
|
||||
IPC::sharedPage shmSessions(SHM_SESSIONS, SHM_SESSIONS_SIZE, false, false);
|
||||
if (shmSessions.mapped){
|
||||
char shmEmpty[SHM_SESSIONS_ITEM];
|
||||
memset(shmEmpty, 0, SHM_SESSIONS_ITEM);
|
||||
std::string host;
|
||||
Socket::hostBytesToStr(statComm.getHost().data(), 16, host);
|
||||
uint32_t shmOffset = 0;
|
||||
const std::string &cName = capa["name"].asStringRef();
|
||||
while (shmOffset + SHM_SESSIONS_ITEM < SHM_SESSIONS_SIZE){
|
||||
// compare crc
|
||||
if (*(uint32_t*)(shmSessions.mapped + shmOffset) == crc){
|
||||
// compare stream name
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 4, streamName.c_str(), 100) == 0){
|
||||
// compare connector
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 104, cName.c_str(), 20) == 0){
|
||||
// compare host
|
||||
if (strncmp(shmSessions.mapped + shmOffset + 124, host.c_str(), 40) == 0){
|
||||
initialSync = shmSessions.mapped[shmOffset + 164];
|
||||
HIGH_MSG("Instant-sync from session cache to %u", (unsigned int)initialSync);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// stop if we reached the end
|
||||
if (memcmp(shmSessions.mapped + shmOffset, shmEmpty, SHM_SESSIONS_ITEM) == 0){
|
||||
break;
|
||||
}
|
||||
shmOffset += SHM_SESSIONS_ITEM;
|
||||
}
|
||||
}
|
||||
if (cacheLock){cacheLock.post();}
|
||||
}
|
||||
unsigned int i = 0;
|
||||
statComm.setSync(initialSync);
|
||||
// wait max 10 seconds for sync
|
||||
while ((!statComm.getSync() || statComm.getSync() == 2) && i++ < 100){
|
||||
Util::wait(100);
|
||||
stats(true);
|
||||
}
|
||||
HIGH_MSG("USER_NEW sync achieved: %u", statComm.getSync());
|
||||
// 1 = check requested (connection is new)
|
||||
if (statComm.getSync() == 1){
|
||||
std::string payload = streamName + "\n" + getConnectedHost() + "\n" +
|
||||
JSON::Value(crc).asString() + "\n" + capa["name"].asStringRef() +
|
||||
"\n" + reqUrl + "\n" + statComm.getSessId();
|
||||
if (!Triggers::doTrigger("USER_NEW", payload, streamName)){
|
||||
onFail("Not allowed to play (USER_NEW)");
|
||||
statComm.setSync(100); // 100 = denied
|
||||
}else{
|
||||
statComm.setSync(10); // 10 = accepted
|
||||
}
|
||||
}
|
||||
// 100 = denied
|
||||
if (statComm.getSync() == 100){onFail("Not allowed to play (USER_NEW cache)");}
|
||||
if (statComm.getSync() == 0){
|
||||
onFail("Not allowed to play (USER_NEW init timeout)", true);
|
||||
}
|
||||
if (statComm.getSync() == 2){
|
||||
onFail("Not allowed to play (USER_NEW re-init timeout)", true);
|
||||
}
|
||||
// anything else = accepted
|
||||
}else{
|
||||
statComm.setSync(10); // auto-accept if no trigger
|
||||
}
|
||||
}
|
||||
recursingSync = false;
|
||||
}
|
||||
|
||||
std::string Output::getConnectedHost(){return myConn.getHost();}
|
||||
|
||||
std::string Output::getConnectedBinHost(){
|
||||
|
@ -433,10 +348,10 @@ namespace Mist{
|
|||
|
||||
//Connect to stats reporting, if not connected already
|
||||
if (!statComm){
|
||||
statComm.reload();
|
||||
statComm.reload(streamName, getConnectedHost(), sid, capa["name"].asStringRef(), reqUrl, sessionMode);
|
||||
stats(true);
|
||||
}
|
||||
|
||||
|
||||
//push inputs do not need to wait for stream to be ready for playback
|
||||
if (isPushing()){return;}
|
||||
|
||||
|
@ -986,7 +901,7 @@ namespace Mist{
|
|||
INFO_MSG("Will split recording every %lld seconds", atoll(targetParams["split"].c_str()));
|
||||
targetParams["nxt-split"] = JSON::Value((int64_t)(seekPos + endRec)).asString();
|
||||
}
|
||||
// Duration to record in seconds. Overrides recstop.
|
||||
// Duration to record in seconds. Oversides recstop.
|
||||
if (targetParams.count("duration")){
|
||||
long long endRec = atoll(targetParams["duration"].c_str()) * 1000;
|
||||
targetParams["recstop"] = JSON::Value((int64_t)(seekPos + endRec)).asString();
|
||||
|
@ -1301,6 +1216,7 @@ namespace Mist{
|
|||
/// request URL (if any)
|
||||
/// ~~~~~~~~~~~~~~~
|
||||
int Output::run(){
|
||||
sessionMode = Util::getGlobalConfig("sessionMode").asInt();
|
||||
/*LTS-START*/
|
||||
// Connect to file target, if needed
|
||||
if (isFileTarget()){
|
||||
|
@ -1507,6 +1423,8 @@ namespace Mist{
|
|||
/*LTS-END*/
|
||||
|
||||
disconnect();
|
||||
stats(true);
|
||||
userSelect.clear();
|
||||
myConn.close();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1822,7 +1740,7 @@ namespace Mist{
|
|||
// also cancel if it has been less than a second since the last update
|
||||
// unless force is set to true
|
||||
uint64_t now = Util::bootSecs();
|
||||
if (now == lastStats && !force){return;}
|
||||
if (now <= lastStats && !force){return;}
|
||||
|
||||
if (isRecording()){
|
||||
if(lastPushUpdate == 0){
|
||||
|
@ -1861,13 +1779,17 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
if (!statComm){statComm.reload();}
|
||||
if (!statComm){return;}
|
||||
if (!statComm){statComm.reload(streamName, getConnectedHost(), sid, capa["name"].asStringRef(), reqUrl, sessionMode);}
|
||||
if (!statComm){return;}
|
||||
if (statComm.getExit()){
|
||||
onFail("Shutting down since this session is not allowed to view this stream");
|
||||
return;
|
||||
}
|
||||
|
||||
lastStats = now;
|
||||
|
||||
VERYHIGH_MSG("Writing stats: %s, %s, %u, %" PRIu64 ", %" PRIu64, getConnectedHost().c_str(), streamName.c_str(),
|
||||
crc & 0xFFFFFFFFu, myConn.dataUp(), myConn.dataDown());
|
||||
VERYHIGH_MSG("Writing stats: %s, %s, %s, %" PRIu64 ", %" PRIu64, getConnectedHost().c_str(), streamName.c_str(),
|
||||
sid.c_str(), myConn.dataUp(), myConn.dataDown());
|
||||
/*LTS-START*/
|
||||
if (statComm.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
onFail("Shutting down on controller request");
|
||||
|
@ -1875,9 +1797,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
statComm.setNow(now);
|
||||
statComm.setHost(getConnectedBinHost());
|
||||
statComm.setCRC(crc);
|
||||
statComm.setStream(streamName);
|
||||
statComm.setConnector(getStatsName());
|
||||
connStats(now, statComm);
|
||||
statComm.setLastSecond(thisPacket ? thisPacket.getTime() : 0);
|
||||
|
@ -1887,7 +1806,7 @@ namespace Mist{
|
|||
// Tag the session with the user agent
|
||||
if (newUA && ((now - myConn.connTime()) >= uaDelay || !myConn) && UA.size()){
|
||||
std::string APIcall =
|
||||
"{\"tag_sessid\":{\"" + statComm.getSessId() + "\":" + JSON::string_escape("UA:" + UA) + "}}";
|
||||
"{\"tag_sessid\":{\"" + statComm.sessionId + "\":" + JSON::string_escape("UA:" + UA) + "}}";
|
||||
Socket::UDPConnection uSock;
|
||||
uSock.SetDestination(UDP_API_HOST, UDP_API_PORT);
|
||||
uSock.SendNow(APIcall);
|
||||
|
@ -1895,8 +1814,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
|
||||
doSync();
|
||||
|
||||
if (isPushing()){
|
||||
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
|
||||
if (it->second.getStatus() & COMM_STATUS_REQDISCONNECT){
|
||||
|
@ -1912,7 +1829,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void Output::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void Output::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
statComm.setUp(myConn.dataUp());
|
||||
statComm.setDown(myConn.dataDown());
|
||||
statComm.setTime(now - myConn.connTime());
|
||||
|
|
|
@ -86,7 +86,6 @@ namespace Mist{
|
|||
std::string hostLookup(std::string ip);
|
||||
bool onList(std::string ip, std::string list);
|
||||
std::string getCountry(std::string ip);
|
||||
void doSync(bool force = false);
|
||||
/*LTS-END*/
|
||||
std::map<size_t, uint32_t> currentPage;
|
||||
void loadPageForKey(size_t trackId, size_t keyNum);
|
||||
|
@ -105,6 +104,7 @@ namespace Mist{
|
|||
bool firstData;
|
||||
uint64_t lastPushUpdate;
|
||||
bool newUA;
|
||||
|
||||
protected: // these are to be messed with by child classes
|
||||
virtual bool inlineRestartCapable() const{
|
||||
return false;
|
||||
|
@ -122,15 +122,16 @@ namespace Mist{
|
|||
virtual std::string getStatsName();
|
||||
virtual bool hasSessionIDs(){return false;}
|
||||
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
|
||||
std::set<size_t> getSupportedTracks(const std::string &type = "") const;
|
||||
|
||||
inline virtual bool keepGoing(){return config->is_active && myConn;}
|
||||
|
||||
Comms::Statistics statComm;
|
||||
Comms::Connections statComm;
|
||||
bool isBlocking; ///< If true, indicates that myConn is blocking.
|
||||
uint32_t crc; ///< Checksum, if any, for usage in the stats.
|
||||
std::string sid; ///< Random identifier used to split connections into sessions
|
||||
uint64_t sessionMode;
|
||||
uint64_t nextKeyTime();
|
||||
|
||||
// stream delaying variables
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutCMAF::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutCMAF::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
// For non-push usage, call usual function.
|
||||
if (!isRecording()){
|
||||
Output::connStats(now, statComm);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Mist{
|
|||
bool isReadyForPlay();
|
||||
|
||||
protected:
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
void onTrackEnd(size_t idx);
|
||||
bool hasSessionIDs(){return !config->getBool("mergesessions");}
|
||||
|
||||
|
@ -72,6 +72,7 @@ namespace Mist{
|
|||
void startPushOut();
|
||||
void pushNext();
|
||||
|
||||
uint32_t crc;
|
||||
HTTP::URL pushUrl;
|
||||
std::map<size_t, CMAFPushTrack> pushTracks;
|
||||
void setupTrackObject(size_t idx);
|
||||
|
|
|
@ -55,7 +55,6 @@ namespace Mist{
|
|||
}
|
||||
|
||||
void HTTPOutput::onFail(const std::string &msg, bool critical){
|
||||
INFO_MSG("Failing '%s': %s", H.url.c_str(), msg.c_str());
|
||||
if (!webSock && !isRecording() && !responded){
|
||||
H.Clean(); // make sure no parts of old requests are left in any buffers
|
||||
H.SetHeader("Server", APPIDENT);
|
||||
|
@ -238,18 +237,6 @@ namespace Mist{
|
|||
}
|
||||
/*LTS-END*/
|
||||
if (H.hasHeader("User-Agent")){UA = H.GetHeader("User-Agent");}
|
||||
if (hasSessionIDs()){
|
||||
if (H.GetVar("sessId").size()){
|
||||
std::string ua = H.GetVar("sessId");
|
||||
crc = checksum::crc32(0, ua.data(), ua.size());
|
||||
}else{
|
||||
std::string ua = JSON::Value(getpid()).asString();
|
||||
crc = checksum::crc32(0, ua.data(), ua.size());
|
||||
}
|
||||
}else{
|
||||
std::string mixed_ua = UA + H.GetHeader("X-Playback-Session-Id");
|
||||
crc = checksum::crc32(0, mixed_ua.data(), mixed_ua.size());
|
||||
}
|
||||
|
||||
if (H.GetVar("audio") != ""){targetParams["audio"] = H.GetVar("audio");}
|
||||
if (H.GetVar("video") != ""){targetParams["video"] = H.GetVar("video");}
|
||||
|
@ -281,6 +268,21 @@ namespace Mist{
|
|||
realTime = 0;
|
||||
}
|
||||
}
|
||||
// Get session ID cookie or generate a random one if it wasn't set
|
||||
if (!sid.size()){
|
||||
std::map<std::string, std::string> storage;
|
||||
const std::string koekjes = H.GetHeader("Cookie");
|
||||
HTTP::parseVars(koekjes, storage);
|
||||
if (storage.count("sid")){
|
||||
// Get sid cookie, which is used to divide connections into sessions
|
||||
sid = storage.at("sid");
|
||||
}else{
|
||||
// Else generate one
|
||||
const std::string newSid = UA + JSON::Value(getpid()).asString();
|
||||
sid = JSON::Value(checksum::crc32(0, newSid.data(), newSid.size())).asString();
|
||||
H.SetHeader("sid", sid.c_str());
|
||||
}
|
||||
}
|
||||
// Handle upgrade to websocket if the output supports it
|
||||
std::string upgradeHeader = H.GetHeader("Upgrade");
|
||||
Util::stringToLower(upgradeHeader);
|
||||
|
|
|
@ -344,7 +344,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutTSSRT::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutTSSRT::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
if (!srtConn){return;}
|
||||
statComm.setUp(srtConn.dataUp());
|
||||
statComm.setDown(srtConn.dataDown());
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Mist{
|
|||
bool isReadyForPlay(){return true;}
|
||||
virtual void requestHandler();
|
||||
protected:
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
virtual std::string getConnectedHost(){return srtConn.remotehost;}
|
||||
virtual std::string getConnectedBinHost(){return srtConn.getBinHost();}
|
||||
|
||||
|
|
|
@ -1015,7 +1015,7 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
void OutWebRTC::connStats(uint64_t now, Comms::Statistics &statComm){
|
||||
void OutWebRTC::connStats(uint64_t now, Comms::Connections &statComm){
|
||||
statComm.setUp(myConn.dataUp());
|
||||
statComm.setDown(myConn.dataDown());
|
||||
statComm.setPacketCount(totalPkts);
|
||||
|
|
|
@ -144,7 +144,7 @@ namespace Mist{
|
|||
void onDTSCConverterHasInitData(const size_t trackID, const std::string &initData);
|
||||
void onRTPPacketizerHasRTPPacket(const char *data, size_t nbytes);
|
||||
void onRTPPacketizerHasRTCPPacket(const char *data, uint32_t nbytes);
|
||||
virtual void connStats(uint64_t now, Comms::Statistics &statComm);
|
||||
virtual void connStats(uint64_t now, Comms::Connections &statComm);
|
||||
|
||||
private:
|
||||
uint64_t lastRecv;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue