winlin

fix #179, support dvr http api. 2.0.123.

@@ -532,6 +532,7 @@ Supported operating systems and hardware: @@ -532,6 +532,7 @@ Supported operating systems and hardware:
532 532
533 ### SRS 2.0 history 533 ### SRS 2.0 history
534 534
  535 +* v2.0, 2015-02-24, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), support dvr http api. 2.0.123.
535 * v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122. 536 * v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122.
536 * v2.0, 2015-02-19, refine pithy print to more easyer to use. 2.0.121. 537 * v2.0, 2015-02-19, refine pithy print to more easyer to use. 2.0.121.
537 * v2.0, 2015-02-18, fix [#133](https://github.com/winlinvip/simple-rtmp-server/issues/133), support push rtsp to srs. 2.0.120. 538 * 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 { @@ -314,6 +314,9 @@ vhost dvr.srs.com {
314 # vhost, stop all dvr of this vhost. 314 # vhost, stop all dvr of this vhost.
315 # response in json, where: 315 # response in json, where:
316 # {code:0} 316 # {code:0}
  317 + # method=PUT, use as RPC(remote process call).
  318 + # reap_segment, the request params in json, where:
  319 + # {action:"reap_segment", vhost:"__defaultVhost", path_tmpl:"./[15].[04].[05].[999].flv"}
317 # when reap segment, the callback POST request in json: 320 # when reap segment, the callback POST request in json:
318 # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__", 321 # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
319 # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv" 322 # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
@@ -322,6 +325,7 @@ vhost dvr.srs.com { @@ -322,6 +325,7 @@ vhost dvr.srs.com {
322 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback 325 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
323 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback 326 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
324 # default: session 327 # default: session
  328 + # TODO: FIXME: update wiki for the api plan.
325 dvr_plan session; 329 dvr_plan session;
326 # the dvr output path. 330 # the dvr output path.
327 # we supports some variables to generate the filename. 331 # we supports some variables to generate the filename.
@@ -48,6 +48,9 @@ using namespace std; @@ -48,6 +48,9 @@ using namespace std;
48 // the sleep interval for http async callback. 48 // the sleep interval for http async callback.
49 #define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000 49 #define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000
50 50
  51 +// the use raction for dvr rpc.
  52 +#define SRS_DVR_USER_ACTION_REAP_SEGMENT "reap_segment"
  53 +
51 SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p) 54 SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p)
52 { 55 {
53 req = NULL; 56 req = NULL;
@@ -1003,6 +1006,10 @@ int SrsDvrApiPlan::on_video(SrsSharedPtrMessage* __video) @@ -1003,6 +1006,10 @@ int SrsDvrApiPlan::on_video(SrsSharedPtrMessage* __video)
1003 sh_video = __video->copy(); 1006 sh_video = __video->copy();
1004 } 1007 }
1005 1008
  1009 + if ((ret = check_user_actions(__video)) != ERROR_SUCCESS) {
  1010 + return ret;
  1011 + }
  1012 +
1006 if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) { 1013 if ((ret = SrsDvrPlan::on_video(__video)) != ERROR_SUCCESS) {
1007 return ret; 1014 return ret;
1008 } 1015 }
@@ -1101,6 +1108,29 @@ int SrsDvrApiPlan::stop() @@ -1101,6 +1108,29 @@ int SrsDvrApiPlan::stop()
1101 return ret; 1108 return ret;
1102 } 1109 }
1103 1110
  1111 +int SrsDvrApiPlan::rpc(SrsJsonObject* obj)
  1112 +{
  1113 + int ret = ERROR_SUCCESS;
  1114 +
  1115 + SrsJsonAny* prop = NULL;
  1116 + if ((prop = obj->ensure_property_string("action")) == NULL) {
  1117 + ret = ERROR_HTTP_DVR_REQUEST;
  1118 + srs_error("dvr: rpc required action request. ret=%d", ret);
  1119 + return ret;
  1120 + }
  1121 +
  1122 + action = prop->to_str();
  1123 + if (action == SRS_DVR_USER_ACTION_REAP_SEGMENT) {
  1124 + if ((prop = obj->ensure_property_string("path_tmpl")) != NULL) {
  1125 + path_template = prop->to_str();
  1126 + }
  1127 + } else {
  1128 + ret = ERROR_HTTP_DVR_REQUEST;
  1129 + }
  1130 +
  1131 + return ret;
  1132 +}
  1133 +
