207 lines
6.7 KiB
C++
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;
|
|
}
|