winlin

fix #179, dvr support api to start or stop. 2.0.123

@@ -304,7 +304,7 @@ vhost dvr.srs.com { @@ -304,7 +304,7 @@ vhost dvr.srs.com {
304 # to start dvr of specified vhost. 304 # to start dvr of specified vhost.
305 # request should encode in json, specifies the dvr to create, where: 305 # request should encode in json, specifies the dvr to create, where:
306 # {path_tmpl:"./[15].[04].[05].[999].flv", 306 # {path_tmpl:"./[15].[04].[05].[999].flv",
307 - # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://dvr/callback" 307 + # wait_keyframe:true, vhost:"__defaultVhost", callback:"http://127.0.0.1:8085/api/v1/dvrs"
308 # } 308 # }
309 # response in json, where: 309 # response in json, where:
310 # {code:0} 310 # {code:0}
@@ -315,7 +315,12 @@ vhost dvr.srs.com { @@ -315,7 +315,12 @@ vhost dvr.srs.com {
315 # response in json, where: 315 # response in json, where:
316 # {code:0} 316 # {code:0}
317 # when reap segment, the callback POST request in json: 317 # when reap segment, the callback POST request in json:
318 - # {action:"on_dvr_reap_segment"} 318 + # {action:"on_dvr_reap_segment", client_id:100, vhost:"__defaultVhost__",
  319 + # app:"live", stream:"livestream", cwd:"/home/winlin/srs", file:"./dvr.flv"
  320 + # }
  321 + # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com
  322 + # @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
319 # default: session 324 # default: session
320 dvr_plan session; 325 dvr_plan session;
321 # the dvr output path. 326 # the dvr output path.
@@ -45,6 +45,9 @@ using namespace std; @@ -45,6 +45,9 @@ using namespace std;
45 // update the flv duration and filesize every this interval in ms. 45 // update the flv duration and filesize every this interval in ms.
46 #define __SRS_DVR_UPDATE_DURATION_INTERVAL 60000 46 #define __SRS_DVR_UPDATE_DURATION_INTERVAL 60000
47 47
  48 +// the sleep interval for http async callback.
  49 +#define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000
  50 +
48 SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p) 51 SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p)
49 { 52 {
50 req = NULL; 53 req = NULL;
@@ -201,29 +204,6 @@ int SrsFlvSegment::close() @@ -201,29 +204,6 @@ int SrsFlvSegment::close()
201 srs_error("dvr: notify plan to reap segment failed. ret=%d", ret); 204 srs_error("dvr: notify plan to reap segment failed. ret=%d", ret);
202 return ret; 205 return ret;
203 } 206 }
204 -  
205 -#ifdef SRS_AUTO_HTTP_CALLBACK  
206 - if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {  
207 - // HTTP: on_dvr  
208 - SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);  
209 - if (!on_dvr) {  
210 - srs_info("ignore the empty http callback: on_dvr");  
211 - return ret;  
212 - }  
213 -  
214 - int connection_id = _srs_context->get_id();  
215 - std::string ip = req->ip;  
216 - std::string cwd = _srs_config->cwd();  
217 - std::string file = path;  
218 - for (int i = 0; i < (int)on_dvr->args.size(); i++) {  
219 - std::string url = on_dvr->args.at(i);  
220 - if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {  
221 - srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);  
222 - return ret;  
223 - }  
224 - }  
225 - }  
226 -#endif  
227 207
228 return ret; 208 return ret;
229 } 209 }
@@ -578,6 +558,160 @@ int SrsFlvSegment::on_reload_vhost_dvr(std::string /*vhost*/) @@ -578,6 +558,160 @@ int SrsFlvSegment::on_reload_vhost_dvr(std::string /*vhost*/)
578 return ret; 558 return ret;
579 } 559 }
580 560
  561 +ISrsDvrAsyncCall::ISrsDvrAsyncCall()
  562 +{
  563 +}
  564 +
  565 +ISrsDvrAsyncCall::~ISrsDvrAsyncCall()
  566 +{
  567 +}
  568 +
  569 +SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(SrsRequest* r, string p)
  570 +{
  571 + req = r;
  572 + path = p;
  573 +}
  574 +
  575 +SrsDvrAsyncCallOnDvr::~SrsDvrAsyncCallOnDvr()
  576 +{
  577 +}
  578 +
  579 +int SrsDvrAsyncCallOnDvr::call()
  580 +{
  581 + int ret = ERROR_SUCCESS;
  582 +
  583 +#ifdef SRS_AUTO_HTTP_CALLBACK
  584 + // http callback for on_dvr in config.
  585 + if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
  586 + // HTTP: on_dvr
  587 + SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);
  588 + if (!on_dvr) {
  589 + srs_info("ignore the empty http callback: on_dvr");
  590 + return ret;
  591 + }
  592 +
  593 + int connection_id = _srs_context->get_id();
  594 + std::string ip = req->ip;
  595 + std::string cwd = _srs_config->cwd();
  596 + std::string file = path;
  597 + for (int i = 0; i < (int)on_dvr->args.size(); i++) {
  598 + std::string url = on_dvr->args.at(i);
  599 + if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {
  600 + srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);
  601 + return ret;
  602 + }
  603 + }
  604 + }
  605 +#endif
  606 +
  607 + return ret;
  608 +}
  609 +
  610 +string SrsDvrAsyncCallOnDvr::to_string()
  611 +{
  612 + std::stringstream ss;
  613 + ss << "vhost=" << req->vhost << ", file=" << path;
  614 + return ss.str();
  615 +}
  616 +
  617 +SrsDvrAsyncCallOnSegment::SrsDvrAsyncCallOnSegment(SrsRequest* r, string c, string p)
  618 +{
  619 + req = r;
  620 + callback = c;
  621 + path = p;
  622 +}
  623 +
  624 +SrsDvrAsyncCallOnSegment::~SrsDvrAsyncCallOnSegment()
  625 +{
  626 +}
  627 +
  628 +int SrsDvrAsyncCallOnSegment::call()
  629 +{
  630 + int ret = ERROR_SUCCESS;
  631 +
  632 +#ifdef SRS_AUTO_HTTP_CALLBACK
  633 + // HTTP: callback
  634 + if (callback.empty()) {
  635 + srs_warn("dvr: ignore for callback empty, vhost=%s", req->vhost.c_str());
  636 + return ret;
  637 + }
  638 +
  639 + int connection_id = _srs_context->get_id();
  640 + std::string cwd = _srs_config->cwd();
  641 + std::string file = path;
  642 + std::string url = callback;
  643 + if ((ret = SrsHttpHooks::on_dvr_reap_segment(url, connection_id, req, cwd, file)) != ERROR_SUCCESS) {
  644 + srs_error("hook client on_dvr_reap_segment failed. url=%s, ret=%d", url.c_str(), ret);
  645 + return ret;
  646 + }
  647 +#endif
  648 +
  649 + return ret;
  650 +}
  651 +
  652 +string SrsDvrAsyncCallOnSegment::to_string()
  653 +{
  654 + std::stringstream ss;
  655 + ss << "vhost=" << req->vhost << ", file=" << path << "callback=" << callback;
  656 + return ss.str();
  657 +}
  658 +
  659 +SrsDvrAsyncCallThread::SrsDvrAsyncCallThread()
  660 +{
  661 + pthread = new SrsThread("async", this, SRS_AUTO_ASYNC_CALLBACL_SLEEP_US, true);
  662 +}
  663 +
  664 +SrsDvrAsyncCallThread::~SrsDvrAsyncCallThread()
  665 +{
  666 + stop();
  667 + srs_freep(pthread);
  668 +
  669 + std::vector<ISrsDvrAsyncCall*>::iterator it;
  670 + for (it = callbacks.begin(); it != callbacks.end(); ++it) {
  671 + ISrsDvrAsyncCall* call = *it;
  672 + srs_freep(call);
  673 + }
  674 + callbacks.clear();
  675 +}
  676 +
  677 +int SrsDvrAsyncCallThread::call(ISrsDvrAsyncCall* c)
  678 +{
  679 + int ret = ERROR_SUCCESS;
  680 +
  681 + callbacks.push_back(c);
  682 +
  683 + return ret;
  684 +}
  685 +
  686 +int SrsDvrAsyncCallThread::start()
  687 +{
  688 + return pthread->start();
  689 +}
  690 +
  691 +void SrsDvrAsyncCallThread::stop()
  692 +{
  693 + pthread->stop();
  694 +}
  695 +
  696 +int SrsDvrAsyncCallThread::cycle()
  697 +{
  698 + int ret = ERROR_SUCCESS;
  699 +
  700 + std::vector<ISrsDvrAsyncCall*> copies = callbacks;
  701 + callbacks.clear();
  702 +
  703 + std::vector<ISrsDvrAsyncCall*>::iterator it;
  704 + for (it = copies.begin(); it != copies.end(); ++it) {
  705 + ISrsDvrAsyncCall* call = *it;
  706 + if ((ret = call->call()) != ERROR_SUCCESS) {
  707 + srs_warn("dvr: ignore callback %s, ret=%d", call->to_string().c_str(), ret);
  708 + }
  709 + srs_freep(call);
  710 + }
  711 +
  712 + return ret;
  713 +}
  714 +
