winlin

for #179, dvr suport vhost/app/stream level control. 2.0.125.

@@ -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, for [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), dvr suport vhost/app/stream level control. 2.0.125.
535 * v2.0, 2015-02-24, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), fix hls bug, write pts/dts error. 2.0.124. 536 * v2.0, 2015-02-24, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), fix hls bug, write pts/dts error. 2.0.124.
536 * v2.0, 2015-02-24, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), support dvr http api. 2.0.123. 537 * v2.0, 2015-02-24, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), support dvr http api. 2.0.123.
537 * v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122. 538 * v2.0, 2015-02-19, refine dvr, append file when dvr file exists. 2.0.122.
@@ -293,19 +293,24 @@ vhost dvr.srs.com { @@ -293,19 +293,24 @@ vhost dvr.srs.com {
293 # http url to control dvr, for example, http://dev:1985/api/v1/dvrs 293 # http url to control dvr, for example, http://dev:1985/api/v1/dvrs
294 # method=GET 294 # method=GET
295 # to query dvrs of server. 295 # to query dvrs of server.
296 - # request params, for example ?vhost=__defaultVhost__, where:  
297 - # vhost, query all dvr of this vhost. 296 + # request params, for example ?vhost=__defaultVhost__&&app=live&&stream=livestream, where:
  297 + # vhost, <required>, query all dvr of this vhost.
  298 + # app, [optinal], query all dvr of this app. query all app if not specified.
  299 + # stream, [optional], query specified dvr stream. query all stream if not specified.
298 # response in json, where: 300 # response in json, where:
299 # {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv", 301 # {code:0, dvrs: [{path_tmpl:"./[15].[04].[05].[999].flv", path_dvr:"./22.7.43.312.flv",
300 - # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs", 302 + # vhost:"__defaultVhost", app:"live", stream:"livestream",
  303 + # wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs",
301 # status:"stop"|"start" 304 # status:"stop"|"start"
302 # }]} 305 # }]}
303 # method=POST 306 # method=POST
304 # to start dvr of specified vhost. 307 # to start dvr of specified vhost.
305 # request should encode in json, specifies the dvr to create, where: 308 # request should encode in json, specifies the dvr to create, where:
306 # {path_tmpl:"./[15].[04].[05].[999].flv", 309 # {path_tmpl:"./[15].[04].[05].[999].flv",
307 - # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs" 310 + # vhost:"__defaultVhost", app:"live", stream:"livestream",
  311 + # wait_keyframe:true, callback:"http://127.0.0.1:8085/api/v1/dvrs"
308 # } 312 # }
  313 + # @remark, the app and stream is optional.
309 # response in json, where: 314 # response in json, where:
310 # {code:0} 315 # {code:0}
311 # method=DELETE, to stop dvr 316 # method=DELETE, to stop dvr
@@ -316,7 +321,10 @@ vhost dvr.srs.com { @@ -316,7 +321,10 @@ vhost dvr.srs.com {
316 # {code:0} 321 # {code:0}
317 # method=PUT, use as RPC(remote process call). 322 # method=PUT, use as RPC(remote process call).
318 # reap_segment, the request params in json, where: 323 # reap_segment, the request params in json, where:
319 - # {action:"reap_segment", vhost:"__defaultVhost", path_tmpl:"./[15].[04].[05].[999].flv"} 324 + # {action:"reap_segment", vhost:"__defaultVhost", app:"live", stream:"livestream",
  325 + # path_tmpl:"./[15].[04].[05].[999].flv"
  326 + # }
  327 + # @remark, the app and stream is optional.
320 # when reap segment, the callback POST request in json: 328 # when reap segment, the callback POST request in json:
321 # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__", 329 # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
322 # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv" 330 # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
@@ -1078,6 +1078,8 @@ int SrsDvrApiPlan::dumps(stringstream& ss) @@ -1078,6 +1078,8 @@ int SrsDvrApiPlan::dumps(stringstream& ss)
1078 << __SRS_JFIELD_STR("path_dvr", segment->get_path()) << __SRS_JFIELD_CONT 1078 << __SRS_JFIELD_STR("path_dvr", segment->get_path()) << __SRS_JFIELD_CONT
1079 << __SRS_JFIELD_BOOL("wait_keyframe", wait_keyframe) << __SRS_JFIELD_CONT 1079 << __SRS_JFIELD_BOOL("wait_keyframe", wait_keyframe) << __SRS_JFIELD_CONT
1080 << __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT 1080 << __SRS_JFIELD_STR("vhost", req->vhost) << __SRS_JFIELD_CONT
  1081 + << __SRS_JFIELD_STR("app", req->app) << __SRS_JFIELD_CONT
  1082 + << __SRS_JFIELD_STR("stream", req->stream) << __SRS_JFIELD_CONT
1081 << __SRS_JFIELD_STR("callback", callback) << __SRS_JFIELD_CONT 1083 << __SRS_JFIELD_STR("callback", callback) << __SRS_JFIELD_CONT
1082 << __SRS_JFIELD_STR("status", (dvr_enabled? "start":"stop")) 1084 << __SRS_JFIELD_STR("status", (dvr_enabled? "start":"stop"))
1083 << __SRS_JOBJECT_END; 1085 << __SRS_JOBJECT_END;
@@ -1482,7 +1484,7 @@ int SrsApiDvrPool::add_dvr(SrsDvrApiPlan* dvr) @@ -1482,7 +1484,7 @@ int SrsApiDvrPool::add_dvr(SrsDvrApiPlan* dvr)
1482 return ERROR_SUCCESS; 1484 return ERROR_SUCCESS;
1483 } 1485 }
1484 1486
1485 -int SrsApiDvrPool::dumps(string vhost, stringstream& ss) 1487 +int SrsApiDvrPool::dumps(string vhost, string app, string stream, stringstream& ss)
1486 { 1488 {
1487 int ret = ERROR_SUCCESS; 1489 int ret = ERROR_SUCCESS;
1488 1490
@@ -1494,6 +1496,12 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss) @@ -1494,6 +1496,12 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss)
1494 if (!vhost.empty() && plan->req->vhost != vhost) { 1496 if (!vhost.empty() && plan->req->vhost != vhost) {
1495 continue; 1497 continue;
1496 } 1498 }
  1499 + if (!app.empty() && plan->req->app != app) {
  1500 + continue;
  1501 + }
  1502 + if (!stream.empty() && plan->req->stream != stream) {
  1503 + continue;
  1504 + }
1497 plans.push_back(plan); 1505 plans.push_back(plan);
1498 } 1506 }
1499 1507
@@ -1504,7 +1512,7 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss) @@ -1504,7 +1512,7 @@ int SrsApiDvrPool::dumps(string vhost, stringstream& ss)
1504 return ret; 1512 return ret;
1505 } 1513 }
1506 1514
1507 - if (i < (int)dvrs.size() - 1) { 1515 + if (i < (int)plans.size() - 1) {
1508 ss << __SRS_JFIELD_CONT; 1516 ss << __SRS_JFIELD_CONT;
1509 } 1517 }
1510 } 1518 }
@@ -1534,19 +1542,33 @@ int SrsApiDvrPool::create(SrsJsonAny* json) @@ -1534,19 +1542,33 @@ int SrsApiDvrPool::create(SrsJsonAny* json)
1534 } 1542 }
1535 1543
1536 std::string vhost = prop->to_str(); 1544 std::string vhost = prop->to_str();
  1545 + std::string app, stream;
  1546 + if ((prop = obj->ensure_property_string("app")) != NULL) {
  1547 + app = prop->to_str();
  1548 + }
  1549 + if ((prop = obj->ensure_property_string("stream")) != NULL) {
  1550 + stream = prop->to_str();
  1551 + }
  1552 +
