mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
fix #179, support dvr http api. 2.0.123.
This commit is contained in:
parent
691f732204
commit
a3648063d5
5 changed files with 157 additions and 0 deletions
|
@ -532,6 +532,7 @@ Supported operating systems and hardware:
|
||||||
|
|
||||||
### SRS 2.0 history
|
### SRS 2.0 history
|
||||||
|
|
||||||
|
* v2.0, 2015-02-24, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), support dvr http api. 2.0.123.
|
||||||
* v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122.
|
* v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122.
|
||||||
* v2.0, 2015-02-19, refine pithy print to more easyer to use. 2.0.121.
|
* v2.0, 2015-02-19, refine pithy print to more easyer to use. 2.0.121.
|
||||||
* v2.0, 2015-02-18, fix [#133](https://github.com/winlinvip/simple-rtmp-server/issues/133), support push rtsp to srs. 2.0.120.
|
* v2.0, 2015-02-18, fix [#133](https://github.com/winlinvip/simple-rtmp-server/issues/133), support push rtsp to srs. 2.0.120.
|
||||||
|
|
|
@ -314,6 +314,9 @@ 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}
|
||||||
|
# method=PUT, use as RPC(remote process call).
|
||||||
|
# reap_segment, the request params in json, where:
|
||||||
|
# {action:"reap_segment", vhost:"__defaultVhost", path_tmpl:"./[15].[04].[05].[999].flv"}
|
||||||
# when reap segment, the callback POST request in json:
|
# when reap segment, the callback POST request in json:
|
||||||
# {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
|
# {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
|
||||||
# app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
|
# app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
|
||||||
|
@ -322,6 +325,7 @@ vhost dvr.srs.com {
|
||||||
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
|
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
|
||||||
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
|
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
|
||||||
# default: session
|
# default: session
|
||||||
|
# TODO: FIXME: update wiki for the api plan.
|
||||||
dvr_plan session;
|
dvr_plan session;
|
||||||
# the dvr output path.
|
# the dvr output path.
|
||||||
# we supports some variables to generate the filename.
|
# we supports some variables to generate the filename.
|
||||||
|
|
|
@ -48,6 +48,9 @@ using namespace std;
|
||||||
// the sleep interval for http async callback.
|
// the sleep interval for http async callback.
|
||||||
#define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000
|
#define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000
|
||||||
|
|
||||||
|
// the use raction for dvr rpc.
|
||||||
|
#define SRS_DVR_USER_ACTION_REAP_SEGMENT "reap_segment"
|
||||||
|
|
||||||
SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p)
|
SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p)
|
||||||
{
|
{
|
||||||
req = NULL;
|
req = NULL;
|
||||||
|
@ -1003,6 +1006,10 @@ int SrsDvrApiPlan::on_video(SrsSharedPtrMessage* __video)
|
||||||
sh_video = __video->copy();
|
sh_video = __video->copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((ret = check_user_actions(__video)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
|
if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1108,29 @@ int SrsDvrApiPlan::stop()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SrsDvrApiPlan::rpc(SrsJsonObject* obj)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
SrsJsonAny* prop = NULL;
|
||||||
|
if ((prop = obj->ensure_property_string("action")) == NULL) {
|
||||||
|
ret = ERROR_HTTP_DVR_REQUEST;
|
||||||
|
srs_error("dvr: rpc required action request. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
action = prop->to_str();
|
||||||
|
if (action == SRS_DVR_USER_ACTION_REAP_SEGMENT) {
|
||||||
|
if ((prop = obj->ensure_property_string("path_tmpl")) != NULL) {
|
||||||
|
path_template = prop->to_str();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = ERROR_HTTP_DVR_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int SrsDvrApiPlan::on_reap_segment()
|
int SrsDvrApiPlan::on_reap_segment()
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
@ -1116,6 +1146,64 @@ int SrsDvrApiPlan::on_reap_segment()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SrsDvrApiPlan::check_user_actions(SrsSharedPtrMessage* msg)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
srs_assert(segment);
|
||||||
|
|
||||||
|
if (action == SRS_DVR_USER_ACTION_REAP_SEGMENT) {
|
||||||
|
// when wait keyframe, ignore if no frame arrived.
|
||||||
|
// @see https://github.com/winlinvip/simple-rtmp-server/issues/177
|
||||||
|
if (_srs_config->get_dvr_wait_keyframe(req->vhost)) {
|
||||||
|
if (!msg->is_video()) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* payload = msg->payload;
|
||||||
|
int size = msg->size;
|
||||||
|
bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size)
|
||||||
|
&& SrsFlvCodec::video_is_keyframe(payload, size)
|
||||||
|
&& !SrsFlvCodec::video_is_sequence_header(payload, size);
|
||||||
|
if (!is_key_frame) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reap segment
|
||||||
|
if ((ret = segment->close()) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use new path template if user specified.
|
||||||
|
if (!path_template.empty() && (ret = set_path_tmpl(path_template)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open new flv file
|
||||||
|
if ((ret = segment->open()) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset rcp params.
|
||||||
|
action = "";
|
||||||
|
path_template = "";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
SrsDvrAppendPlan::SrsDvrAppendPlan()
|
SrsDvrAppendPlan::SrsDvrAppendPlan()
|
||||||
{
|
{
|
||||||
last_update_time = 0;
|
last_update_time = 0;
|
||||||
|
@ -1506,6 +1594,46 @@ int SrsApiDvrPool::stop(string vhost)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SrsApiDvrPool::rpc(SrsJsonAny* json)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!json->is_object()) {
|
||||||
|
ret = ERROR_HTTP_DVR_REQUEST;
|
||||||
|
srs_error("dvr: rpc required object request. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsJsonObject* obj = json->to_object();
|
||||||
|
|
||||||
|
SrsJsonAny* prop = NULL;
|
||||||
|
if ((prop = obj->ensure_property_string("vhost")) == NULL) {
|
||||||
|
ret = ERROR_HTTP_DVR_REQUEST;
|
||||||
|
srs_error("dvr: rpc required vhost request. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
std::string vhost = prop->to_str();
|
||||||
|
|
||||||
|
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->rpc(obj)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
SrsDvr::SrsDvr(SrsSource* s)
|
SrsDvr::SrsDvr(SrsSource* s)
|
||||||
{
|
{
|
||||||
source = s;
|
source = s;
|
||||||
|
|
|
@ -44,6 +44,7 @@ class SrsFileWriter;
|
||||||
class SrsFlvEncoder;
|
class SrsFlvEncoder;
|
||||||
class SrsDvrPlan;
|
class SrsDvrPlan;
|
||||||
class SrsJsonAny;
|
class SrsJsonAny;
|
||||||
|
class SrsJsonObject;
|
||||||
class SrsThread;
|
class SrsThread;
|
||||||
|
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
|
@ -305,6 +306,10 @@ private:
|
||||||
std::string callback;
|
std::string callback;
|
||||||
bool autostart;
|
bool autostart;
|
||||||
bool started;
|
bool started;
|
||||||
|
private:
|
||||||
|
// user action, reap_segment.
|
||||||
|
std::string action;
|
||||||
|
std::string path_template;
|
||||||
public:
|
public:
|
||||||
SrsDvrApiPlan();
|
SrsDvrApiPlan();
|
||||||
virtual ~SrsDvrApiPlan();
|
virtual ~SrsDvrApiPlan();
|
||||||
|
@ -322,8 +327,11 @@ public:
|
||||||
virtual int start();
|
virtual int start();
|
||||||
virtual int dumps(std::stringstream& ss);
|
virtual int dumps(std::stringstream& ss);
|
||||||
virtual int stop();
|
virtual int stop();
|
||||||
|
virtual int rpc(SrsJsonObject* obj);
|
||||||
protected:
|
protected:
|
||||||
virtual int on_reap_segment();
|
virtual int on_reap_segment();
|
||||||
|
private:
|
||||||
|
virtual int check_user_actions(SrsSharedPtrMessage* msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -389,6 +397,7 @@ 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);
|
virtual int stop(std::string vhost);
|
||||||
|
virtual int rpc(SrsJsonAny* json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -516,6 +516,21 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
|
||||||
} else if (r->is_http_delete()) {
|
} else if (r->is_http_delete()) {
|
||||||
int ret = pool->stop(r->query_get("vhost"));
|
int ret = pool->stop(r->query_get("vhost"));
|
||||||
|
|
||||||
|
ss << __SRS_JOBJECT_START
|
||||||
|
<< __SRS_JFIELD_ERROR(ret)
|
||||||
|
<< __SRS_JOBJECT_END;
|
||||||
|
} else if (r->is_http_put()) {
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::string body = r->body();
|
||||||
|
SrsJsonAny* json = SrsJsonAny::loads((char*)body.c_str());
|
||||||
|
if (!json) {
|
||||||
|
ret = ERROR_HTTP_JSON_REQUIRED;
|
||||||
|
} else {
|
||||||
|
SrsAutoFree(SrsJsonAny, json);
|
||||||
|
ret = pool->rpc(json);
|
||||||
|
}
|
||||||
|
|
||||||
ss << __SRS_JOBJECT_START
|
ss << __SRS_JOBJECT_START
|
||||||
<< __SRS_JFIELD_ERROR(ret)
|
<< __SRS_JFIELD_ERROR(ret)
|
||||||
<< __SRS_JOBJECT_END;
|
<< __SRS_JOBJECT_END;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue