diff --git a/trunk/research/api-server/server.py b/trunk/research/api-server/server.py index 2c3e142e5..3eab0c92e 100755 --- a/trunk/research/api-server/server.py +++ b/trunk/research/api-server/server.py @@ -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() diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 5c6100b68..34d21218f 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -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; } } diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index 0736a3813..66615bcd4 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -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); diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index be196e9dc..4d327bd7f 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -36,6 +36,7 @@ using namespace std; #include #include #include +#include #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 diff --git a/trunk/src/app/srs_app_http_hooks.hpp b/trunk/src/app/srs_app_http_hooks.hpp index a748c3c23..c51e51a80 100644 --- a/trunk/src/app/srs_app_http_hooks.hpp +++ b/trunk/src/app/srs_app_http_hooks.hpp @@ -37,6 +37,7 @@ class SrsHttpUri; class SrsSocket; class SrsRequest; class SrsHttpParser; +class SrsFlvSegment; #include @@ -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