1537 SrsDvrApiPlan* dvr = NULL; 1553 SrsDvrApiPlan* dvr = NULL;
1538 for (int i = 0; i < (int)dvrs.size(); i++) { 1554 for (int i = 0; i < (int)dvrs.size(); i++) {
1539 SrsDvrApiPlan* plan = dvrs.at(i); 1555 SrsDvrApiPlan* plan = dvrs.at(i);
1540 if (!vhost.empty() && plan->req->vhost != vhost) { 1556 if (!vhost.empty() && plan->req->vhost != vhost) {
1541 continue; 1557 continue;
1542 } 1558 }
  1559 + if (!app.empty() && plan->req->app != app) {
  1560 + continue;
  1561 + }
  1562 + if (!stream.empty() && plan->req->stream != stream) {
  1563 + continue;
  1564 + }
1543 dvr = plan; 1565 dvr = plan;
1544 break; 1566 break;
1545 } 1567 }
1546 1568
1547 if (!dvr) { 1569 if (!dvr) {
1548 - ret = ERROR_HTTP_DVR_CREATE_REQUEST;  
1549 - srs_error("dvr: api create dvr request vhost invalid. vhost=%s. ret=%d", vhost.c_str(), ret); 1570 + ret = ERROR_HTTP_DVR_NO_TAEGET;
  1571 + srs_error("dvr: create not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret);
1550 return ret; 1572 return ret;
1551 } 1573 }
1552 1574
@@ -1570,7 +1592,7 @@ int SrsApiDvrPool::create(SrsJsonAny* json) @@ -1570,7 +1592,7 @@ int SrsApiDvrPool::create(SrsJsonAny* json)
1570 return dvr->start(); 1592 return dvr->start();
1571 } 1593 }
1572 1594
1573 -int SrsApiDvrPool::stop(string vhost) 1595 +int SrsApiDvrPool::stop(string vhost, string app, string stream)
1574 { 1596 {
1575 int ret = ERROR_SUCCESS; 1597 int ret = ERROR_SUCCESS;
1576 1598
@@ -1580,9 +1602,21 @@ int SrsApiDvrPool::stop(string vhost) @@ -1580,9 +1602,21 @@ int SrsApiDvrPool::stop(string vhost)
1580 if (!vhost.empty() && plan->req->vhost != vhost) { 1602 if (!vhost.empty() && plan->req->vhost != vhost) {
1581 continue; 1603 continue;
1582 } 1604 }
  1605 + if (!app.empty() && plan->req->app != app) {
  1606 + continue;
  1607 + }
  1608 + if (!stream.empty() && plan->req->stream != stream) {
  1609 + continue;
  1610 + }
1583 plans.push_back(plan); 1611 plans.push_back(plan);
1584 } 1612 }
1585 1613
  1614 + if (plans.empty()) {
  1615 + ret = ERROR_HTTP_DVR_NO_TAEGET;
  1616 + srs_error("dvr: stop not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret);
  1617 + return ret;
  1618 + }
  1619 +
1586 for (int i = 0; i < (int)plans.size(); i++) { 1620 for (int i = 0; i < (int)plans.size(); i++) {
1587 SrsDvrApiPlan* plan = plans.at(i); 1621 SrsDvrApiPlan* plan = plans.at(i);
1588 1622
@@ -1613,6 +1647,13 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json) @@ -1613,6 +1647,13 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json)
1613 return ret; 1647 return ret;
1614 } 1648 }
1615 std::string vhost = prop->to_str(); 1649 std::string vhost = prop->to_str();
  1650 + std::string app, stream;
  1651 + if ((prop = obj->ensure_property_string("app")) != NULL) {
  1652 + app = prop->to_str();
  1653 + }
  1654 + if ((prop = obj->ensure_property_string("stream")) != NULL) {
  1655 + stream = prop->to_str();
  1656 + }