581 SrsDvrPlan::SrsDvrPlan() 715 SrsDvrPlan::SrsDvrPlan()
582 { 716 {
583 source = NULL; 717 source = NULL;
@@ -585,11 +719,13 @@ SrsDvrPlan::SrsDvrPlan() @@ -585,11 +719,13 @@ SrsDvrPlan::SrsDvrPlan()
585 719
586 dvr_enabled = false; 720 dvr_enabled = false;
587 segment = new SrsFlvSegment(this); 721 segment = new SrsFlvSegment(this);
  722 + async = new SrsDvrAsyncCallThread();
588 } 723 }
589 724
590 SrsDvrPlan::~SrsDvrPlan() 725 SrsDvrPlan::~SrsDvrPlan()
591 { 726 {
592 srs_freep(segment); 727 srs_freep(segment);
  728 + srs_freep(async);
593 } 729 }
594 730
595 int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r) 731 int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r)
@@ -603,6 +739,10 @@ int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r) @@ -603,6 +739,10 @@ int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r)
603 return ret; 739 return ret;
604 } 740 }
605 741
  742 + if ((ret = async->start()) != ERROR_SUCCESS) {
  743 + return ret;
  744 + }
  745 +
606 return ret; 746 return ret;
607 } 747 }
608 748
@@ -671,7 +811,13 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) @@ -671,7 +811,13 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video)
671 811
672 int SrsDvrPlan::on_reap_segment() 812 int SrsDvrPlan::on_reap_segment()
673 { 813 {
674 - return ERROR_SUCCESS; 814 + int ret = ERROR_SUCCESS;
  815 +
  816 + if ((ret = async->call(new SrsDvrAsyncCallOnDvr(req, segment->get_path()))) != ERROR_SUCCESS) {
  817 + return ret;
  818 + }
  819 +
  820 + return ret;
675 } 821 }
676 822
677 SrsDvrPlan* SrsDvrPlan::create_plan(string vhost) 823 SrsDvrPlan* SrsDvrPlan::create_plan(string vhost)
@@ -958,23 +1104,14 @@ int SrsDvrApiPlan::stop() @@ -958,23 +1104,14 @@ int SrsDvrApiPlan::stop()
958 int SrsDvrApiPlan::on_reap_segment() 1104 int SrsDvrApiPlan::on_reap_segment()
959 { 1105 {
960 int ret = ERROR_SUCCESS; 1106 int ret = ERROR_SUCCESS;
961 -  
962 -#ifdef SRS_AUTO_HTTP_CALLBACK  
963 - // HTTP: callback  
964 - if (callback.empty()) {  
965 - srs_warn("dvr: ignore for callback empty, vhost=%s", req->vhost.c_str()); 1107 +
  1108 + if ((ret = SrsDvrPlan::on_reap_segment()) != ERROR_SUCCESS) {
966 return ret; 1109 return ret;
967 } 1110 }
968 -  
969 - int connection_id = _srs_context->get_id();  
970 - std::string cwd = _srs_config->cwd();  
971 - std::string file = segment->get_path();  
972 - std::string url = callback;  
973 - if ((ret = SrsHttpHooks::on_dvr_reap_segment(url, connection_id, req, cwd, file)) != ERROR_SUCCESS) {  
974 - srs_error("hook client on_dvr_reap_segment failed. url=%s, ret=%d", url.c_str(), ret); 1111 +
  1112 + if ((ret = async->call(new SrsDvrAsyncCallOnSegment(req, callback, segment->get_path()))) != ERROR_SUCCESS) {
975 return ret; 1113 return ret;
976 } 1114 }
977 -#endif  
978 1115
979 return ret; 1116 return ret;
980 } 1117 }
@@ -44,9 +44,11 @@ class SrsFileWriter; @@ -44,9 +44,11 @@ class SrsFileWriter;
44 class SrsFlvEncoder; 44 class SrsFlvEncoder;
45 class SrsDvrPlan; 45 class SrsDvrPlan;
46 class SrsJsonAny; 46 class SrsJsonAny;
  47 +class SrsThread;
