mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
for #179, support http api to start dvr.
This commit is contained in:
parent
c67a4fdf97
commit
849e59b05d
9 changed files with 320 additions and 21 deletions
|
@ -296,15 +296,14 @@ vhost dvr.srs.com {
|
|||
# request params, for example ?vhost=__defaultVhost__, where:
|
||||
# vhost, query all dvr of this vhost.
|
||||
# response in json, where:
|
||||
# {code:0, dvrs: [{plan:"api", path:"./objs/nginx/html",
|
||||
# autostart:true, wait_keyframe:true, jitter:"full"
|
||||
# {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"
|
||||
# }]}
|
||||
# method=POST
|
||||
# to start dvr of specified vhost.
|
||||
# request should encode in json, specifies the dvr to create, where:
|
||||
# {plan:"api", path:"./objs/nginx/html",
|
||||
# autostart:true, wait_keyframe:true, jitter:"full",
|
||||
# vhost:"__defaultVhost", callback:"http://dvr/callback"
|
||||
# {path_tmpl:"./[15].[04].[05].[999].flv",
|
||||
# wait_keyframe:true, vhost:"__defaultVhost", callback:"http://dvr/callback"
|
||||
# }
|
||||
# response in json, where:
|
||||
# {code:0}
|
||||
|
@ -329,7 +328,7 @@ vhost dvr.srs.com {
|
|||
# [05], repleace this const to current second.
|
||||
# [999], repleace this const to current millisecond.
|
||||
# [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
|
||||
# 1. No variables, the rule of SRS1.0(auto add [stream].[timestamp].flv as filename):
|
||||
# dvr_path ./objs/nginx/html;
|
||||
|
|
|
@ -1977,6 +1977,38 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* sc)
|
|||
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)
|
||||
{
|
||||
srs_assert(root);
|
||||
|
@ -3327,6 +3359,13 @@ string SrsConfig::get_dvr_path(string vhost)
|
|||
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)
|
||||
{
|
||||
SrsConfDirective* dvr = get_dvr(vhost);
|
||||
|
@ -3378,6 +3417,13 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost)
|
|||
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)
|
||||
{
|
||||
SrsConfDirective* dvr = get_dvr(vhost);
|
||||
|
|
|
@ -452,6 +452,14 @@ public:
|
|||
* get the max udp port for rtp of stream caster rtsp.
|
||||
*/
|
||||
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
|
||||
public:
|
||||
/**
|
||||
|
@ -919,6 +927,7 @@ public:
|
|||
* get the dvr path, the flv file to save in.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -931,6 +940,7 @@ public:
|
|||
* whether wait keyframe to reap segment.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -194,6 +194,11 @@ int SrsFlvSegment::close()
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
|
||||
|
@ -405,6 +410,11 @@ int SrsFlvSegment::update_flv_metadata()
|
|||
return ret;
|
||||
}
|
||||
|
||||
string SrsFlvSegment::get_path()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
string SrsFlvSegment::generate_path()
|
||||
{
|
||||
// the path in config, for example,
|
||||
|
@ -657,6 +667,11 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int SrsDvrPlan::on_reap_segment()
|
||||
{
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
|
||||
{
|
||||
std::string plan = _srs_config->get_dvr_plan(vhost);
|
||||
|
@ -726,12 +741,32 @@ void SrsDvrSessionPlan::on_unpublish()
|
|||
|
||||
SrsDvrApiPlan::SrsDvrApiPlan()
|
||||
{
|
||||
autostart = false;
|
||||
started = false;
|
||||
}
|
||||
|
||||
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 ret = ERROR_SUCCESS;
|
||||
|
@ -745,6 +780,13 @@ int SrsDvrApiPlan::on_publish()
|
|||
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) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -760,18 +802,76 @@ int SrsDvrApiPlan::on_publish()
|
|||
|
||||
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.
|
||||
int ret = segment->close();
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
srs_warn("ignore flv close error. ret=%d", ret);
|
||||
}
|
||||
|
||||
// ignore error.
|
||||
int ret = segment->close();
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
srs_warn("ignore flv close error. ret=%d", ret);
|
||||
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()
|
||||
|
@ -1043,16 +1143,103 @@ 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
source = s;
|
||||
|
|
|
@ -43,6 +43,7 @@ class SrsSharedPtrMessage;
|
|||
class SrsFileWriter;
|
||||
class SrsFlvEncoder;
|
||||
class SrsDvrPlan;
|
||||
class SrsJsonAny;
|
||||
|
||||
#include <srs_app_source.hpp>
|
||||
#include <srs_app_reload.hpp>
|
||||
|
@ -149,6 +150,10 @@ public:
|
|||
* update the flv metadata.
|
||||
*/
|
||||
virtual int update_flv_metadata();
|
||||
/**
|
||||
* get the current dvr path.
|
||||
*/
|
||||
virtual std::string get_path();
|
||||
private:
|
||||
/**
|
||||
* generate the flv segment path.
|
||||
|
@ -179,9 +184,10 @@ class SrsDvrPlan
|
|||
{
|
||||
public:
|
||||
friend class SrsFlvSegment;
|
||||
public:
|
||||
SrsRequest* req;
|
||||
protected:
|
||||
SrsSource* source;
|
||||
SrsRequest* req;
|
||||
SrsFlvSegment* segment;
|
||||
bool dvr_enabled;
|
||||
public:
|
||||
|
@ -204,6 +210,7 @@ public:
|
|||
*/
|
||||
virtual int on_video(SrsSharedPtrMessage* __video);
|
||||
protected:
|
||||
virtual int on_reap_segment();
|
||||
virtual int on_dvr_request_sh();
|
||||
virtual int on_video_keyframe();
|
||||
virtual int64_t filter_timestamp(int64_t timestamp);
|
||||
|
@ -229,12 +236,25 @@ public:
|
|||
*/
|
||||
class SrsDvrApiPlan : public SrsDvrPlan
|
||||
{
|
||||
private:
|
||||
std::string callback;
|
||||
bool autostart;
|
||||
bool started;
|
||||
public:
|
||||
SrsDvrApiPlan();
|
||||
virtual ~SrsDvrApiPlan();
|
||||
public:
|
||||
virtual int initialize(SrsSource* s, SrsRequest* r);
|
||||
virtual int on_publish();
|
||||
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
|
||||
{
|
||||
private:
|
||||
std::vector<SrsDvrApiPlan*> dvrs;
|
||||
static SrsApiDvrPool* _instance;
|
||||
private:
|
||||
SrsApiDvrPool();
|
||||
|
@ -309,7 +330,10 @@ public:
|
|||
static SrsApiDvrPool* instance();
|
||||
virtual ~SrsApiDvrPool();
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -494,12 +494,25 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
|
|||
SrsApiDvrPool* pool = SrsApiDvrPool::instance();
|
||||
if (r->is_http_get()) {
|
||||
std::stringstream data;
|
||||
int ret = pool->dumps(data);
|
||||
int ret = pool->dumps(r->query_get("vhost"), data);
|
||||
|
||||
ss << __SRS_JOBJECT_START
|
||||
<< __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT
|
||||
<< __SRS_JFIELD_ORG("dvrs", data.str())
|
||||
<< __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 {
|
||||
ss << __SRS_JOBJECT_START
|
||||
<< __SRS_JFIELD_ERROR(ERROR_HTTP_DVR_REQUEST)
|
||||
|
|
|
@ -457,6 +457,21 @@ SrsJsonAny* SrsJsonObject::ensure_property_string(string name)
|
|||
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()
|
||||
{
|
||||
marker = SRS_JSON_Array;
|
||||
|
|
|
@ -123,6 +123,7 @@ public:
|
|||
public:
|
||||
/**
|
||||
* read json tree from str:char*
|
||||
* @return json object. NULL if error.
|
||||
*/
|
||||
static SrsJsonAny* loads(char* str);
|
||||
};
|
||||
|
@ -148,6 +149,7 @@ public:
|
|||
virtual void set(std::string key, SrsJsonAny* value);
|
||||
virtual SrsJsonAny* get_property(std::string name);
|
||||
virtual SrsJsonAny* ensure_property_string(std::string name);
|
||||
virtual SrsJsonAny* ensure_property_boolean(std::string name);
|
||||
};
|
||||
|
||||
class SrsJsonArray : public SrsJsonAny
|
||||
|
@ -214,6 +216,7 @@ that is:
|
|||
#define __SRS_JFIELD_NAME(k) "\"" << k << "\":"
|
||||
#define __SRS_JFIELD_STR(k, v) "\"" << k << "\":\"" << 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_CONT ","
|
||||
#define __SRS_JOBJECT_END "}"
|
||||
|
|
|
@ -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_HTTP_DVR_DISABLED 3050
|
||||
#define ERROR_HTTP_DVR_REQUEST 3051
|
||||
#define ERROR_HTTP_JSON_REQUIRED 3052
|
||||
#define ERROR_HTTP_DVR_CREATE_REQUEST 3053
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// HTTP/StreamCaster protocol error.
|
||||
|
|
Loading…
Reference in a new issue