正在显示
5 个修改的文件
包含
157 行增加
和
0 行删除
| @@ -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) |
-
请 注册 或 登录 后发表评论