47 48
48 #include <srs_app_source.hpp> 49 #include <srs_app_source.hpp>
49 #include <srs_app_reload.hpp> 50 #include <srs_app_reload.hpp>
  51 +#include <srs_app_thread.hpp>
50 52
51 /** 53 /**
52 * a piece of flv segment. 54 * a piece of flv segment.
@@ -174,6 +176,63 @@ public: @@ -174,6 +176,63 @@ public:
174 }; 176 };
175 177
176 /** 178 /**
  179 +* the dvr async call.
  180 +*/
  181 +class ISrsDvrAsyncCall
  182 +{
  183 +public:
  184 + ISrsDvrAsyncCall();
  185 + virtual ~ISrsDvrAsyncCall();
  186 +public:
  187 + virtual int call() = 0;
  188 + virtual std::string to_string() = 0;
  189 +};
  190 +class SrsDvrAsyncCallOnDvr : public ISrsDvrAsyncCall
  191 +{
  192 +private:
  193 + std::string path;
  194 + SrsRequest* req;
  195 +public:
  196 + SrsDvrAsyncCallOnDvr(SrsRequest* r, std::string p);
  197 + virtual ~SrsDvrAsyncCallOnDvr();
  198 +public:
  199 + virtual int call();
  200 + virtual std::string to_string();
  201 +};
  202 +class SrsDvrAsyncCallOnSegment : public ISrsDvrAsyncCall
  203 +{
  204 +private:
  205 + std::string callback;
  206 + std::string path;
  207 + SrsRequest* req;
  208 +public:
  209 + SrsDvrAsyncCallOnSegment(SrsRequest* r, std::string c, std::string p);
  210 + virtual ~SrsDvrAsyncCallOnSegment();
  211 +public:
  212 + virtual int call();
  213 + virtual std::string to_string();
  214 +};
  215 +
  216 +/**
  217 +* the async callback for dvr.
  218 +*/
  219 +class SrsDvrAsyncCallThread : public ISrsThreadHandler
  220 +{
  221 +private:
  222 + SrsThread* pthread;
  223 + std::vector<ISrsDvrAsyncCall*> callbacks;
  224 +public:
  225 + SrsDvrAsyncCallThread();
  226 + virtual ~SrsDvrAsyncCallThread();
  227 +public:
  228 + virtual int call(ISrsDvrAsyncCall* c);
  229 +public:
  230 + virtual int start();
  231 + virtual void stop();
  232 + virtual int cycle();
  233 +};
  234 +
  235 +/**
177 * the plan for dvr. 236 * the plan for dvr.
178 * use to control the following dvr params: 237 * use to control the following dvr params:
179 * 1. filename: the filename for record file. 238 * 1. filename: the filename for record file.
@@ -189,6 +248,7 @@ public: @@ -189,6 +248,7 @@ public:
189 protected: 248 protected:
190 SrsSource* source; 249 SrsSource* source;
191 SrsFlvSegment* segment; 250 SrsFlvSegment* segment;
  251 + SrsDvrAsyncCallThread* async;
192 bool dvr_enabled; 252 bool dvr_enabled;
193 public: 253 public:
194 SrsDvrPlan(); 254 SrsDvrPlan();
@@ -851,9 +851,18 @@ SrsHttpMessage::~SrsHttpMessage() @@ -851,9 +851,18 @@ SrsHttpMessage::~SrsHttpMessage()
851 int SrsHttpMessage::initialize() 851 int SrsHttpMessage::initialize()
852 { 852 {
853 int ret = ERROR_SUCCESS; 853 int ret = ERROR_SUCCESS;
  854 +
  855 + std::string host = get_request_header("Host");
  856 +
  857 + // donot parse the empty host for uri,
  858 + // for example, the response contains no host,
  859 + // ignore it is ok.
  860 + if (host.empty()) {
  861 + return ret;
  862 + }
854 863
855 // parse uri to schema/server:port/path?query 864 // parse uri to schema/server:port/path?query
856 - std::string uri = "http://" + get_request_header("Host") + _url; 865 + std::string uri = "http://" + host + _url;
857 if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) { 866 if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) {
858 return ret; 867 return ret;
859 } 868 }
@@ -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 122 34 +#define VERSION_REVISION 123
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"