1104 int SrsDvrApiPlan::on_reap_segment() 1134 int SrsDvrApiPlan::on_reap_segment()
1105 { 1135 {
1106 int ret = ERROR_SUCCESS; 1136 int ret = ERROR_SUCCESS;
@@ -1116,6 +1146,64 @@ int SrsDvrApiPlan::on_reap_segment() @@ -1116,6 +1146,64 @@ int SrsDvrApiPlan::on_reap_segment()
1116 return ret; 1146 return ret;
1117 } 1147 }
1118 1148
  1149 +int SrsDvrApiPlan::check_user_actions(SrsSharedPtrMessage* msg)
  1150 +{
  1151 + int ret = ERROR_SUCCESS;
  1152 +
  1153 + srs_assert(segment);
  1154 +
  1155 + if (action == SRS_DVR_USER_ACTION_REAP_SEGMENT) {
  1156 + // when wait keyframe, ignore if no frame arrived.
  1157 + // @see https://github.com/winlinvip/simple-rtmp-server/issues/177
  1158 + if (_srs_config->get_dvr_wait_keyframe(req->vhost)) {
  1159 + if (!msg->is_video()) {
  1160 + return ret;
  1161 + }
  1162 +
  1163 + char* payload = msg->payload;
  1164 + int size = msg->size;
  1165 + bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size)
  1166 + && SrsFlvCodec::video_is_keyframe(payload, size)
  1167 + && !SrsFlvCodec::video_is_sequence_header(payload, size);
  1168 + if (!is_key_frame) {
  1169 + return ret;
  1170 + }
  1171 + }
  1172 +
  1173 + // reap segment
  1174 + if ((ret = segment->close()) != ERROR_SUCCESS) {
  1175 + return ret;
  1176 + }
  1177 +
  1178 + // use new path template if user specified.
  1179 + if (!path_template.empty() && (ret = set_path_tmpl(path_template)) != ERROR_SUCCESS) {
  1180 + return ret;
  1181 + }
  1182 +
  1183 + // open new flv file
  1184 + if ((ret = segment->open()) != ERROR_SUCCESS) {
  1185 + return ret;
  1186 + }
  1187 +
  1188 + // update sequence header
  1189 + if (metadata && (ret = SrsDvrPlan::on_meta_data(metadata)) != ERROR_SUCCESS) {
  1190 + return ret;
  1191 + }
  1192 + if (sh_video && (ret = SrsDvrPlan::on_video(sh_video)) != ERROR_SUCCESS) {
  1193 + return ret;
  1194 + }
  1195 + if (sh_audio && (ret = SrsDvrPlan::on_audio(sh_audio)) != ERROR_SUCCESS) {
  1196 + return ret;
  1197 + }
  1198 + }
  1199 +
  1200 + // reset rcp params.
  1201 + action = "";
  1202 + path_template = "";
  1203 +
  1204 + return ret;
  1205 +}
  1206 +
1119 SrsDvrAppendPlan::SrsDvrAppendPlan() 1207 SrsDvrAppendPlan::SrsDvrAppendPlan()
1120 { 1208 {
1121 last_update_time = 0; 1209 last_update_time = 0;
@@ -1506,6 +1594,46 @@ int SrsApiDvrPool::stop(string vhost) @@ -1506,6 +1594,46 @@ int SrsApiDvrPool::stop(string vhost)
1506 return ret; 1594 return ret;
1507 } 1595 }
1508 1596
  1597 +int SrsApiDvrPool::rpc(SrsJsonAny* json)
  1598 +{
  1599 + int ret = ERROR_SUCCESS;
  1600 +
  1601 + if (!json->is_object()) {
  1602 + ret = ERROR_HTTP_DVR_REQUEST;
  1603 + srs_error("dvr: rpc required object request. ret=%d", ret);
  1604 + return ret;
  1605 + }
  1606 +
  1607 + SrsJsonObject* obj = json->to_object();
  1608 +
  1609 + SrsJsonAny* prop = NULL;
  1610 + if ((prop = obj->ensure_property_string("vhost")) == NULL) {
  1611 + ret = ERROR_HTTP_DVR_REQUEST;
  1612 + srs_error("dvr: rpc required vhost request. ret=%d", ret);
  1613 + return ret;
  1614 + }
  1615 + std::string vhost = prop->to_str();
  1616 +
  1617 + std::vector<SrsDvrApiPlan*> plans;
  1618 + for (int i = 0; i < (int)dvrs.size(); i++) {
  1619 + SrsDvrApiPlan* plan = dvrs.at(i);
  1620 + if (!vhost.empty() && plan->req->vhost != vhost) {
  1621 + continue;
  1622 + }
  1623 + plans.push_back(plan);
  1624 + }
  1625 +
  1626 + for (int i = 0; i < (int)plans.size(); i++) {
  1627 + SrsDvrApiPlan* plan = plans.at(i);
  1628 +
  1629 + if ((ret = plan->rpc(obj)) != ERROR_SUCCESS) {
  1630 + return ret;
  1631 + }
  1632 + }
  1633 +
  1634 + return ret;
  1635 +}
  1636 +
