1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

for #179, refine dvr, support callback when reap dvr segment.

This commit is contained in:
winlin 2015-02-22 10:45:13 +08:00
parent 849e59b05d
commit 1246989ea9
9 changed files with 281 additions and 50 deletions

View file

@ -297,7 +297,8 @@ vhost dvr.srs.com {
# vhost, query all dvr of this vhost. # vhost, query all dvr of this vhost.
# response in json, where: # response in json, where:
# {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv", # {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv",
# wait_keyframe:true, vhost:"__defaultVhost", callback:"http://dvr/callback" # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs",
# status:"stop"|"start"
# }]} # }]}
# method=POST # method=POST
# to start dvr of specified vhost. # to start dvr of specified vhost.
@ -313,6 +314,8 @@ vhost dvr.srs.com {
# vhost, stop all dvr of this vhost. # vhost, stop all dvr of this vhost.
# response in json, where: # response in json, where:
# {code:0} # {code:0}
# when reap segment, the callback POST request in json:
# {action:"on_dvr_reap_segment"}
# default: session # default: session
dvr_plan session; dvr_plan session;
# the dvr output path. # the dvr output path.

View file

@ -253,7 +253,7 @@ class RESTDvrs(object):
return json.dumps(dvrs) return json.dumps(dvrs)
''' '''
for SRS hook: on_dvr for SRS hook: on_dvr, on_dvr_reap_segment
on_dvr: on_dvr:
when srs reap a dvr file, call the hook, when srs reap a dvr file, call the hook,
the request in the POST data string is a object encode by json: the request in the POST data string is a object encode by json:
@ -265,6 +265,17 @@ class RESTDvrs(object):
"cwd": "/usr/local/srs", "cwd": "/usr/local/srs",
"file": "./objs/nginx/html/live/livestream.1420254068776.flv" "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
} }
on_dvr_reap_segment:
when api dvr specifes the callback when reap flv segment, call the hook,
the request in the POST data string is a object encode by json:
{
"action": "on_dvr_reap_segment",
"client_id": 1985,
"vhost": "video.test.com", "app": "live",
"stream": "livestream",
"cwd": "/usr/local/srs",
"file": "./objs/nginx/html/live/livestream.1420254068776.flv"
}
if valid, the hook must return HTTP code 200(Stauts OK) and response if valid, the hook must return HTTP code 200(Stauts OK) and response
an int value specifies the error code(0 corresponding to success): an int value specifies the error code(0 corresponding to success):
0 0
@ -287,6 +298,8 @@ class RESTDvrs(object):
action = json_req["action"] action = json_req["action"]
if action == "on_dvr": if action == "on_dvr":
code = self.__on_dvr(json_req) code = self.__on_dvr(json_req)
if action == "on_dvr_reap_segment":
code = self.__on_dvr_reap_segment(json_req)
else: else:
trace("invalid request action: %s"%(json_req["action"])) trace("invalid request action: %s"%(json_req["action"]))
code = Error.request_invalid_action code = Error.request_invalid_action
@ -308,6 +321,18 @@ class RESTDvrs(object):
return code return code
def __on_dvr_reap_segment(self, req):
code = Error.success
trace("srs %s: client id=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%(
req["action"], req["client_id"], req["vhost"], req["app"], req["stream"],
req["cwd"], req["file"]
))
# TODO: process the on_dvr event
return code
''' '''
handle the sessions requests: client play/stop stream handle the sessions requests: client play/stop stream
''' '''

View file