1616 1657
1617 std::vector<SrsDvrApiPlan*> plans; 1658 std::vector<SrsDvrApiPlan*> plans;
1618 for (int i = 0; i < (int)dvrs.size(); i++) { 1659 for (int i = 0; i < (int)dvrs.size(); i++) {
@@ -1623,6 +1664,12 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json) @@ -1623,6 +1664,12 @@ int SrsApiDvrPool::rpc(SrsJsonAny* json)
1623 plans.push_back(plan); 1664 plans.push_back(plan);
1624 } 1665 }
1625 1666
  1667 + if (plans.empty()) {
  1668 + ret = ERROR_HTTP_DVR_NO_TAEGET;
  1669 + srs_error("dvr: rpc not found for url=%s/%s/%s, ret=%d", vhost.c_str(), app.c_str(), stream.c_str(), ret);
  1670 + return ret;
  1671 + }
  1672 +
1626 for (int i = 0; i < (int)plans.size(); i++) { 1673 for (int i = 0; i < (int)plans.size(); i++) {
1627 SrsDvrApiPlan* plan = plans.at(i); 1674 SrsDvrApiPlan* plan = plans.at(i);
1628 1675
@@ -394,9 +394,9 @@ public: @@ -394,9 +394,9 @@ public:
394 public: 394 public:
395 virtual int add_dvr(SrsDvrApiPlan* dvr); 395 virtual int add_dvr(SrsDvrApiPlan* dvr);
396 public: 396 public:
397 - virtual int dumps(std::string vhost, std::stringstream& ss); 397 + virtual int dumps(std::string vhost, std::string app, std::string stream, std::stringstream& ss);
398 virtual int create(SrsJsonAny* json); 398 virtual int create(SrsJsonAny* json);
399 - virtual int stop(std::string vhost); 399 + virtual int stop(std::string vhost, std::string app, std::string stream);
400 virtual int rpc(SrsJsonAny* json); 400 virtual int rpc(SrsJsonAny* json);
401 }; 401 };
402 402
@@ -494,7 +494,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) @@ -494,7 +494,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
494 SrsApiDvrPool* pool = SrsApiDvrPool::instance(); 494 SrsApiDvrPool* pool = SrsApiDvrPool::instance();
495 if (r->is_http_get()) { 495 if (r->is_http_get()) {
496 std::stringstream data; 496 std::stringstream data;
497 - int ret = pool->dumps(r->query_get("vhost"), data); 497 + int ret = pool->dumps(r->query_get("vhost"), r->query_get("app"), r->query_get("stream"), data);
498 498
499 ss << __SRS_JOBJECT_START 499 ss << __SRS_JOBJECT_START
500 << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT 500 << __SRS_JFIELD_ERROR(ret) << __SRS_JFIELD_CONT
@@ -514,7 +514,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) @@ -514,7 +514,7 @@ int SrsGoApiDvrs::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
514 << __SRS_JFIELD_ERROR(ret) 514 << __SRS_JFIELD_ERROR(ret)
515 << __SRS_JOBJECT_END; 515 << __SRS_JOBJECT_END;
516 } else if (r->is_http_delete()) { 516 } else if (r->is_http_delete()) {
517 - int ret = pool->stop(r->query_get("vhost")); 517 + int ret = pool->stop(r->query_get("vhost"), r->query_get("app"), r->query_get("stream"));
518 518
519 ss << __SRS_JOBJECT_START 519 ss << __SRS_JOBJECT_START
520 << __SRS_JFIELD_ERROR(ret) 520 << __SRS_JFIELD_ERROR(ret)
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // current release version 31 // current release version
32 #define VERSION_MAJOR 2 32 #define VERSION_MAJOR 2
33 #define VERSION_MINOR 0 33 #define VERSION_MINOR 0
34 -#define VERSION_REVISION 124 34 +#define VERSION_REVISION 125
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"
@@ -213,6 +213,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -213,6 +213,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
213 #define ERROR_HTTP_DVR_REQUEST 3051 213 #define ERROR_HTTP_DVR_REQUEST 3051
214 #define ERROR_HTTP_JSON_REQUIRED 3052 214 #define ERROR_HTTP_JSON_REQUIRED 3052
215 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053 215 #define ERROR_HTTP_DVR_CREATE_REQUEST 3053
  216 +#define ERROR_HTTP_DVR_NO_TAEGET 3054
216 217
217 /////////////////////////////////////////////////////// 218 ///////////////////////////////////////////////////////
218 // HTTP/StreamCaster protocol error. 219 // HTTP/StreamCaster protocol error.