222 lines
6.2 KiB
C++
222 lines
6.2 KiB
C++
#include "output_json.h"
|
|
#include <mist/stream.h>
|
|
#include <iomanip>
|
|
|
|
namespace Mist {
|
|
OutJSON::OutJSON(Socket::Connection & conn) : HTTPOutput(conn){
|
|
realTime = 0;
|
|
bootMsOffset = 0;
|
|
keepReselecting = false;
|
|
dupcheck = false;
|
|
noReceive = false;
|
|
}
|
|
|
|
void OutJSON::init(Util::Config * cfg){
|
|
HTTPOutput::init(cfg);
|
|
capa["name"] = "JSON";
|
|
capa["desc"] = "Enables HTTP protocol JSON streaming.";
|
|
capa["url_match"] = "/$.json";
|
|
capa["codecs"][0u][0u].append("@+meta");
|
|
capa["methods"][0u]["handler"] = "http";
|
|
capa["methods"][0u]["type"] = "html5/text/javascript";
|
|
capa["methods"][0u]["priority"] = 0ll;
|
|
capa["methods"][0u]["url_rel"] = "/$.json";
|
|
capa["methods"][1u]["handler"] = "ws";
|
|
capa["methods"][1u]["type"] = "html5/text/javascript";
|
|
capa["methods"][1u]["priority"] = 0ll;
|
|
capa["methods"][1u]["url_rel"] = "/$.json";
|
|
}
|
|
|
|
void OutJSON::sendNext(){
|
|
JSON::Value jPack = thisPacket.toJSON();
|
|
if (dupcheck){
|
|
if (jPack.compareExcept(lastVal, nodup)){
|
|
return;//skip duplicates
|
|
}
|
|
lastVal = jPack;
|
|
}
|
|
if (webSock){
|
|
webSock->sendFrame(jPack.toString());
|
|
return;
|
|
}
|
|
if (!jsonp.size()){
|
|
if(!first) {
|
|
myConn.SendNow(", ", 2);
|
|
}else{
|
|
myConn.SendNow("[", 1);
|
|
first = false;
|
|
}
|
|
}else{
|
|
myConn.SendNow(jsonp + "(");
|
|
}
|
|
myConn.SendNow(jPack.toString());
|
|
if (jsonp.size()){
|
|
myConn.SendNow(");\n", 3);
|
|
}
|
|
}
|
|
|
|
void OutJSON::sendHeader(){
|
|
std::string method = H.method;
|
|
H.Clean();
|
|
H.SetHeader("Content-Type", "text/javascript");
|
|
H.protocol = "HTTP/1.0";
|
|
H.setCORSHeaders();
|
|
H.SendResponse("200", "OK", myConn);
|
|
sentHeader = true;
|
|
}
|
|
|
|
void OutJSON::onFail(){
|
|
//Only run failure handle if we're not being persistent
|
|
if (!keepReselecting){
|
|
HTTPOutput::onFail();
|
|
}else{
|
|
onFinish();
|
|
}
|
|
}
|
|
|
|
bool OutJSON::onFinish(){
|
|
static bool recursive = false;
|
|
if (recursive){return true;}
|
|
recursive = true;
|
|
if (keepReselecting && !isPushing()){
|
|
uint64_t maxTimer = 7200;
|
|
while (--maxTimer && nProxy.userClient.isAlive() && keepGoing()){
|
|
Util::wait(500);
|
|
stats();
|
|
if (Util::getStreamStatus(streamName) != STRMSTAT_READY){
|
|
disconnect();
|
|
}else{
|
|
updateMeta();
|
|
if (isReadyForPlay()){
|
|
recursive = false;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
recursive = false;
|
|
}
|
|
if (!jsonp.size() && !first){
|
|
myConn.SendNow("]\n", 2);
|
|
}
|
|
myConn.close();
|
|
return false;
|
|
}
|
|
|
|
void OutJSON::onWebsocketConnect(){
|
|
sentHeader = true;
|
|
parseData = !noReceive;
|
|
}
|
|
|
|
void OutJSON::preWebsocketConnect(){
|
|
if (H.GetVar("password") != ""){pushPass = H.GetVar("password");}
|
|
if (H.GetVar("password").size() || H.GetVar("push").size()){noReceive = true;}
|
|
|
|
if (H.GetVar("persist") != ""){keepReselecting = true;}
|
|
if (H.GetVar("dedupe") != ""){
|
|
dupcheck = true;
|
|
size_t index;
|
|
std::string dupes = H.GetVar("dedupe");
|
|
while (dupes != "") {
|
|
index = dupes.find(',');
|
|
nodup.insert(dupes.substr(0, index));
|
|
if (index != std::string::npos) {
|
|
dupes.erase(0, index + 1);
|
|
} else {
|
|
dupes = "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OutJSON::onWebsocketFrame(){
|
|
if (!isPushing()){
|
|
if (!allowPush(pushPass)){
|
|
onFinish();
|
|
return;
|
|
}
|
|
}
|
|
if (!bootMsOffset){
|
|
if (myMeta.bootMsOffset){
|
|
bootMsOffset = myMeta.bootMsOffset;
|
|
}else{
|
|
bootMsOffset = Util::bootMS();
|
|
}
|
|
}
|
|
//We now know we're allowed to push. Read a JSON object.
|
|
JSON::Value inJSON = JSON::fromString(webSock->data, webSock->data.size());
|
|
if (!inJSON || !inJSON.isObject()){
|
|
//Ignore empty and/or non-parsable JSON packets
|
|
MEDIUM_MSG("Ignoring non-JSON object: %s", webSock->data);
|
|
return;
|
|
}
|
|
//Let's create a new track for pushing purposes, if needed
|
|
if (!pushTrack){
|
|
pushTrack = 1;
|
|
while (myMeta.tracks.count(pushTrack)){
|
|
++pushTrack;
|
|
}
|
|
}
|
|
myMeta.tracks[pushTrack].type = "meta";
|
|
myMeta.tracks[pushTrack].codec = "JSON";
|
|
//We have a track set correctly. Let's attempt to buffer a frame.
|
|
inJSON["trackid"] = (long long)pushTrack;
|
|
inJSON["datatype"] = "meta";
|
|
lastSendTime = Util::bootMS();
|
|
if (!inJSON.isMember("unix")){
|
|
//Base timestamp on arrival time
|
|
inJSON["time"] = (long long)(lastSendTime - bootMsOffset);
|
|
}else{
|
|
//Base timestamp on unix time
|
|
inJSON["time"] = (long long)((lastSendTime - bootMsOffset) + (Util::epoch() - Util::bootSecs()) * 1000);
|
|
}
|
|
inJSON["bmo"] = (long long)bootMsOffset;
|
|
lastVal = inJSON;
|
|
std::string packedJson = inJSON.toNetPacked();
|
|
DTSC::Packet newPack(packedJson.data(), packedJson.size(), true);
|
|
bufferLivePacket(newPack);
|
|
if (!idleInterval){idleInterval = 100;}
|
|
if (isBlocking){setBlocking(false);}
|
|
}
|
|
|
|
/// Repeats last JSON packet every 5 seconds to keep stream alive.
|
|
void OutJSON::onIdle(){
|
|
if (nProxy.trackState[pushTrack] != FILL_ACC){
|
|
continueNegotiate(pushTrack);
|
|
if (nProxy.trackState[pushTrack] == FILL_ACC){
|
|
idleInterval = 5000;
|
|
}
|
|
return;
|
|
}
|
|
lastVal["time"] = (long long)(lastVal["time"].asInt() + (Util::bootMS() - lastSendTime));
|
|
lastSendTime = Util::bootMS();
|
|
lastVal.netPrepare();
|
|
std::string packedJson = lastVal.toNetPacked();
|
|
DTSC::Packet newPack(packedJson.data(), packedJson.size(), true);
|
|
myMeta.tracks[pushTrack].type = "meta";
|
|
myMeta.tracks[pushTrack].codec = "JSON";
|
|
bufferLivePacket(newPack);
|
|
}
|
|
|
|
void OutJSON::onHTTP(){
|
|
std::string method = H.method;
|
|
preWebsocketConnect();//Not actually a websocket, but we need to do the same checks
|
|
jsonp = "";
|
|
if (H.GetVar("callback") != ""){jsonp = H.GetVar("callback");}
|
|
if (H.GetVar("jsonp") != ""){jsonp = H.GetVar("jsonp");}
|
|
|
|
H.Clean();
|
|
H.setCORSHeaders();
|
|
if(method == "OPTIONS" || method == "HEAD"){
|
|
H.SetHeader("Content-Type", "text/javascript");
|
|
H.protocol = "HTTP/1.0";
|
|
H.SendResponse("200", "OK", myConn);
|
|
H.Clean();
|
|
return;
|
|
}
|
|
first = true;
|
|
parseData = true;
|
|
wantRequest = false;
|
|
}
|
|
|
|
}
|
|
|