mistserver/src/player.cpp

207 lines
6.7 KiB
C++

/// \file player.cpp
/// Holds all code for the MistPlayer application used for VoD streams.
#if DEBUG >= 4
#include <iostream>//for std::cerr
#endif
#include <stdio.h> //for fileno
#include <sys/time.h>
#include <mist/dtsc.h>
#include <mist/json.h>
#include <mist/config.h>
#include <mist/socket.h>
/// Copy of stats from buffer_user.cpp
class Stats{
public:
unsigned int up;
unsigned int down;
std::string host;
std::string connector;
unsigned int conntime;
Stats(){
up = 0; down = 0; conntime = 0;
};
/// Reads a stats string and parses it to the internal representation.
Stats(std::string s){
Buffer::Stats::Stats(std::string s){
size_t f = s.find(' ');
if (f != std::string::npos){
host = s.substr(0, f);
s.erase(0, f+1);
}
f = s.find(' ');
if (f != std::string::npos){
connector = s.substr(0, f);
s.erase(0, f+1);
}
f = s.find(' ');
if (f != std::string::npos){
conntime = atoi(s.substr(0, f).c_str());
s.erase(0, f+1);
}
f = s.find(' ');
if (f != std::string::npos){
up = atoi(s.substr(0, f).c_str());
s.erase(0, f+1);
down = atoi(s.c_str());
}
};
};
/// Gets the current system time in milliseconds.
long long int getNowMS(){
timeval t;
gettimeofday(&t, 0);
return t.tv_sec * 1000 + t.tv_usec/1000;
}//getNowMS
int main(int argc, char** argv){
Util::Config conf(argv[0], PACKAGE_VERSION);
conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"help\":\"Name of the file to write to stdout.\"}"));
conf.parseArgs(argc, argv);
conf.activate();
int playing = 0;
DTSC::File source = DTSC::File(conf.getString("filename"));
Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin));
std::string meta_str = source.getHeader();
JSON::Value pausemark;
pausemark["datatype"] = "pause_marker";
pausemark["time"] = (long long int)0;
Socket::Connection StatsSocket = Socket::Connection("/tmp/mist/statistics", true);
//send the header
{
in_out.Send("DTSC");
unsigned int size = htonl(meta_str.size());
in_out.Send((char*)&size, 4);
in_out.Send(meta_str);
}
JSON::Value meta = JSON::fromDTMI(meta_str);
JSON::Value last_pack;
long long now, timeDiff = 0, lastTime = 0;
Stats sts;
while (in_out.connected()){
if (in_out.spool()){
while (in_out.Received().find('\n') != std::string::npos){
std::string cmd = in_out.Received().substr(0, in_out.Received().find('\n'));
in_out.Received().erase(0, in_out.Received().find('\n')+1);
if (cmd != ""){
switch (cmd[0]){
case 'P':{ //Push
#if DEBUG >= 4
std::cerr << "Received push - ignoring (" << cmd << ")" << std::endl;
#endif
in_out.close();//pushing to VoD makes no sense
} break;
case 'S':{ //Stats
if (!StatsSocket.connected()){
StatsSocket = Socket::Connection("/tmp/mist/statistics", true);
}
if (StatsSocket.connected()){
sts = Stats(cmd.substr(2));
JSON::Value json_sts;
json_sts["vod"]["down"] = (long long int)sts.down;
json_sts["vod"]["up"] = (long long int)sts.up;
json_sts["vod"]["time"] = (long long int)sts.conntime;
json_sts["vod"]["host"] = sts.host;
json_sts["vod"]["connector"] = sts.connector;
json_sts["vod"]["filename"] = conf.getString("filename");
json_sts["vod"]["now"] = (long long int)time(0);
json_sts["vod"]["meta"] = meta;
StatsSocket.Send(json_sys.toString());
StatsSocket.Send("\n\n");
StatsSocket.flush();
}
} break;
case 's':{ //second-seek
#if DEBUG >= 4
std::cerr << "Received ms-seek (" << cmd << ")" << std::endl;
#endif
int ms = JSON::Value(cmd.substr(2)).asInt();
bool ret = source.seek_time(ms);
#if DEBUG >= 4
std::cerr << "Second-seek completed (time " << ms << "ms) " << ret << std::endl;
#endif
} break;
case 'f':{ //frame-seek
#if DEBUG >= 4
std::cerr << "Received frame-seek (" << cmd << ")" << std::endl;
#endif
bool ret = source.seek_frame(JSON::Value(cmd.substr(2)).asInt());
#if DEBUG >= 4
std::cerr << "Frame-seek completed " << ret << std::endl;
#endif
} break;
case 'p':{ //play
#if DEBUG >= 4
std::cerr << "Received play" << std::endl;
#endif
playing = -1;
in_out.setBlocking(false);
} break;
case 'o':{ //once-play
#if DEBUG >= 4
std::cerr << "Received once-play" << std::endl;
#endif
if (playing <= 0){playing = 1;}
++playing;
in_out.setBlocking(false);
} break;
case 'q':{ //quit-playing
#if DEBUG >= 4
std::cerr << "Received quit-playing" << std::endl;
#endif
playing = 0;
in_out.setBlocking(true);
} break;
}
}
}
}
if (playing != 0){
now = getNowMS();
if (playing > 0 || now - timeDiff >= lastTime || lastTime - (now - timeDiff) > 15000) {
source.seekNext();
lastTime = source.getJSON()["time"].asInt();
if ((now - timeDiff - lastTime) > 5000 || (now - timeDiff - lastTime < -5000)){
timeDiff = now - lastTime;
}
if (source.getJSON().isMember("keyframe")){
if (playing > 0){--playing;}
if (playing == 0){
#if DEBUG >= 4
std::cerr << "Sending pause_marker" << std::endl;
#endif
pausemark["time"] = (long long int)now;
pausemark.toPacked();
in_out.Send(pausemark.toNetPacked());
in_out.flush();
in_out.setBlocking(true);
}
}
if (playing != 0){
//insert proper header for this type of data
in_out.Send("DTPD");
//insert the packet length
unsigned int size = htonl(source.getPacket().size());
in_out.Send((char*)&size, 4);
in_out.Send(source.getPacket());
}
} else {
usleep(std::min(10000LL, lastTime - (now - timeDiff)) * 1000);
}
}
usleep(10000);//sleep 10ms
}
StatsSocket.close();
return 0;
}