1509 SrsDvr::SrsDvr(SrsSource* s) 1637 SrsDvr::SrsDvr(SrsSource* s)
1510 { 1638 {
1511 source = s; 1639 source = s;
@@ -44,6 +44,7 @@ class SrsFileWriter; @@ -44,6 +44,7 @@ class SrsFileWriter;
44 class SrsFlvEncoder; 44 class SrsFlvEncoder;
45 class SrsDvrPlan; 45 class SrsDvrPlan;
46 class SrsJsonAny; 46 class SrsJsonAny;
  47 +class SrsJsonObject;
47 class SrsThread; 48 class SrsThread;
48 49
49 #include <srs_app_source.hpp> 50 #include <srs_app_source.hpp>
@@ -305,6 +306,10 @@ private: @@ -305,6 +306,10 @@ private:
305 std::string callback; 306 std::string callback;
306 bool autostart; 307 bool autostart;
307 bool started; 308 bool started;
  309 +private:
  310 + // user action, reap_segment.
  311 + std::string action;
  312 + std::string path_template;
308 public: 313 public:
309 SrsDvrApiPlan(); 314 SrsDvrApiPlan();
310 virtual ~SrsDvrApiPlan(); 315 virtual ~SrsDvrApiPlan();
@@ -322,8 +327,11 @@ public: @@ -322,8 +327,11 @@ public:
322 virtual int start(); 327 virtual int start();
323 virtual int dumps(std::stringstream& ss); 328 virtual int dumps(std::stringstream& ss);
324 virtual int stop(); 329 virtual int stop();
  330 + virtual int rpc(SrsJsonObject* obj);
325 protected: 331 protected:
326 virtual int on_reap_segment(); 332 virtual int on_reap_segment();
  333 +private:
  334 + virtual int check_user_actions(SrsSharedPtrMessage* msg);
327 }; 335 };
328 336
329 /** 337 /**
@@ -389,6 +397,7 @@ public: @@ -389,6 +397,7 @@ public:
389 virtual int dumps(std::string vhost, std::stringstream& ss); 397 virtual int dumps(std::string vhost, std::stringstream& ss);
390 virtual int create(SrsJsonAny* json); 398 virtual int create(SrsJsonAny* json);
391 virtual int stop(std::string vhost); 399 virtual int stop(std::string vhost);
  400 + virtual int rpc(SrsJsonAny* json);
392 }; 401 };
393 402
394 /** 403 /**
@@ -519,6 +519,21 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) @@ -519,6 +519,21 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
519 ss << __SRS_JOBJECT_START 519 ss << __SRS_JOBJECT_START
520 << __SRS_JFIELD_ERROR(ret) 520 << __SRS_JFIELD_ERROR(ret)
521 << __SRS_JOBJECT_END; 521 << __SRS_JOBJECT_END;
  522 + } else if (r->is_http_put()) {
  523 + int ret = ERROR_SUCCESS;
  524 +
  525 + std::string body = r->body();
  526 + SrsJsonAny* json = SrsJsonAny::loads((char*)body.c_str());
  527 + if (!json) {
  528 + ret = ERROR_HTTP_JSON_REQUIRED;
  529 + } else {
  530 + SrsAutoFree(SrsJsonAny, json);
  531 + ret = pool->rpc(json);
  532 + }
  533 +
  534 + ss << __SRS_JOBJECT_START
  535 + << __SRS_JFIELD_ERROR(ret)
  536 + << __SRS_JOBJECT_END;
522 } else { 537 } else {
523 ss << __SRS_JOBJECT_START 538 ss << __SRS_JOBJECT_START
524 << __SRS_JFIELD_ERROR(ERROR_HTTP_DVR_REQUEST) 539 << __SRS_JFIELD_ERROR(ERROR_HTTP_DVR_REQUEST)