winlin

refine plan, add stream start time for atc

... ... @@ -325,6 +325,56 @@ class RESTSessions(object):
# TODO: process the on_stop event
return code
# the rest dvrs, when dvr got keyframe, call this api.
class RESTDvrs(object):
exposed = True
def __init__(self):
pass
# the dvrs POST api specified in following.
#
# when dvr got an keyframe, call the hook,
# the request in the POST data string is a object encode by json:
# {
# "action": "on_dvr_keyframe",
# "vhost": "video.test.com", "app": "live",
# "stream": "livestream"
# }
#
# if valid, the hook must return HTTP code 200(Stauts OK) and response
# an int value specifies the error code(0 corresponding to success):
# 0
def POST(self):
enable_crossdomain()
req = cherrypy.request.body.read()
trace("post to sessions, req=%s"%(req))
try:
json_req = json.loads(req)
except Exception, ex:
code = Error.system_parse_json
trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code))
return str(code)
action = json_req["action"]
if action == "on_dvr_keyframe":
code = self.__on_dvr_keyframe(json_req)
else:
code = Errors.request_invalid_action
trace("invalid request action: %s, code=%s"%(json_req["action"], code))
return str(code)
def __on_dvr_keyframe(self, req):
code = Error.success
trace("srs %s: vhost=%s, app=%s, stream=%s"%(
req["action"], req["vhost"], req["app"], req["stream"]
))
# TODO: process the on_dvr_keyframe event
return code
global_arm_server_id = os.getpid();
class ArmServer:
... ... @@ -930,6 +980,7 @@ class V1(object):
self.clients = RESTClients()
self.streams = RESTStreams()
self.sessions = RESTSessions()
self.dvrs = RESTDvrs()
self.chats = RESTChats()
self.servers = RESTServers()
self.nodes = RESTNodes()
... ...
... ... @@ -303,10 +303,12 @@ SrsFlvSegment::SrsFlvSegment()
segment_has_keyframe = false;
duration = 0;
starttime = -1;
stream_starttime = 0;
}
void SrsFlvSegment::reset()
{
segment_has_keyframe = false;
duration = 0;
starttime = -1;
}
... ... @@ -319,7 +321,7 @@ SrsDvrPlan::SrsDvrPlan()
dvr_enabled = false;
fs = new SrsFileStream();
enc = new SrsFlvEncoder();
segment = NULL;
segment = new SrsFlvSegment();
}
SrsDvrPlan::~SrsDvrPlan()
... ... @@ -362,6 +364,22 @@ int SrsDvrPlan::on_publish()
// always update time cache.
srs_update_system_time_ms();
// when republish, stream starting.
segment->stream_starttime = srs_get_system_time_ms();
if ((ret = open_new_segment()) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsDvrPlan::open_new_segment()
{
int ret = ERROR_SUCCESS;
SrsRequest* req = _req;
// new flv file
std::stringstream path;
... ... @@ -468,8 +486,7 @@ int SrsDvrPlan::flv_open(string stream, string path)
{
int ret = ERROR_SUCCESS;
srs_freep(segment);
segment = new SrsFlvSegment();
segment->reset();
std::string tmp_file = path + ".tmp";
if ((ret = fs->open(tmp_file)) != ERROR_SUCCESS) {
... ... @@ -548,7 +565,7 @@ int SrsDvrPlan::on_dvr_keyframe()
for (int i = 0; i < (int)on_dvr_keyframe->args.size(); i++) {
std::string url = on_dvr_keyframe->args.at(i);
SrsHttpHooks::on_dvr_keyframe(url, _req);
SrsHttpHooks::on_dvr_keyframe(url, _req, segment);
}
#endif
... ... @@ -620,6 +637,9 @@ int SrsDvrSegmentPlan::on_publish()
int ret = ERROR_SUCCESS;
// if already opened, continue to dvr.
// the segment plan maybe keep running longer than the encoder.
// for example, segment running, encoder restart,
// the segment plan will just continue going and donot open new segment.
if (fs->is_open()) {
dvr_enabled = true;
return ret;
... ... @@ -648,15 +668,14 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
srs_assert(segment);
// reap if exceed duration.
if (segment->duration > 0 && segment_duration > 0 && segment->duration > segment_duration) {
segment->reset();
if (segment_duration > 0 && segment->duration > segment_duration) {
if ((ret = flv_close()) != ERROR_SUCCESS) {
segment->reset();
return ret;
}
on_unpublish();
if ((ret = on_publish()) != ERROR_SUCCESS) {
if ((ret = open_new_segment()) != ERROR_SUCCESS) {
return ret;
}
}
... ...
... ... @@ -121,11 +121,15 @@ public:
* whether current segment has keyframe.
*/
bool segment_has_keyframe;
/**
* current segment duration and starttime.
*/
/**
* current segment duration and starttime.
*/
int64_t duration;
int64_t starttime;
/**
* stream start time, to generate atc pts.
*/
int64_t stream_starttime;
public:
SrsFlvSegment();
virtual void reset();
... ... @@ -137,6 +141,7 @@ public:
* 1. filename: the filename for record file.
* 2. reap flv: when to reap the flv and start new piece.
*/
// TODO: FIXME: the plan is too fat, refine me.
class SrsDvrPlan
{
protected:
... ... @@ -165,12 +170,13 @@ public:
protected:
virtual int flv_open(std::string stream, std::string path);
virtual int flv_close();
virtual int open_new_segment();
virtual int update_duration(SrsSharedPtrMessage* msg);
private:
/**
* when srs reap the flv(close the segment),
* if has keyframe, notice the api.
*/
/**
* when srs reap the flv(close the segment),
* if has keyframe, notice the api.
*/
virtual int on_dvr_keyframe();
public:
static SrsDvrPlan* create_plan(std::string vhost);
... ...
... ... @@ -36,6 +36,7 @@ using namespace std;
#include <srs_app_socket.hpp>
#include <srs_app_http.hpp>
#include <srs_app_json.hpp>
#include <srs_app_dvr.hpp>
#define SRS_HTTP_RESPONSE_OK "0"
... ... @@ -194,14 +195,6 @@ int SrsHttpHooks::on_connect(string url, int client_id, string ip, SrsRequest* r
return ret;
}
/**
{
"action": "on_connect",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"pageUrl": "http://www.test.com/live.html"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_connect") << JFIELD_CONT
... ... @@ -247,14 +240,6 @@ void SrsHttpHooks::on_close(string url, int client_id, string ip, SrsRequest* re
return;
}
/**
{
"action": "on_close",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_close") << JFIELD_CONT
... ... @@ -300,14 +285,6 @@ int SrsHttpHooks::on_publish(string url, int client_id, string ip, SrsRequest* r
return ret;
}
/**
{
"action": "on_publish",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_publish") << JFIELD_CONT
... ... @@ -354,14 +331,6 @@ void SrsHttpHooks::on_unpublish(string url, int client_id, string ip, SrsRequest
return;
}
/**
{
"action": "on_unpublish",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_unpublish") << JFIELD_CONT
... ... @@ -408,14 +377,6 @@ int SrsHttpHooks::on_play(string url, int client_id, string ip, SrsRequest* req)
return ret;
}
/**
{
"action": "on_play",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_play") << JFIELD_CONT
... ... @@ -462,14 +423,6 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req
return;
}
/**
{
"action": "on_stop",
"client_id": 1985,
"ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_stop") << JFIELD_CONT
... ... @@ -505,10 +458,15 @@ void SrsHttpHooks::on_stop(string url, int client_id, string ip, SrsRequest* req
return;
}
void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req)
void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* segment)
{
int ret = ERROR_SUCCESS;
srs_trace("flv segment %s, atc_start=%"PRId64", "
"has_key=%d, starttime=%"PRId64", duration=%d",
segment->current_flv_path.c_str(), segment->stream_starttime,
segment->segment_has_keyframe, segment->starttime, (int)segment->duration);
SrsHttpUri uri;
if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {
srs_warn("http uri parse on_dvr_keyframe url failed, ignored. "
... ... @@ -516,13 +474,6 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req)
return;
}
/**
{
"action": "on_dvr_keyframe",
"vhost": "video.test.com", "app": "live",
"stream": "livestream"
}
*/
std::stringstream ss;
ss << JOBJECT_START
<< JFIELD_STR("action", "on_dvr_keyframe") << JFIELD_CONT
... ...
... ... @@ -37,6 +37,7 @@ class SrsHttpUri;
class SrsSocket;
class SrsRequest;
class SrsHttpParser;
class SrsFlvSegment;
#include <srs_app_st.hpp>
... ... @@ -126,8 +127,9 @@ public:
* on_dvr_keyframe hook, when dvr get keyframe.
* @param url the api server url, to process the event.
* ignore if empty.
* @param segment the current flv segment.
*/
static void on_dvr_keyframe(std::string url, SrsRequest* req);
static void on_dvr_keyframe(std::string url, SrsRequest* req, SrsFlvSegment* segment);
};
#endif
... ...