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

for #179, support http api to start dvr.

This commit is contained in:
winlin 2015-02-21 23:09:21 +08:00
parent c67a4fdf97
commit 849e59b05d
9 changed files with 320 additions and 21 deletions

View file

@ -296,15 +296,14 @@ vhost dvr.srs.com {
# request params, for example ?vhost=__defaultVhost__, where: # request params, for example ?vhost=__defaultVhost__, where:
# 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: [{plan:"api", path:"./objs/nginx/html", # {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv",
# autostart:true, wait_keyframe:true, jitter:"full" # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://dvr/callback"
# }]} # }]}
# method=POST # method=POST
# to start dvr of specified vhost. # to start dvr of specified vhost.
# request should encode in json, specifies the dvr to create, where: # request should encode in json, specifies the dvr to create, where:
# {plan:"api", path:"./objs/nginx/html", # {path_tmpl:"./[15].[04].[05].[999].flv",
# autostart:true, wait_keyframe:true, jitter:"full", # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://dvr/callback"
# vhost:"__defaultVhost", callback:"http://dvr/callback"
# } # }
# response in json, where: # response in json, where:
# {code:0} # {code:0}
@ -329,7 +328,7 @@ vhost dvr.srs.com {
# [05], repleace this const to current second. # [05], repleace this const to current second.
# [999], repleace this const to current millisecond. # [999], repleace this const to current millisecond.
# [timestamp],replace this const to current UNIX timestamp in ms. # [timestamp],replace this const to current UNIX timestamp in ms.
# @remark we use golang time format "2006-01-02 15:04:05.999" # @remark we use golang time format "2006-01-02 15:04:05.999" as "[2006]-[01]-[02]_[15].[04].[05]_[999]"
# for example, for url rtmp://ossrs.net/live/livestream and time 2015-01-03 10:57:30.776 # for example, for url rtmp://ossrs.net/live/livestream and time 2015-01-03 10:57:30.776
# 1. No variables, the rule of SRS1.0(auto add [stream].[timestamp].flv as filename): # 1. No variables, the rule of SRS1.0(auto add [stream].[timestamp].flv as filename):
# dvr_path ./objs/nginx/html; # dvr_path ./objs/nginx/html;

View file

@ -1977,6 +1977,38 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* sc)
return ::atoi(conf->arg0().c_str()); return ::atoi(conf->arg0().c_str());
} }
SrsConfDirective* SrsConfig::create_directive(string vhost, string directive, string sub_directive)
{
SrsConfDirective* vhost_conf = get_vhost(vhost);
if (!vhost_conf) {
vhost_conf = new SrsConfDirective();
root->directives.push_back(vhost_conf);
}
if (directive.empty()) {
return vhost_conf;
}
SrsConfDirective* dir = vhost_conf->get(directive);
if (!dir) {
dir = new SrsConfDirective();
vhost_conf->directives.push_back(dir);
}
if (sub_directive.empty()) {
return dir;
}
SrsConfDirective* sdir = dir->get(sub_directive);
if (!sdir) {
sdir = new SrsConfDirective();
dir->directives.push_back(sdir);
}
return sdir;
}
SrsConfDirective* SrsConfig::get_vhost(string vhost) SrsConfDirective* SrsConfig::get_vhost(string vhost)
{ {
srs_assert(root); srs_assert(root);
@ -3327,6 +3359,13 @@ string SrsConfig::get_dvr_path(string vhost)
return conf->arg0(); return conf->arg0();
} }
void SrsConfig::set_dvr_path(string vhost, string path)
{
SrsConfDirective* conf = create_directive(vhost, "dvr", "dvr_path");
conf->args.clear();
conf->args.push_back(path);
}
string SrsConfig::get_dvr_plan(string vhost) string SrsConfig::get_dvr_plan(string vhost)
{ {
SrsConfDirective* dvr = get_dvr(vhost); SrsConfDirective* dvr = get_dvr(vhost);
@ -3378,6 +3417,13 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost)
return false; return false;
} }
void SrsConfig::set_dvr_wait_keyframe(string vhost, bool wait_keyframe)
{
SrsConfDirective* conf = create_directive(vhost, "dvr", "dvr_wait_keyframe");
conf->args.clear();
conf->args.push_back(wait_keyframe? "on":"off");
}
bool SrsConfig::get_dvr_autostart(string vhost) bool SrsConfig::get_dvr_autostart(string vhost)
{ {
SrsConfDirective* dvr = get_dvr(vhost); SrsConfDirective* dvr = get_dvr(vhost);

View file

@ -452,6 +452,14 @@ public:
* get the max udp port for rtp of stream caster rtsp. * get the max udp port for rtp of stream caster rtsp.
*/ */
virtual int get_stream_caster_rtp_port_max(SrsConfDirective* sc); virtual int get_stream_caster_rtp_port_max(SrsConfDirective* sc);
private:
/**
* create directive under vhost.
* @param directive, get the directive of vhost. get vhost if directive is empty.
* @param sub_directive, get the sub directive of vhost. get directive if sub-directive is empty.
* @return the vhost(empty directive and sub-directive); the directive(empty sub-directive); the sub-directive.
*/
virtual SrsConfDirective* create_directive(std::string vhost, std::string directive, std::string sub_directive);
// vhost specified section // vhost specified section
public: public:
/** /**
@ -919,6 +927,7 @@ public:
* get the dvr path, the flv file to save in. * get the dvr path, the flv file to save in.
*/ */
virtual std::string get_dvr_path(std::string vhost); virtual std::string get_dvr_path(std::string vhost);
virtual void set_dvr_path(std::string vhost, std::string path);
/** /**
* get the plan of dvr, how to reap the flv file. * get the plan of dvr, how to reap the flv file.
*/ */
@ -931,6 +940,7 @@ public:
* whether wait keyframe to reap segment. * whether wait keyframe to reap segment.
*/ */
virtual bool get_dvr_wait_keyframe(std::string vhost); virtual bool get_dvr_wait_keyframe(std::string vhost);
virtual void set_dvr_wait_keyframe(std::string vhost, bool wait_keyframe);
/** /**
* whether autostart for dvr. wait api to start dvr if false. * whether autostart for dvr. wait api to start dvr if false.
*/ */

View file

@ -195,6 +195,11 @@ int SrsFlvSegment::close()
} }
} }
if ((ret = plan->on_reap_segment()) != ERROR_SUCCESS) {
srs_error("dvr: notify plan to reap segment failed. ret=%d", ret);
return ret;
}
#ifdef SRS_AUTO_HTTP_CALLBACK #ifdef SRS_AUTO_HTTP_CALLBACK
if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
// HTTP: on_dvr // HTTP: on_dvr
@ -405,6 +410,11 @@ int SrsFlvSegment::update_flv_metadata()
return ret; return ret;
} }
string SrsFlvSegment::get_path()
{
return path;
}
string SrsFlvSegment::generate_path() string SrsFlvSegment::generate_path()
{ {
// the path in config, for example, // the path in config, for example,
@ -657,6 +667,11 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video)
return ret; return ret;
} }
int SrsDvrPlan::on_reap_segment()
{
return ERROR_SUCCESS;
}
SrsDvrPlan* SrsDvrPlan::create_plan(string vhost) SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
{ {
std::string plan = _srs_config->get_dvr_plan(vhost); std::string plan = _srs_config->get_dvr_plan(vhost);
@ -726,12 +741,32 @@ void SrsDvrSessionPlan::on_unpublish()
SrsDvrApiPlan::SrsDvrApiPlan() SrsDvrApiPlan::SrsDvrApiPlan()
{ {
autostart = false;
started = false;
} }
SrsDvrApiPlan::~SrsDvrApiPlan() SrsDvrApiPlan::~SrsDvrApiPlan()
{ {
} }
int SrsDvrApiPlan::initialize(SrsSource* s, SrsRequest* r)
{
int ret = ERROR_SUCCESS;
if ((ret = SrsDvrPlan::initialize(s, r)) != ERROR_SUCCESS) {
return ret;
}
SrsApiDvrPool* pool = SrsApiDvrPool::instance();
if ((ret = pool->add_dvr(this)) != ERROR_SUCCESS) {
return ret;
}
autostart = _srs_config->get_dvr_autostart(r->vhost);
return ret;
}
int SrsDvrApiPlan::on_publish() int SrsDvrApiPlan::on_publish()
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -745,6 +780,13 @@ int SrsDvrApiPlan::on_publish()
return ret; return ret;
} }
// api disabled dvr when not autostart.
bool autostart = _srs_config->get_dvr_autostart(req->vhost);
if (!autostart && !started) {
srs_warn("dvr: api not start and disabled for not autostart.");
return ret;
}
if ((ret = segment->close()) != ERROR_SUCCESS) { if ((ret = segment->close()) != ERROR_SUCCESS) {
return ret; return ret;
} }
@ -760,11 +802,36 @@ int SrsDvrApiPlan::on_publish()
void SrsDvrApiPlan::on_unpublish() void SrsDvrApiPlan::on_unpublish()
{ {
// support multiple publish.
if (!dvr_enabled) {
return;
} }
int SrsDvrApiPlan::set_path_tmpl(string path_tmpl)
{
_srs_config->set_dvr_path(req->vhost, path_tmpl);
return ERROR_SUCCESS;
}
int SrsDvrApiPlan::set_callback(string value)
{
callback = value;
return ERROR_SUCCESS;
}
int SrsDvrApiPlan::set_wait_keyframe(bool wait_keyframe)
{
_srs_config->set_dvr_wait_keyframe(req->vhost, wait_keyframe);
return ERROR_SUCCESS;
}
int SrsDvrApiPlan::start()
{
int ret = ERROR_SUCCESS;
if (started) {
return ret;
}
// stop dvr
if (dvr_enabled) {
// ignore error. // ignore error.
int ret = segment->close(); int ret = segment->close();
if (ret != ERROR_SUCCESS) { if (ret != ERROR_SUCCESS) {
@ -774,6 +841,39 @@ void SrsDvrApiPlan::on_unpublish()
dvr_enabled = false; dvr_enabled = false;
} }
// start dvr
if ((ret = on_publish()) != ERROR_SUCCESS) {
return ret;
}
started = true;
return ret;
}
int SrsDvrApiPlan::dumps(stringstream& ss)
{
int ret = ERROR_SUCCESS;
bool wait_keyframe = _srs_config->get_dvr_wait_keyframe(req->vhost);
std::string path_template = _srs_config->get_dvr_path(req->vhost);
ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_STR("path_tmpl", path_template) << __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_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_STR("callback", callback)
<< __SRS_JOBJECT_END;
return ret;
}
int SrsDvrApiPlan::on_reap_segment()
{
// TODO: FIXME: implements it.
return ERROR_SUCCESS;
}
SrsDvrAppendPlan::SrsDvrAppendPlan() SrsDvrAppendPlan::SrsDvrAppendPlan()
{ {
last_update_time = 0; last_update_time = 0;
@ -1043,16 +1143,103 @@ SrsApiDvrPool::SrsApiDvrPool()
SrsApiDvrPool::~SrsApiDvrPool() SrsApiDvrPool::~SrsApiDvrPool()
{ {
dvrs.clear();
} }
int SrsApiDvrPool::dumps(stringstream& ss) int SrsApiDvrPool::add_dvr(SrsDvrApiPlan* dvr)
{
dvrs.push_back(dvr);
return ERROR_SUCCESS;
}
int SrsApiDvrPool::dumps(string vhost, stringstream& ss)
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
ss << __SRS_JARRAY_START
<< __SRS_JARRAY_END; ss << __SRS_JARRAY_START;
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->dumps(ss)) != ERROR_SUCCESS) {
return ret; return ret;
} }
if (i < (int)dvrs.size() - 1) {
ss << __SRS_JFIELD_CONT;
}
}
ss << __SRS_JARRAY_END;
return ret;
}
int SrsApiDvrPool::create(SrsJsonAny* json)
{
int ret = ERROR_SUCCESS;
srs_assert(json);
if (!json->is_object()) {
ret = ERROR_HTTP_DVR_CREATE_REQUEST;
srs_error("dvr: api create dvr request requires json object. 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_CREATE_REQUEST;
srs_error("dvr: api create dvr request requires vhost. ret=%d", ret);
return ret;
}
std::string vhost = prop->to_str();
SrsDvrApiPlan* dvr = NULL;
for (int i = 0; i < (int)dvrs.size(); i++) {
SrsDvrApiPlan* plan = dvrs.at(i);
if (!vhost.empty() && plan->req->vhost != vhost) {
continue;
}
dvr = plan;
break;
}
if (!dvr) {
ret = ERROR_HTTP_DVR_CREATE_REQUEST;
srs_error("dvr: api create dvr request vhost invalid. vhost=%s. ret=%d", vhost.c_str(), ret);
return ret;
}
// update optional parameters for plan.
if ((prop = obj->ensure_property_string("path_tmpl")) != NULL) {
if ((ret = dvr->set_path_tmpl(prop->to_str())) != ERROR_SUCCESS) {
return ret;
}
}
if ((prop = obj->ensure_property_boolean("wait_keyframe")) != NULL) {
if ((ret = dvr->set_wait_keyframe(prop->to_boolean())) != ERROR_SUCCESS) {
return ret;
}
}
if ((prop = obj->ensure_property_string("callback")) != NULL) {
if ((ret = dvr->set_callback(prop->to_str())) != ERROR_SUCCESS) {
return ret;
}
}
return dvr->start();
}
SrsDvr::SrsDvr(SrsSource* s) SrsDvr::SrsDvr(SrsSource* s)
{ {
source = s; source = s;

View file

@ -43,6 +43,7 @@ class SrsSharedPtrMessage;
class SrsFileWriter; class SrsFileWriter;
class SrsFlvEncoder; class SrsFlvEncoder;
class SrsDvrPlan; class SrsDvrPlan;
class SrsJsonAny;
#include <srs_app_source.hpp> #include <srs_app_source.hpp>
#include <srs_app_reload.hpp> #include <srs_app_reload.hpp>
@ -149,6 +150,10 @@ public:
* update the flv metadata. * update the flv metadata.
*/ */
virtual int update_flv_metadata(); virtual int update_flv_metadata();
/**
* get the current dvr path.
*/
virtual std::string get_path();
private: private:
/** /**
* generate the flv segment path. * generate the flv segment path.
@ -179,9 +184,10 @@ class SrsDvrPlan
{ {
public: public:
friend class SrsFlvSegment; friend class SrsFlvSegment;
public:
SrsRequest* req;
protected: protected:
SrsSource* source; SrsSource* source;
SrsRequest* req;
SrsFlvSegment* segment; SrsFlvSegment* segment;
bool dvr_enabled; bool dvr_enabled;
public: public:
@ -204,6 +210,7 @@ public:
*/ */
virtual int on_video(SrsSharedPtrMessage* __video); virtual int on_video(SrsSharedPtrMessage* __video);
protected: protected:
virtual int on_reap_segment();
virtual int on_dvr_request_sh(); virtual int on_dvr_request_sh();
virtual int on_video_keyframe(); virtual int on_video_keyframe();
virtual int64_t filter_timestamp(int64_t timestamp); virtual int64_t filter_timestamp(int64_t timestamp);
@ -229,12 +236,25 @@ public:
*/ */
class SrsDvrApiPlan : public SrsDvrPlan class SrsDvrApiPlan : public SrsDvrPlan
{ {
private:
std::string callback;
bool autostart;
bool started;
public: public:
SrsDvrApiPlan(); SrsDvrApiPlan();
virtual ~SrsDvrApiPlan(); virtual ~SrsDvrApiPlan();
public: public:
virtual int initialize(SrsSource* s, SrsRequest* r);
virtual int on_publish(); virtual int on_publish();
virtual void on_unpublish(); virtual void on_unpublish();
public:
virtual int set_path_tmpl(std::string path_tmpl);
virtual int set_callback(std::string value);
virtual int set_wait_keyframe(bool wait_keyframe);
virtual int start();
virtual int dumps(std::stringstream& ss);
protected:
virtual int on_reap_segment();
}; };
/** /**
@ -302,6 +322,7 @@ private:
class SrsApiDvrPool class SrsApiDvrPool
{ {
private: private:
std::vector<SrsDvrApiPlan*> dvrs;
static SrsApiDvrPool* _instance; static SrsApiDvrPool* _instance;
private: private:
SrsApiDvrPool(); SrsApiDvrPool();
@ -309,7 +330,10 @@ public:
static SrsApiDvrPool* instance(); static SrsApiDvrPool* instance();
virtual ~SrsApiDvrPool(); virtual ~SrsApiDvrPool();
public: public:
virtual int dumps(std::stringstream& ss); virtual int add_dvr(SrsDvrApiPlan* dvr);
public:
virtual int dumps(std::string vhost, std::stringstream& ss);
virtual int create(SrsJsonAny* json);
}; };
/** /**

View file

@ -494,12 +494,25 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
SrsApiDvrPool* pool = SrsApiDvrPool::instance(); SrsApiDvrPool* pool = SrsApiDvrPool::instance();
if (r->is_http_get()) { if (r->is_http_get()) {
std::stringstream data; std::stringstream data;
int ret = pool->dumps(data); int ret = pool->dumps(r->query_get("vhost"), data);
ss << __SRS_JOBJECT_START ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT
<< __SRS_JFIELD_ORG("dvrs", data.str()) << __SRS_JFIELD_ORG("dvrs", data.str())
<< __SRS_JOBJECT_END; << __SRS_JOBJECT_END;
} else if (r->is_http_post()) {
char* body = (char*)r->body().c_str();
SrsJsonAny* json = SrsJsonAny::loads(body);
int ret = ERROR_SUCCESS;
if (!json) {
ret = ERROR_HTTP_JSON_REQUIRED;
} else {
SrsAutoFree(SrsJsonAny, json);
ret = pool->create(json);
}
ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_ERROR(ret)
<< __SRS_JOBJECT_END;
} else { } else {
ss << __SRS_JOBJECT_START ss << __SRS_JOBJECT_START
<< __SRS_JFIELD_ERROR(ERROR_HTTP_DVR_REQUEST) << __SRS_JFIELD_ERROR(ERROR_HTTP_DVR_REQUEST)

View file

@ -457,6 +457,21 @@ SrsJsonAny* SrsJsonObject::ensure_property_string(string name)
return prop; return prop;
} }
SrsJsonAny* SrsJsonObject::ensure_property_boolean(string name)
{
SrsJsonAny* prop = get_property(name);
if (!prop) {
return NULL;
}
if (!prop->is_boolean()) {
return NULL;
}
return prop;
}
SrsJsonArray::SrsJsonArray() SrsJsonArray::SrsJsonArray()
{ {
marker = SRS_JSON_Array; marker = SRS_JSON_Array;

View file

@ -123,6 +123,7 @@ public:
public: public:
/** /**
* read json tree from str:char* * read json tree from str:char*
* @return json object. NULL if error.
*/ */
static SrsJsonAny* loads(char* str); static SrsJsonAny* loads(char* str);
}; };
@ -148,6 +149,7 @@ public:
virtual void set(std::string key, SrsJsonAny* value); virtual void set(std::string key, SrsJsonAny* value);
virtual SrsJsonAny* get_property(std::string name); virtual SrsJsonAny* get_property(std::string name);
virtual SrsJsonAny* ensure_property_string(std::string name); virtual SrsJsonAny* ensure_property_string(std::string name);
virtual SrsJsonAny* ensure_property_boolean(std::string name);
}; };
class SrsJsonArray : public SrsJsonAny class SrsJsonArray : public SrsJsonAny
@ -214,6 +216,7 @@ that is:
#define __SRS_JFIELD_NAME(k) "\"" << k << "\":" #define __SRS_JFIELD_NAME(k) "\"" << k << "\":"
#define __SRS_JFIELD_STR(k, v) "\"" << k << "\":\"" << v << "\"" #define __SRS_JFIELD_STR(k, v) "\"" << k << "\":\"" << v << "\""
#define __SRS_JFIELD_ORG(k, v) "\"" << k << "\":" << std::dec << v #define __SRS_JFIELD_ORG(k, v) "\"" << k << "\":" << std::dec << v
#define __SRS_JFIELD_BOOL(k, v) __SRS_JFIELD_ORG(k, (v? "true":"false"))
#define __SRS_JFIELD_ERROR(ret) "\"" << "code" << "\":" << ret #define __SRS_JFIELD_ERROR(ret) "\"" << "code" << "\":" << ret
#define __SRS_JFIELD_CONT "," #define __SRS_JFIELD_CONT ","
#define __SRS_JOBJECT_END "}" #define __SRS_JOBJECT_END "}"

View file

@ -211,6 +211,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_HLS_TRY_MP3 3049 #define ERROR_HLS_TRY_MP3 3049
#define ERROR_HTTP_DVR_DISABLED 3050 #define ERROR_HTTP_DVR_DISABLED 3050
#define ERROR_HTTP_DVR_REQUEST 3051 #define ERROR_HTTP_DVR_REQUEST 3051
#define ERROR_HTTP_JSON_REQUIRED 3052
#define ERROR_HTTP_DVR_CREATE_REQUEST 3053
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// HTTP/StreamCaster protocol error. // HTTP/StreamCaster protocol error.