@ -3342,6 +3342,13 @@ bool SrsConfig::get_dvr_enabled(string vhost)
return false; return false;
} }
void SrsConfig::set_dvr_enabled(string vhost, bool enabled)
{
SrsConfDirective* conf = create_directive(vhost, "dvr", "enabled");
conf->args.clear();
conf->args.push_back(enabled? "on":"off");
}
string SrsConfig::get_dvr_path(string vhost) string SrsConfig::get_dvr_path(string vhost)
{ {
SrsConfDirective* dvr = get_dvr(vhost); SrsConfDirective* dvr = get_dvr(vhost);

View file

@ -923,6 +923,7 @@ public:
* whether dvr is enabled. * whether dvr is enabled.
*/ */
virtual bool get_dvr_enabled(std::string vhost); virtual bool get_dvr_enabled(std::string vhost);
virtual void set_dvr_enabled(std::string vhost, bool enabled);
/** /**
* get the dvr path, the flv file to save in. * get the dvr path, the flv file to save in.
*/ */

View file

@ -145,14 +145,14 @@ int SrsFlvSegment::open(bool use_tmp_file)
} }
} }
// when exists, donot write flv header.
if (fresh_flv_file) {
// initialize the encoder. // initialize the encoder.
if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) { if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {
srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret); srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret);
return ret; return ret;
} }
// when exists, donot write flv header.
if (fresh_flv_file) {
// write the flv header to writer. // write the flv header to writer.
if ((ret = enc->write_header()) != ERROR_SUCCESS) { if ((ret = enc->write_header()) != ERROR_SUCCESS) {
srs_error("write flv header failed. ret=%d", ret); srs_error("write flv header failed. ret=%d", ret);
@ -195,6 +195,8 @@ int SrsFlvSegment::close()
} }
} }
// TODO: FIXME: the http callback is async, which will trigger thread switch,
// so the on_video maybe invoked during the http callback, and error.
if ((ret = plan->on_reap_segment()) != ERROR_SUCCESS) { if ((ret = plan->on_reap_segment()) != ERROR_SUCCESS) {
srs_error("dvr: notify plan to reap segment failed. ret=%d", ret); srs_error("dvr: notify plan to reap segment failed. ret=%d", ret);
return ret; return ret;
@ -743,10 +745,15 @@ SrsDvrApiPlan::SrsDvrApiPlan()
{ {
autostart = false; autostart = false;
started = false; started = false;
metadata = sh_audio = sh_video = NULL;
} }
SrsDvrApiPlan::~SrsDvrApiPlan() SrsDvrApiPlan::~SrsDvrApiPlan()
{ {
srs_freep(metadata);
srs_freep(sh_audio);
srs_freep(sh_video);
} }
int SrsDvrApiPlan::initialize(SrsSource* s, SrsRequest* r) int SrsDvrApiPlan::initialize(SrsSource* s, SrsRequest* r)
@ -787,6 +794,8 @@ int SrsDvrApiPlan::on_publish()
return ret; return ret;
} }
dvr_enabled = true;
if ((ret = segment->close()) != ERROR_SUCCESS) { if ((ret = segment->close()) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -795,7 +804,16 @@ int SrsDvrApiPlan::on_publish()
return ret; return ret;
} }
dvr_enabled = true; // update sequence header
if (metadata && (ret = SrsDvrPlan::on_meta_data(metadata)) != ERROR_SUCCESS) {
return ret;
}
if (sh_video && (ret = SrsDvrPlan::on_video(sh_video)) != ERROR_SUCCESS) {
return ret;
}
if (sh_audio && (ret = SrsDvrPlan::on_audio(sh_audio)) != ERROR_SUCCESS) {
return ret;
}
return ret; return ret;
} }
@ -804,6 +822,48 @@ void SrsDvrApiPlan::on_unpublish()
{ {
} }
int SrsDvrApiPlan::on_meta_data(SrsSharedPtrMessage* __metadata)
{
int ret = ERROR_SUCCESS;
srs_freep(metadata);
metadata = __metadata->copy();
return ret;
}
int SrsDvrApiPlan::on_audio(SrsSharedPtrMessage* __audio)
{
int ret = ERROR_SUCCESS;
if (SrsFlvCodec::audio_is_sequence_header(__audio->payload, __audio->size)) {
srs_freep(sh_audio);
sh_audio = __audio->copy();
}
if ((ret = SrsDvrPlan::on_audio(__audio)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsDvrApiPlan::on_video(SrsSharedPtrMessage* __video)
{
int ret = ERROR_SUCCESS;
if (SrsFlvCodec::video_is_sequence_header(__video->payload, __video->size)) {
srs_freep(sh_video);
sh_video = __video->copy();
}
if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
int SrsDvrApiPlan::set_path_tmpl(string path_tmpl) int SrsDvrApiPlan::set_path_tmpl(string path_tmpl)
{ {
_srs_config->set_dvr_path(req->vhost, path_tmpl); _srs_config->set_dvr_path(req->vhost, path_tmpl);
@ -830,6 +890,9 @@ int SrsDvrApiPlan::start()
return ret; return ret;
} }
// enable the config.
_srs_config->set_dvr_enabled(req->vhost, true);
// stop dvr // stop dvr
if (dvr_enabled) { if (dvr_enabled) {
// ignore error. // ignore error.
@ -862,16 +925,58 @@ int SrsDvrApiPlan::dumps(stringstream& ss)
<< __SRS_JFIELD_STR("path_dvr", segment->get_path()) << __SRS_JFIELD_CONT << __SRS_JFIELD_STR("path_dvr", segment->get_path()) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_BOOL("wait_keyframe", wait_keyframe) << __SRS_JFIELD_CONT << __SRS_JFIELD_BOOL("wait_keyframe", wait_keyframe) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT << __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("callback", callback) << __SRS_JFIELD_STR("callback", callback) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("status", (dvr_enabled? "start":"stop"))
<< __SRS_JOBJECT_END; << __SRS_JOBJECT_END;
return ret; return ret;
} }
int SrsDvrApiPlan::stop()
{
int ret = ERROR_SUCCESS;
_srs_config->set_dvr_enabled(req->vhost, false);
started = false;
// stop dvr
if (dvr_enabled) {
// ignore error.
int ret = segment->close();
if (ret != ERROR_SUCCESS) {
srs_warn("ignore flv close error. ret=%d", ret);
}
dvr_enabled = false;
}
srs_trace("dvr: stop dvr of vhost=%s", req->vhost.c_str());
return ret;
}
int SrsDvrApiPlan::on_reap_segment() int SrsDvrApiPlan::on_reap_segment()
{ {
// TODO: FIXME: implements it. int ret = ERROR_SUCCESS;
return ERROR_SUCCESS;
#ifdef SRS_AUTO_HTTP_CALLBACK
// HTTP: callback
if (callback.empty()) {
srs_warn("dvr: ignore for callback empty, vhost=%s", req->vhost.c_str());
return ret;
}
int connection_id = _srs_context->get_id();
std::string cwd = _srs_config->cwd();
std::string file = segment->get_path();
std::string url = callback;
if ((ret = SrsHttpHooks::on_dvr_reap_segment(url, connection_id, req, cwd, file)) != ERROR_SUCCESS) {
srs_error("hook client on_dvr_reap_segment failed. url=%s, ret=%d", url.c_str(), ret);
return ret;
}
#endif
return ret;
} }
SrsDvrAppendPlan::SrsDvrAppendPlan() SrsDvrAppendPlan::SrsDvrAppendPlan()
@ -909,30 +1014,30 @@ void SrsDvrAppendPlan::on_unpublish()
{ {
} }
int SrsDvrAppendPlan::on_audio(SrsSharedPtrMessage* audio) int SrsDvrAppendPlan::on_audio(SrsSharedPtrMessage* __audio)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
if ((ret = update_duration(audio)) != ERROR_SUCCESS) { if ((ret = update_duration(__audio)) != ERROR_SUCCESS) {
return ret; return ret;
} }
if ((ret = SrsDvrPlan::on_audio(audio)) != ERROR_SUCCESS) { if ((ret = SrsDvrPlan::on_audio(__audio)) != ERROR_SUCCESS) {
return ret; return ret;
} }
return ret; return ret;
} }
int SrsDvrAppendPlan::on_video(SrsSharedPtrMessage* video) int SrsDvrAppendPlan::on_video(SrsSharedPtrMessage* __video)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
if ((ret = update_duration(video)) != ERROR_SUCCESS) { if ((ret = update_duration(__video)) != ERROR_SUCCESS) {
return ret; return ret;
} }
if ((ret = SrsDvrPlan::on_video(video)) != ERROR_SUCCESS) { if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -1038,40 +1143,40 @@ int SrsDvrSegmentPlan::on_meta_data(SrsSharedPtrMessage* __metadata)
return ret; return ret;
} }
int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio) int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* __audio)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
if (SrsFlvCodec::audio_is_sequence_header(audio->payload, audio->size)) { if (SrsFlvCodec::audio_is_sequence_header(__audio->payload, __audio->size)) {
srs_freep(sh_audio); srs_freep(sh_audio);
sh_audio = audio->copy(); sh_audio = __audio->copy();
} }
if ((ret = update_duration(audio)) != ERROR_SUCCESS) { if ((ret = update_duration(__audio)) != ERROR_SUCCESS) {
return ret; return ret;
} }
if ((ret = SrsDvrPlan::on_audio(audio)) != ERROR_SUCCESS) { if ((ret = SrsDvrPlan::on_audio(__audio)) != ERROR_SUCCESS) {
return ret; return ret;
} }
return ret; return ret;
} }
int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video) int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* __video)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
if (SrsFlvCodec::video_is_sequence_header(video->payload, video->size)) { if (SrsFlvCodec::video_is_sequence_header(__video->payload, __video->size)) {
srs_freep(sh_video); srs_freep(sh_video);
sh_video = video->copy(); sh_video = __video->copy();
} }
if ((ret = update_duration(video)) != ERROR_SUCCESS) { if ((ret = update_duration(__video)) != ERROR_SUCCESS) {
return ret; return ret;
} }
if ((ret = SrsDvrPlan::on_video(video)) != ERROR_SUCCESS) { if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -1240,6 +1345,30 @@ int SrsApiDvrPool::create(SrsJsonAny* json)
return dvr->start(); return dvr->start();
} }
int SrsApiDvrPool::stop(string vhost)
{
int ret = ERROR_SUCCESS;
std::vector<SrsDvrApiPlan*> plans;
for (int i = 0; i < (int)dvrs.size(); i++) {
SrsDvrApiPlan* plan = dvrs.at(i);
if (!vhost.empty() && plan->req->vhost != vhost) {
continue;
}
plans.push_back(plan);
}
for (int i = 0; i < (int)plans.size(); i++) {
SrsDvrApiPlan* plan = plans.at(i);
if ((ret = plan->stop()) != ERROR_SUCCESS) {
return ret;
}
}
return ret;
}
SrsDvr::SrsDvr(SrsSource* s) SrsDvr::SrsDvr(SrsSource* s)
{ {
source = s; source = s;

View file

@ -236,6 +236,11 @@ public:
*/ */
class SrsDvrApiPlan : public SrsDvrPlan class SrsDvrApiPlan : public SrsDvrPlan
{ {
private:
// cache the metadata and sequence header, for new segment maybe opened.
SrsSharedPtrMessage* sh_audio;
SrsSharedPtrMessage* sh_video;
SrsSharedPtrMessage* metadata;
private: private:
std::string callback; std::string callback;
bool autostart; bool autostart;
@ -247,12 +252,16 @@ public:
virtual int initialize(SrsSource* s, SrsRequest* r); virtual int initialize(SrsSource* s, SrsRequest* r);
virtual int on_publish(); virtual int on_publish();
virtual void on_unpublish(); virtual void on_unpublish();
virtual int on_meta_data(SrsSharedPtrMessage* __metadata);
virtual int on_audio(SrsSharedPtrMessage* __audio);
virtual int on_video(SrsSharedPtrMessage* __video);
public: public:
virtual int set_path_tmpl(std::string path_tmpl); virtual int set_path_tmpl(std::string path_tmpl);
virtual int set_callback(std::string value); virtual int set_callback(std::string value);
virtual int set_wait_keyframe(bool wait_keyframe); virtual int set_wait_keyframe(bool wait_keyframe);
virtual int start(); virtual int start();
virtual int dumps(std::stringstream& ss); virtual int dumps(std::stringstream& ss);
virtual int stop();
protected: protected:
virtual int on_reap_segment(); virtual int on_reap_segment();
}; };
@ -270,14 +279,8 @@ public:
public: public:
virtual int on_publish(); virtual int on_publish();
virtual void on_unpublish(); virtual void on_unpublish();
/** virtual int on_audio(SrsSharedPtrMessage* __audio);
* @param audio, directly ptr, copy it if need to save it. virtual int on_video(SrsSharedPtrMessage* __video);
*/
virtual int on_audio(SrsSharedPtrMessage* audio);
/**
* @param video, directly ptr, copy it if need to save it.
*/
virtual int on_video(SrsSharedPtrMessage* video);
private: private:
virtual int update_duration(SrsSharedPtrMessage* msg); virtual int update_duration(SrsSharedPtrMessage* msg);
}; };
@ -300,18 +303,9 @@ public:
virtual int initialize(SrsSource* source, SrsRequest* req); virtual int initialize(SrsSource* source, SrsRequest* req);
virtual int on_publish(); virtual int on_publish();
virtual void on_unpublish(); virtual void on_unpublish();
/**
* when got metadata.
*/
virtual int on_meta_data(SrsSharedPtrMessage* __metadata); virtual int on_meta_data(SrsSharedPtrMessage* __metadata);
/** virtual int on_audio(SrsSharedPtrMessage* __audio);
* @param audio, directly ptr, copy it if need to save it. virtual int on_video(SrsSharedPtrMessage* __video);
*/
virtual int on_audio(SrsSharedPtrMessage* audio);
/**
* @param video, directly ptr, copy it if need to save it.
*/
virtual int on_video(SrsSharedPtrMessage* video);
private: private:
virtual int update_duration(SrsSharedPtrMessage* msg); virtual int update_duration(SrsSharedPtrMessage* msg);
}; };
@ -334,6 +328,7 @@ public:
public: public:
virtual int dumps(std::string vhost, std::stringstream& ss); virtual int dumps(std::string vhost, std::stringstream& ss);
virtual int create(SrsJsonAny* json); virtual int create(SrsJsonAny* json);
virtual int stop(std::string vhost);
}; };
/** /**

View file

@ -501,8 +501,8 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
<< __SRS_JFIELD_ORG("dvrs", data.str()) << __SRS_JFIELD_ORG("dvrs", data.str())
<< __SRS_JOBJECT_END; << __SRS_JOBJECT_END;
} else if (r->is_http_post()) { } else if (r->is_http_post()) {
char* body = (char*)r->body().c_str(); std::string body = r->body();
SrsJsonAny* json = SrsJsonAny::loads(body); SrsJsonAny* json = SrsJsonAny::loads((char*)body.c_str());
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
if (!json) { if (!json) {
ret = ERROR_HTTP_JSON_REQUIRED; ret = ERROR_HTTP_JSON_REQUIRED;
@ -510,6 +510,12 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
SrsAutoFree(SrsJsonAny, json); SrsAutoFree(SrsJsonAny, json);
ret = pool->create(json); ret = pool->create(json);
} }
ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_ERROR(ret)
<< __SRS_JOBJECT_END;
} else if (r->is_http_delete()) {
int ret = pool->stop(r->query_get("vhost"));
ss << __SRS_JOBJECT_START ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_ERROR(ret)
<< __SRS_JOBJECT_END; << __SRS_JOBJECT_END;

View file

@ -436,4 +436,60 @@ int SrsHttpHooks::on_dvr(string url, int client_id, string ip, SrsRequest* req,
return ret; return ret;
} }
int SrsHttpHooks::on_dvr_reap_segment(string url, int client_id, SrsRequest* req, string cwd, string file)
{
int ret = ERROR_SUCCESS;
SrsHttpUri uri;
if ((ret = uri.initialize(url)) != ERROR_SUCCESS) {
srs_error("http uri parse on_dvr_reap_segment url failed, ignored. "
"client_id=%d, url=%s, ret=%d", client_id, url.c_str(), ret);
return ret;
}
std::stringstream ss;
ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_STR("action", "on_dvr_reap_segment") << __SRS_JFIELD_CONT
<< __SRS_JFIELD_ORG("client_id", client_id) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("stream", req->stream) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("cwd", cwd) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("file", file)
<< __SRS_JOBJECT_END;
std::string data = ss.str();
std::string res;
int status_code;
SrsHttpClient http;
if ((ret = http.post(&uri, data, status_code, res)) != ERROR_SUCCESS) {
srs_error("http post on_dvr_reap_segment uri failed, ignored. "
"client_id=%d, url=%s, request=%s, response=%s, ret=%d",
client_id, url.c_str(), data.c_str(), res.c_str(), ret);
return ret;
}
// ensure the http status is ok.
// https://github.com/winlinvip/simple-rtmp-server/issues/158
if (status_code != SRS_CONSTS_HTTP_OK) {
ret = ERROR_HTTP_STATUS_INVLIAD;
srs_error("http hook on_dvr_reap_segment status failed. "
"client_id=%d, code=%d, ret=%d", client_id, status_code, ret);
return ret;
}
if (res.empty() || res != SRS_HTTP_RESPONSE_OK) {
ret = ERROR_HTTP_DATA_INVLIAD;
srs_warn("http hook on_dvr_reap_segment validate failed, ignored. "
"client_id=%d, res=%s, ret=%d", client_id, res.c_str(), ret);
return ret;
}
srs_trace("http hook on_dvr_reap_segment success. "
"client_id=%d, url=%s, request=%s, response=%s, ret=%d",
client_id, url.c_str(), data.c_str(), res.c_str(), ret);
return ret;
}
#endif #endif

View file

@ -104,6 +104,15 @@ public:
* @param file the file path, can be relative or absolute path. * @param file the file path, can be relative or absolute path.
*/ */
static int on_dvr(std::string url, int client_id, std::string ip, SrsRequest* req, std::string cwd, std::string file); static int on_dvr(std::string url, int client_id, std::string ip, SrsRequest* req, std::string cwd, std::string file);
/**
* when dvr reap segment, callback.
* @param client_id the id of client on server.
* @param url the api server url, to process the event.
* ignore if empty.
* @param cwd the current work directory, used to resolve the reltive file path.
* @param file the file path, can be relative or absolute path.
*/
static int on_dvr_reap_segment(std::string url, int client_id, SrsRequest* req, std::string cwd, std::string file);
}; };
#endif #endif