winlin

fix #367, support nginx-rtmp exec. 3.0.1

@@ -219,6 +219,7 @@ Supported operating systems and hardware: @@ -219,6 +219,7 @@ Supported operating systems and hardware:
219 1. [experiment] Support push flv stream over HTTP POST to SRS, read [wiki]([CN][v2_CN_Streamer2], [EN][v2_EN_Streamer2]). 219 1. [experiment] Support push flv stream over HTTP POST to SRS, read [wiki]([CN][v2_CN_Streamer2], [EN][v2_EN_Streamer2]).
220 1. [experiment] Support [srs-dolphin][srs-dolphin], the multiple-process SRS. 220 1. [experiment] Support [srs-dolphin][srs-dolphin], the multiple-process SRS.
221 1. [experiment] Support [remote console](http://ossrs.net:1985/console), read [srs-ngb][srs-ngb]. 221 1. [experiment] Support [remote console](http://ossrs.net:1985/console), read [srs-ngb][srs-ngb].
  222 +1. Support nginx-rtmp style exec, read [bug #367][bug #367].
222 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 223 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
223 1. [no-plan] Support RTMP 302 redirect [bug #92][bug #92]. 224 1. [no-plan] Support RTMP 302 redirect [bug #92][bug #92].
224 1. [no-plan] Support multiple processes, for both origin and edge 225 1. [no-plan] Support multiple processes, for both origin and edge
@@ -344,6 +345,7 @@ Remark: @@ -344,6 +345,7 @@ Remark:
344 345
345 ## History 346 ## History
346 347
  348 +* v3.0, 2015-08-25, fix [#367](https://github.com/simple-rtmp-server/srs/issues/367), support nginx-rtmp exec. 3.0.1
347 * <strong>v2.0, 2015-08-23, [2.0 alpha(2.0.185)](https://github.com/simple-rtmp-server/srs/releases/tag/2.0a0) released. 89022 lines.</strong> 349 * <strong>v2.0, 2015-08-23, [2.0 alpha(2.0.185)](https://github.com/simple-rtmp-server/srs/releases/tag/2.0a0) released. 89022 lines.</strong>
348 * v2.0, 2015-08-22, HTTP API support JSONP by specifies the query string callback=xxx. 350 * v2.0, 2015-08-22, HTTP API support JSONP by specifies the query string callback=xxx.
349 * v2.0, 2015-08-20, fix [#380](https://github.com/simple-rtmp-server/srs/issues/380), srs-librtmp send sequence header when sps or pps changed. 351 * v2.0, 2015-08-20, fix [#380](https://github.com/simple-rtmp-server/srs/issues/380), srs-librtmp send sequence header when sps or pps changed.
@@ -998,6 +1000,7 @@ Winlin @@ -998,6 +1000,7 @@ Winlin
998 [bug #304]: https://github.com/simple-rtmp-server/srs/issues/304 1000 [bug #304]: https://github.com/simple-rtmp-server/srs/issues/304
999 [bug #133]: https://github.com/simple-rtmp-server/srs/issues/133 1001 [bug #133]: https://github.com/simple-rtmp-server/srs/issues/133
1000 [bug #92]: https://github.com/simple-rtmp-server/srs/issues/92 1002 [bug #92]: https://github.com/simple-rtmp-server/srs/issues/92
  1003 +[bug #367]: https://github.com/simple-rtmp-server/srs/issues/367
1001 1004
1002 1005
1003 [contact]: https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Contact 1006 [contact]: https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Contact
  1 +# the config for srs to support nginx-rtmp exec.
  2 +# @see https://github.com/simple-rtmp-server/srs/wiki/v2_CN_NgExec
  3 +# @see full.conf for detail config.
  4 +
  5 +listen 1935;
  6 +max_connections 1000;
  7 +vhost __defaultVhost__ {
  8 + exec {
  9 + enabled on;
  10 + publish ./objs/ffmpeg/bin/ffmpeg -f flv -i [url] -c copy -y ./[stream].flv;
  11 + }
  12 +}
@@ -934,6 +934,9 @@ vhost exec.srs.com { @@ -934,6 +934,9 @@ vhost exec.srs.com {
934 # [engine] the tanscode engine name. 934 # [engine] the tanscode engine name.
935 # other variables for exec only: 935 # other variables for exec only:
936 # [url] the rtmp url which trigger the publish. 936 # [url] the rtmp url which trigger the publish.
  937 + # [tcUrl] the client request tcUrl.
  938 + # [swfUrl] the client request swfUrl.
  939 + # [pageUrl] the client request pageUrl.
937 # @remark empty to ignore this exec. 940 # @remark empty to ignore this exec.
938 publish ./objs/ffmpeg/bin/ffmpeg -f flv -i [url] -c copy -y ./[stream].flv; 941 publish ./objs/ffmpeg/bin/ffmpeg -f flv -i [url] -c copy -y ./[stream].flv;
939 } 942 }
@@ -176,7 +176,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then @@ -176,7 +176,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
176 "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static" 176 "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
177 "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds" 177 "srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
178 "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" 178 "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
179 - "srs_app_caster_flv" "srs_app_process") 179 + "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec")
180 DEFINES="" 180 DEFINES=""
181 # add each modules for app 181 # add each modules for app
182 for SRS_MODULE in ${SRS_MODULES[*]}; do 182 for SRS_MODULE in ${SRS_MODULES[*]}; do
@@ -117,6 +117,8 @@ file @@ -117,6 +117,8 @@ file
117 ../../src/app/srs_app_log.cpp, 117 ../../src/app/srs_app_log.cpp,
118 ../../src/app/srs_app_mpegts_udp.hpp, 118 ../../src/app/srs_app_mpegts_udp.hpp,
119 ../../src/app/srs_app_mpegts_udp.cpp, 119 ../../src/app/srs_app_mpegts_udp.cpp,
  120 + ../../src/app/srs_app_ng_exec.hpp,
  121 + ../../src/app/srs_app_ng_exec.cpp,
120 ../../src/app/srs_app_process.hpp, 122 ../../src/app/srs_app_process.hpp,
121 ../../src/app/srs_app_process.cpp, 123 ../../src/app/srs_app_process.cpp,
122 ../../src/app/srs_app_recv_thread.hpp, 124 ../../src/app/srs_app_recv_thread.hpp,
@@ -79,6 +79,7 @@ @@ -79,6 +79,7 @@
79 3C36DB5B1ABD1CB90066CCAF /* srs_lib_bandwidth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB551ABD1CB90066CCAF /* srs_lib_bandwidth.cpp */; }; 79 3C36DB5B1ABD1CB90066CCAF /* srs_lib_bandwidth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB551ABD1CB90066CCAF /* srs_lib_bandwidth.cpp */; };
80 3C36DB5C1ABD1CB90066CCAF /* srs_lib_simple_socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB571ABD1CB90066CCAF /* srs_lib_simple_socket.cpp */; }; 80 3C36DB5C1ABD1CB90066CCAF /* srs_lib_simple_socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB571ABD1CB90066CCAF /* srs_lib_simple_socket.cpp */; };
81 3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */; }; 81 3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */; };
  82 + 3C4AB9331B8C9148006627D3 /* srs_app_ng_exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */; };
82 3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */; }; 83 3C4F97121B8B466D00FF0E46 /* srs_app_process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */; };
83 3C5265B41B241BF0009CA186 /* srs_core_mem_watch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */; }; 84 3C5265B41B241BF0009CA186 /* srs_core_mem_watch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */; };
84 3C663F0F1AB0155100286D8B /* srs_aac_raw_publish.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C663F021AB0155100286D8B /* srs_aac_raw_publish.c */; }; 85 3C663F0F1AB0155100286D8B /* srs_aac_raw_publish.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C663F021AB0155100286D8B /* srs_aac_raw_publish.c */; };
@@ -329,6 +330,9 @@ @@ -329,6 +330,9 @@
329 3C36DB581ABD1CB90066CCAF /* srs_lib_simple_socket.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_lib_simple_socket.hpp; path = ../../../src/libs/srs_lib_simple_socket.hpp; sourceTree = "<group>"; }; 330 3C36DB581ABD1CB90066CCAF /* srs_lib_simple_socket.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_lib_simple_socket.hpp; path = ../../../src/libs/srs_lib_simple_socket.hpp; sourceTree = "<group>"; };
330 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_librtmp.cpp; path = ../../../src/libs/srs_librtmp.cpp; sourceTree = "<group>"; }; 331 3C36DB591ABD1CB90066CCAF /* srs_librtmp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_librtmp.cpp; path = ../../../src/libs/srs_librtmp.cpp; sourceTree = "<group>"; };
331 3C36DB5A1ABD1CB90066CCAF /* srs_librtmp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_librtmp.hpp; path = ../../../src/libs/srs_librtmp.hpp; sourceTree = "<group>"; }; 332 3C36DB5A1ABD1CB90066CCAF /* srs_librtmp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_librtmp.hpp; path = ../../../src/libs/srs_librtmp.hpp; sourceTree = "<group>"; };
  333 + 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_ng_exec.cpp; path = ../../../src/app/srs_app_ng_exec.cpp; sourceTree = "<group>"; };
  334 + 3C4AB9321B8C9148006627D3 /* srs_app_ng_exec.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_ng_exec.hpp; path = ../../../src/app/srs_app_ng_exec.hpp; sourceTree = "<group>"; };
  335 + 3C4AB9341B8C9FF9006627D3 /* exec.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = exec.conf; path = ../../../conf/exec.conf; sourceTree = "<group>"; };
332 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_process.cpp; path = ../../../src/app/srs_app_process.cpp; sourceTree = "<group>"; }; 336 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_process.cpp; path = ../../../src/app/srs_app_process.cpp; sourceTree = "<group>"; };
333 3C4F97111B8B466D00FF0E46 /* srs_app_process.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_process.hpp; path = ../../../src/app/srs_app_process.hpp; sourceTree = "<group>"; }; 337 3C4F97111B8B466D00FF0E46 /* srs_app_process.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_process.hpp; path = ../../../src/app/srs_app_process.hpp; sourceTree = "<group>"; };
334 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_core_mem_watch.cpp; path = ../../../src/core/srs_core_mem_watch.cpp; sourceTree = "<group>"; }; 338 3C5265B21B241BF0009CA186 /* srs_core_mem_watch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_core_mem_watch.cpp; path = ../../../src/core/srs_core_mem_watch.cpp; sourceTree = "<group>"; };
@@ -574,6 +578,8 @@ @@ -574,6 +578,8 @@
574 3C1232751AAE81D900CE8F6C /* srs_app_log.hpp */, 578 3C1232751AAE81D900CE8F6C /* srs_app_log.hpp */,
575 3C1232761AAE81D900CE8F6C /* srs_app_mpegts_udp.cpp */, 579 3C1232761AAE81D900CE8F6C /* srs_app_mpegts_udp.cpp */,
576 3C1232771AAE81D900CE8F6C /* srs_app_mpegts_udp.hpp */, 580 3C1232771AAE81D900CE8F6C /* srs_app_mpegts_udp.hpp */,
  581 + 3C4AB9311B8C9148006627D3 /* srs_app_ng_exec.cpp */,
  582 + 3C4AB9321B8C9148006627D3 /* srs_app_ng_exec.hpp */,
577 3C1232781AAE81D900CE8F6C /* srs_app_pithy_print.cpp */, 583 3C1232781AAE81D900CE8F6C /* srs_app_pithy_print.cpp */,
578 3C1232791AAE81D900CE8F6C /* srs_app_pithy_print.hpp */, 584 3C1232791AAE81D900CE8F6C /* srs_app_pithy_print.hpp */,
579 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */, 585 3C4F97101B8B466D00FF0E46 /* srs_app_process.cpp */,
@@ -676,6 +682,7 @@ @@ -676,6 +682,7 @@
676 3C1EE6AF1AB107EE00576EE9 /* conf */ = { 682 3C1EE6AF1AB107EE00576EE9 /* conf */ = {
677 isa = PBXGroup; 683 isa = PBXGroup;
678 children = ( 684 children = (
  685 + 3C4AB9341B8C9FF9006627D3 /* exec.conf */,
679 3C1EE6B01AB1080900576EE9 /* bandwidth.conf */, 686 3C1EE6B01AB1080900576EE9 /* bandwidth.conf */,
680 3C1EE6B11AB1080900576EE9 /* console.conf */, 687 3C1EE6B11AB1080900576EE9 /* console.conf */,
681 3C1EE6B21AB1080900576EE9 /* demo.19350.conf */, 688 3C1EE6B21AB1080900576EE9 /* demo.19350.conf */,
@@ -902,6 +909,7 @@ @@ -902,6 +909,7 @@
902 3C689F981AB6AAAC00C9CEEE /* key.c in Sources */, 909 3C689F981AB6AAAC00C9CEEE /* key.c in Sources */,
903 3C12329B1AAE81D900CE8F6C /* srs_app_ffmpeg.cpp in Sources */, 910 3C12329B1AAE81D900CE8F6C /* srs_app_ffmpeg.cpp in Sources */,
904 3C1232421AAE81A400CE8F6C /* srs_rtmp_amf0.cpp in Sources */, 911 3C1232421AAE81A400CE8F6C /* srs_rtmp_amf0.cpp in Sources */,
  912 + 3C4AB9331B8C9148006627D3 /* srs_app_ng_exec.cpp in Sources */,
905 3C1232AA1AAE81D900CE8F6C /* srs_app_pithy_print.cpp in Sources */, 913 3C1232AA1AAE81D900CE8F6C /* srs_app_pithy_print.cpp in Sources */,
906 3C12329C1AAE81D900CE8F6C /* srs_app_forward.cpp in Sources */, 914 3C12329C1AAE81D900CE8F6C /* srs_app_forward.cpp in Sources */,
907 3C1232251AAE814D00CE8F6C /* srs_kernel_file.cpp in Sources */, 915 3C1232251AAE814D00CE8F6C /* srs_kernel_file.cpp in Sources */,
@@ -633,6 +633,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -633,6 +633,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
633 } 633 }
634 srs_trace("vhost %s reload atc success.", vhost.c_str()); 634 srs_trace("vhost %s reload atc success.", vhost.c_str());
635 } 635 }
  636 +
636 // gop_cache, only one per vhost 637 // gop_cache, only one per vhost
637 if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) { 638 if (!srs_directive_equals(new_vhost->get("gop_cache"), old_vhost->get("gop_cache"))) {
638 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 639 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -644,6 +645,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -644,6 +645,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
644 } 645 }
645 srs_trace("vhost %s reload gop_cache success.", vhost.c_str()); 646 srs_trace("vhost %s reload gop_cache success.", vhost.c_str());
646 } 647 }
  648 +
647 // queue_length, only one per vhost 649 // queue_length, only one per vhost
648 if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) { 650 if (!srs_directive_equals(new_vhost->get("queue_length"), old_vhost->get("queue_length"))) {
649 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 651 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -655,6 +657,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -655,6 +657,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
655 } 657 }
656 srs_trace("vhost %s reload queue_length success.", vhost.c_str()); 658 srs_trace("vhost %s reload queue_length success.", vhost.c_str());
657 } 659 }
  660 +
658 // time_jitter, only one per vhost 661 // time_jitter, only one per vhost
659 if (!srs_directive_equals(new_vhost->get("time_jitter"), old_vhost->get("time_jitter"))) { 662 if (!srs_directive_equals(new_vhost->get("time_jitter"), old_vhost->get("time_jitter"))) {
660 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 663 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -666,6 +669,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -666,6 +669,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
666 } 669 }
667 srs_trace("vhost %s reload time_jitter success.", vhost.c_str()); 670 srs_trace("vhost %s reload time_jitter success.", vhost.c_str());
668 } 671 }
  672 +
669 // mix_correct, only one per vhost 673 // mix_correct, only one per vhost
670 if (!srs_directive_equals(new_vhost->get("mix_correct"), old_vhost->get("mix_correct"))) { 674 if (!srs_directive_equals(new_vhost->get("mix_correct"), old_vhost->get("mix_correct"))) {
671 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 675 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -677,6 +681,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -677,6 +681,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
677 } 681 }
678 srs_trace("vhost %s reload mix_correct success.", vhost.c_str()); 682 srs_trace("vhost %s reload mix_correct success.", vhost.c_str());
679 } 683 }
  684 +
680 // forward, only one per vhost 685 // forward, only one per vhost
681 if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) { 686 if (!srs_directive_equals(new_vhost->get("forward"), old_vhost->get("forward"))) {
682 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 687 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -688,6 +693,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -688,6 +693,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
688 } 693 }
689 srs_trace("vhost %s reload forward success.", vhost.c_str()); 694 srs_trace("vhost %s reload forward success.", vhost.c_str());
690 } 695 }
  696 +
691 // hls, only one per vhost 697 // hls, only one per vhost
692 // @remark, the hls_on_error directly support reload. 698 // @remark, the hls_on_error directly support reload.
693 if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) { 699 if (!srs_directive_equals(new_vhost->get("hls"), old_vhost->get("hls"))) {
@@ -722,8 +728,21 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -722,8 +728,21 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
722 return ret; 728 return ret;
723 } 729 }
724 } 730 }
725 - srs_trace("vhost %s reload hlsdvrsuccess.", vhost.c_str()); 731 + srs_trace("vhost %s reload dvr success.", vhost.c_str());
726 } 732 }
  733 +
  734 + // exec, only one per vhost
  735 + if (!srs_directive_equals(new_vhost->get("exec"), old_vhost->get("exec"))) {
  736 + for (it = subscribes.begin(); it != subscribes.end(); ++it) {
  737 + ISrsReloadHandler* subscribe = *it;
  738 + if ((ret = subscribe->on_reload_vhost_exec(vhost)) != ERROR_SUCCESS) {
  739 + srs_error("vhost %s notify subscribes exec failed. ret=%d", vhost.c_str(), ret);
  740 + return ret;
  741 + }
  742 + }
  743 + srs_trace("vhost %s reload exec success.", vhost.c_str());
  744 + }
  745 +
727 // mr, only one per vhost 746 // mr, only one per vhost
728 if (!srs_directive_equals(new_vhost->get("mr"), old_vhost->get("mr"))) { 747 if (!srs_directive_equals(new_vhost->get("mr"), old_vhost->get("mr"))) {
729 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 748 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -735,6 +754,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -735,6 +754,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
735 } 754 }
736 srs_trace("vhost %s reload mr success.", vhost.c_str()); 755 srs_trace("vhost %s reload mr success.", vhost.c_str());
737 } 756 }
  757 +
738 // chunk_size, only one per vhost. 758 // chunk_size, only one per vhost.
739 if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) { 759 if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) {
740 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 760 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -746,6 +766,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -746,6 +766,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
746 } 766 }
747 srs_trace("vhost %s reload chunk_size success.", vhost.c_str()); 767 srs_trace("vhost %s reload chunk_size success.", vhost.c_str());
748 } 768 }
  769 +
749 // mw, only one per vhost 770 // mw, only one per vhost
750 if (!srs_directive_equals(new_vhost->get("mw_latency"), old_vhost->get("mw_latency"))) { 771 if (!srs_directive_equals(new_vhost->get("mw_latency"), old_vhost->get("mw_latency"))) {
751 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 772 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -757,6 +778,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -757,6 +778,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
757 } 778 }
758 srs_trace("vhost %s reload mw success.", vhost.c_str()); 779 srs_trace("vhost %s reload mw success.", vhost.c_str());
759 } 780 }
  781 +
760 // smi(send_min_interval), only one per vhost 782 // smi(send_min_interval), only one per vhost
761 if (!srs_directive_equals(new_vhost->get("send_min_interval"), old_vhost->get("send_min_interval"))) { 783 if (!srs_directive_equals(new_vhost->get("send_min_interval"), old_vhost->get("send_min_interval"))) {
762 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 784 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -768,6 +790,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -768,6 +790,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
768 } 790 }
769 srs_trace("vhost %s reload smi success.", vhost.c_str()); 791 srs_trace("vhost %s reload smi success.", vhost.c_str());
770 } 792 }
  793 +
771 // tcp_nodelay, only one per vhost 794 // tcp_nodelay, only one per vhost
772 if (!srs_directive_equals(new_vhost->get("tcp_nodelay"), old_vhost->get("tcp_nodelay"))) { 795 if (!srs_directive_equals(new_vhost->get("tcp_nodelay"), old_vhost->get("tcp_nodelay"))) {
773 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 796 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -779,6 +802,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -779,6 +802,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
779 } 802 }
780 srs_trace("vhost %s reload tcp_nodelay success.", vhost.c_str()); 803 srs_trace("vhost %s reload tcp_nodelay success.", vhost.c_str());
781 } 804 }
  805 +
782 // publish_1stpkt_timeout, only one per vhost 806 // publish_1stpkt_timeout, only one per vhost
783 if (!srs_directive_equals(new_vhost->get("publish_1stpkt_timeout"), old_vhost->get("publish_1stpkt_timeout"))) { 807 if (!srs_directive_equals(new_vhost->get("publish_1stpkt_timeout"), old_vhost->get("publish_1stpkt_timeout"))) {
784 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 808 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -790,6 +814,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -790,6 +814,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
790 } 814 }
791 srs_trace("vhost %s reload p1stpt success.", vhost.c_str()); 815 srs_trace("vhost %s reload p1stpt success.", vhost.c_str());
792 } 816 }
  817 +
793 // publish_normal_timeout, only one per vhost 818 // publish_normal_timeout, only one per vhost
794 if (!srs_directive_equals(new_vhost->get("publish_normal_timeout"), old_vhost->get("publish_normal_timeout"))) { 819 if (!srs_directive_equals(new_vhost->get("publish_normal_timeout"), old_vhost->get("publish_normal_timeout"))) {
795 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 820 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -801,6 +826,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -801,6 +826,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
801 } 826 }
802 srs_trace("vhost %s reload pnt success.", vhost.c_str()); 827 srs_trace("vhost %s reload pnt success.", vhost.c_str());
803 } 828 }
  829 +
804 // min_latency, only one per vhost 830 // min_latency, only one per vhost
805 if (!srs_directive_equals(new_vhost->get("min_latency"), old_vhost->get("min_latency"))) { 831 if (!srs_directive_equals(new_vhost->get("min_latency"), old_vhost->get("min_latency"))) {
806 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 832 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -812,6 +838,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -812,6 +838,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
812 } 838 }
813 srs_trace("vhost %s reload min_latency success.", vhost.c_str()); 839 srs_trace("vhost %s reload min_latency success.", vhost.c_str());
814 } 840 }
  841 +
815 // http, only one per vhost. 842 // http, only one per vhost.
816 if (!srs_directive_equals(new_vhost->get("http"), old_vhost->get("http"))) { 843 if (!srs_directive_equals(new_vhost->get("http"), old_vhost->get("http"))) {
817 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 844 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -823,6 +850,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -823,6 +850,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
823 } 850 }
824 srs_trace("vhost %s reload http success.", vhost.c_str()); 851 srs_trace("vhost %s reload http success.", vhost.c_str());
825 } 852 }
  853 +
826 // http_static, only one per vhost. 854 // http_static, only one per vhost.
827 // @remark, http_static introduced as alias of http. 855 // @remark, http_static introduced as alias of http.
828 if (!srs_directive_equals(new_vhost->get("http_static"), old_vhost->get("http_static"))) { 856 if (!srs_directive_equals(new_vhost->get("http_static"), old_vhost->get("http_static"))) {
@@ -835,6 +863,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -835,6 +863,7 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
835 } 863 }
836 srs_trace("vhost %s reload http_static success.", vhost.c_str()); 864 srs_trace("vhost %s reload http_static success.", vhost.c_str());
837 } 865 }
  866 +
838 // http_remux, only one per vhost. 867 // http_remux, only one per vhost.
839 if (!srs_directive_equals(new_vhost->get("http_remux"), old_vhost->get("http_remux"))) { 868 if (!srs_directive_equals(new_vhost->get("http_remux"), old_vhost->get("http_remux"))) {
840 for (it = subscribes.begin(); it != subscribes.end(); ++it) { 869 for (it = subscribes.begin(); it != subscribes.end(); ++it) {
@@ -846,10 +875,12 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root) @@ -846,10 +875,12 @@ int SrsConfig::reload_vhost(SrsConfDirective* old_root)
846 } 875 }
847 srs_trace("vhost %s reload http_remux success.", vhost.c_str()); 876 srs_trace("vhost %s reload http_remux success.", vhost.c_str());
848 } 877 }
  878 +
849 // transcode, many per vhost. 879 // transcode, many per vhost.
850 if ((ret = reload_transcode(new_vhost, old_vhost)) != ERROR_SUCCESS) { 880 if ((ret = reload_transcode(new_vhost, old_vhost)) != ERROR_SUCCESS) {
851 return ret; 881 return ret;
852 } 882 }
  883 +
853 // ingest, many per vhost. 884 // ingest, many per vhost.
854 if ((ret = reload_ingest(new_vhost, old_vhost)) != ERROR_SUCCESS) { 885 if ((ret = reload_ingest(new_vhost, old_vhost)) != ERROR_SUCCESS) {
855 return ret; 886 return ret;
@@ -1799,7 +1830,7 @@ int SrsConfig::check_config() @@ -1799,7 +1830,7 @@ int SrsConfig::check_config()
1799 && n != "publish_1stpkt_timeout" && n != "publish_normal_timeout" 1830 && n != "publish_1stpkt_timeout" && n != "publish_normal_timeout"
1800 && n != "security" && n != "http_remux" 1831 && n != "security" && n != "http_remux"
1801 && n != "http" && n != "http_static" 1832 && n != "http" && n != "http_static"
1802 - && n != "hds" 1833 + && n != "hds" && n != "exec"
1803 ) { 1834 ) {
1804 ret = ERROR_SYSTEM_CONFIG_INVALID; 1835 ret = ERROR_SYSTEM_CONFIG_INVALID;
1805 srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret); 1836 srs_error("unsupported vhost directive %s, ret=%d", n.c_str(), ret);
@@ -1817,11 +1848,19 @@ int SrsConfig::check_config() @@ -1817,11 +1848,19 @@ int SrsConfig::check_config()
1817 return ret; 1848 return ret;
1818 } 1849 }
1819 } 1850 }
  1851 + } else if (n == "exec") {
  1852 + for (int j = 0; j < (int)conf->directives.size(); j++) {
  1853 + string m = conf->at(j)->name.c_str();
  1854 + if (m != "enabled" && m != "publish") {
  1855 + ret = ERROR_SYSTEM_CONFIG_INVALID;
  1856 + srs_error("unsupported vhost exec directive %s, ret=%d", m.c_str(), ret);
  1857 + return ret;
  1858 + }
  1859 + }
1820 } else if (n == "mr") { 1860 } else if (n == "mr") {
1821 for (int j = 0; j < (int)conf->directives.size(); j++) { 1861 for (int j = 0; j < (int)conf->directives.size(); j++) {
1822 string m = conf->at(j)->name.c_str(); 1862 string m = conf->at(j)->name.c_str();
1823 - if (m != "enabled" && m != "latency"  
1824 - ) { 1863 + if (m != "enabled" && m != "latency") {
1825 ret = ERROR_SYSTEM_CONFIG_INVALID; 1864 ret = ERROR_SYSTEM_CONFIG_INVALID;
1826 srs_error("unsupported vhost mr directive %s, ret=%d", m.c_str(), ret); 1865 srs_error("unsupported vhost mr directive %s, ret=%d", m.c_str(), ret);
1827 return ret; 1866 return ret;
@@ -1830,9 +1869,7 @@ int SrsConfig::check_config() @@ -1830,9 +1869,7 @@ int SrsConfig::check_config()
1830 } else if (n == "ingest") { 1869 } else if (n == "ingest") {
1831 for (int j = 0; j < (int)conf->directives.size(); j++) { 1870 for (int j = 0; j < (int)conf->directives.size(); j++) {
1832 string m = conf->at(j)->name.c_str(); 1871 string m = conf->at(j)->name.c_str();
1833 - if (m != "enabled" && m != "input" && m != "ffmpeg"  
1834 - && m != "engine"  
1835 - ) { 1872 + if (m != "enabled" && m != "input" && m != "ffmpeg" && m != "engine") {
1836 ret = ERROR_SYSTEM_CONFIG_INVALID; 1873 ret = ERROR_SYSTEM_CONFIG_INVALID;
1837 srs_error("unsupported vhost ingest directive %s, ret=%d", m.c_str(), ret); 1874 srs_error("unsupported vhost ingest directive %s, ret=%d", m.c_str(), ret);
1838 return ret; 1875 return ret;
@@ -3347,6 +3384,52 @@ string SrsConfig::get_engine_output(SrsConfDirective* engine) @@ -3347,6 +3384,52 @@ string SrsConfig::get_engine_output(SrsConfDirective* engine)
3347 return conf->arg0(); 3384 return conf->arg0();
3348 } 3385 }
3349 3386
  3387 +SrsConfDirective* SrsConfig::get_exec(string vhost)
  3388 +{
  3389 + SrsConfDirective* conf = get_vhost(vhost);
  3390 + if (!conf) {
  3391 + return NULL;
  3392 + }
  3393 +
  3394 + return conf->get("exec");
  3395 +}
  3396 +
  3397 +bool SrsConfig::get_exec_enabled(string vhost)
  3398 +{
  3399 + static bool DEFAULT = false;
  3400 +
  3401 + SrsConfDirective* conf = get_exec(vhost);
  3402 + if (!conf) {
  3403 + return DEFAULT;
  3404 + }
  3405 +
  3406 + conf = conf->get("enabled");
  3407 + if (!conf || conf->arg0().empty()) {
  3408 + return DEFAULT;
  3409 + }
  3410 +
  3411 + return SRS_CONF_PERFER_FALSE(conf->arg0());
  3412 +}
  3413 +
  3414 +vector<SrsConfDirective*> SrsConfig::get_exec_publishs(string vhost)
  3415 +{
  3416 + vector<SrsConfDirective*> eps;
  3417 +
  3418 + SrsConfDirective* conf = get_exec(vhost);
  3419 + if (!conf) {
  3420 + return eps;
  3421 + }
  3422 +
  3423 + for (int i = 0; i < (int)conf->directives.size(); i++) {
  3424 + SrsConfDirective* ep = conf->at(i);
  3425 + if (ep->name == "publish") {
  3426 + eps.push_back(ep);
  3427 + }
  3428 + }
  3429 +
  3430 + return eps;
  3431 +}
  3432 +
3350 vector<SrsConfDirective*> SrsConfig::get_ingesters(string vhost) 3433 vector<SrsConfDirective*> SrsConfig::get_ingesters(string vhost)
3351 { 3434 {
3352 vector<SrsConfDirective*> ingeters; 3435 vector<SrsConfDirective*> ingeters;
@@ -793,6 +793,21 @@ public: @@ -793,6 +793,21 @@ public:
793 * @remark, we will use some variable, for instance, [vhost] to substitude with vhost. 793 * @remark, we will use some variable, for instance, [vhost] to substitude with vhost.
794 */ 794 */
795 virtual std::string get_engine_output(SrsConfDirective* engine); 795 virtual std::string get_engine_output(SrsConfDirective* engine);
  796 +// vhost exec secion
  797 +private:
  798 + /**
  799 + * get the exec directive of vhost.
  800 + */
  801 + virtual SrsConfDirective* get_exec(std::string vhost);
  802 +public:
  803 + /**
  804 + * whether the exec is enabled of vhost.
  805 + */
  806 + virtual bool get_exec_enabled(std::string vhost);
  807 + /**
  808 + * get all exec publish directives of vhost.
  809 + */
  810 + virtual std::vector<SrsConfDirective*> get_exec_publishs(std::string vhost);
796 // vhost ingest section 811 // vhost ingest section
797 public: 812 public:
798 /** 813 /**
@@ -208,8 +208,7 @@ int SrsEncoder::parse_ffmpeg(SrsRequest* req, SrsConfDirective* conf) @@ -208,8 +208,7 @@ int SrsEncoder::parse_ffmpeg(SrsRequest* req, SrsConfDirective* conf)
208 208
209 // enabled 209 // enabled
210 if (!_srs_config->get_transcode_enabled(conf)) { 210 if (!_srs_config->get_transcode_enabled(conf)) {
211 - srs_trace("ignore the disabled transcode: %s",  
212 - conf->arg0().c_str()); 211 + srs_trace("ignore the disabled transcode: %s", conf->arg0().c_str());
213 return ret; 212 return ret;
214 } 213 }
215 214
  1 +/*
  2 + The MIT License (MIT)
  3 +
  4 + Copyright (c) 2013-2015 SRS(simple-rtmp-server)
  5 +
  6 + Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 + this software and associated documentation files (the "Software"), to deal in
  8 + the Software without restriction, including without limitation the rights to
  9 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 + the Software, and to permit persons to whom the Software is furnished to do so,
  11 + subject to the following conditions:
  12 +
  13 + The above copyright notice and this permission notice shall be included in all
  14 + copies or substantial portions of the Software.
  15 +
  16 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 + */
  23 +
  24 +#include <srs_app_ng_exec.hpp>
  25 +
  26 +using namespace std;
  27 +
  28 +#include <srs_kernel_error.hpp>
  29 +#include <srs_kernel_log.hpp>
  30 +#include <srs_app_config.hpp>
  31 +#include <srs_rtmp_stack.hpp>
  32 +#include <srs_app_pithy_print.hpp>
  33 +#include <srs_app_process.hpp>
  34 +#include <srs_kernel_utility.hpp>
  35 +#include <srs_kernel_consts.hpp>
  36 +#include <srs_rtmp_utility.hpp>
  37 +
  38 +// when error, ng-exec sleep for a while and retry.
  39 +#define SRS_RTMP_EXEC_SLEEP_US (int64_t)(3*1000*1000LL)
  40 +
  41 +SrsNgExec::SrsNgExec()
  42 +{
  43 + pthread = new SrsReusableThread("encoder", this, SRS_RTMP_EXEC_SLEEP_US);
  44 + pprint = SrsPithyPrint::create_exec();
  45 +}
  46 +
  47 +SrsNgExec::~SrsNgExec()
  48 +{
  49 + on_unpublish();
  50 +
  51 + srs_freep(pthread);
  52 + srs_freep(pprint);
  53 +}
  54 +
  55 +int SrsNgExec::on_publish(SrsRequest* req)
  56 +{
  57 + int ret = ERROR_SUCCESS;
  58 +
  59 + // when publish, parse the exec_publish.
  60 + if ((ret = parse_exec_publish(req)) != ERROR_SUCCESS) {
  61 + return ret;
  62 + }
  63 +
  64 + // start thread to run all processes.
  65 + if ((ret = pthread->start()) != ERROR_SUCCESS) {
  66 + srs_error("st_thread_create failed. ret=%d", ret);
  67 + return ret;
  68 + }
  69 + srs_trace("exec thread cid=%d, current_cid=%d", pthread->cid(), _srs_context->get_id());
  70 +
  71 + return ret;
  72 +}
  73 +
  74 +void SrsNgExec::on_unpublish()
  75 +{
  76 + pthread->stop();
  77 + clear_exec_publish();
  78 +}
  79 +
  80 +int SrsNgExec::cycle()
  81 +{
  82 + int ret = ERROR_SUCCESS;
  83 +
  84 + // ignore when no exec.
  85 + if (exec_publishs.empty()) {
  86 + return ret;
  87 + }
  88 +
  89 + std::vector<SrsProcess*>::iterator it;
  90 + for (it = exec_publishs.begin(); it != exec_publishs.end(); ++it) {
  91 + SrsProcess* process = *it;
  92 +
  93 + // start all processes.
  94 + if ((ret = process->start()) != ERROR_SUCCESS) {
  95 + srs_error("exec publish start failed. ret=%d", ret);
  96 + return ret;
  97 + }
  98 +
  99 + // check process status.
  100 + if ((ret = process->cycle()) != ERROR_SUCCESS) {
  101 + srs_error("exec publish cycle failed. ret=%d", ret);
  102 + return ret;
  103 + }
  104 + }
  105 +
  106 + // pithy print
  107 + show_exec_log_message();
  108 +
  109 + return ret;
  110 +}
  111 +
  112 +void SrsNgExec::on_thread_stop()
  113 +{
  114 + std::vector<SrsProcess*>::iterator it;
  115 + for (it = exec_publishs.begin(); it != exec_publishs.end(); ++it) {
  116 + SrsProcess* ep = *it;
  117 + ep->stop();
  118 + }
  119 +}
  120 +
  121 +int SrsNgExec::parse_exec_publish(SrsRequest* req)
  122 +{
  123 + int ret = ERROR_SUCCESS;
  124 +
  125 + if (!_srs_config->get_exec_enabled(req->vhost)) {
  126 + srs_trace("ignore disabled exec for vhost=%s", req->vhost.c_str());
  127 + return ret;
  128 + }
  129 +
  130 + // stream name: vhost/app/stream for print
  131 + input_stream_name = req->vhost;
  132 + input_stream_name += "/";
  133 + input_stream_name += req->app;
  134 + input_stream_name += "/";
  135 + input_stream_name += req->stream;
  136 +
  137 + std::vector<SrsConfDirective*> eps = _srs_config->get_exec_publishs(req->vhost);
  138 + for (int i = 0; i < (int)eps.size(); i++) {
  139 + SrsConfDirective* ep = eps.at(i);
  140 + SrsProcess* process = new SrsProcess();
  141 +
  142 + std::string binary = ep->arg0();
  143 + std::vector<std::string> argv;
  144 + for (int i = 0; i < (int)ep->args.size(); i++) {
  145 + std::string epa = ep->args.at(i);
  146 +
  147 + if (srs_string_contains(epa, ">")) {
  148 + vector<string> epas = srs_string_split(epa, ">");
  149 + for (int j = 0; j < (int)epas.size(); j++) {
  150 + argv.push_back(parse(req, epas.at(j)));
  151 + if (j == 0) {
  152 + argv.push_back(">");
  153 + }
  154 + }
  155 + continue;
  156 + }
  157 +
  158 + argv.push_back(parse(req, epa));
  159 + }
  160 +
  161 + if ((ret = process->initialize(binary, argv)) != ERROR_SUCCESS) {
  162 + srs_freep(process);
  163 + srs_error("initialize process failed, binary=%s, vhost=%s, ret=%d", binary.c_str(), req->vhost.c_str(), ret);
  164 + return ret;
  165 + }
  166 +
  167 + exec_publishs.push_back(process);
  168 + }
  169 +
  170 + return ret;
  171 +}
  172 +
  173 +void SrsNgExec::clear_exec_publish()
  174 +{
  175 + std::vector<SrsProcess*>::iterator it;
  176 + for (it = exec_publishs.begin(); it != exec_publishs.end(); ++it) {
  177 + SrsProcess* ep = *it;
  178 + srs_freep(ep);
  179 + }
  180 +}
  181 +
  182 +void SrsNgExec::show_exec_log_message()
  183 +{
  184 + pprint->elapse();
  185 +
  186 + // reportable
  187 + if (pprint->can_print()) {
  188 + // TODO: FIXME: show more info.
  189 + srs_trace("-> "SRS_CONSTS_LOG_EXEC" time=%"PRId64", publish=%d, input=%s",
  190 + pprint->age(), (int)exec_publishs.size(), input_stream_name.c_str());
  191 + }
  192 +}
  193 +
  194 +string SrsNgExec::parse(SrsRequest* req, string tmpl)
  195 +{
  196 + string output = tmpl;
  197 +
  198 + output = srs_string_replace(output, "[vhost]", req->vhost);
  199 + output = srs_string_replace(output, "[port]", req->port);
  200 + output = srs_string_replace(output, "[app]", req->app);
  201 + output = srs_string_replace(output, "[stream]", req->stream);
  202 +
  203 + output = srs_string_replace(output, "[tcUrl]", req->tcUrl);
  204 + output = srs_string_replace(output, "[swfUrl]", req->swfUrl);
  205 + output = srs_string_replace(output, "[pageUrl]", req->pageUrl);
  206 +
  207 + if (output.find("[url]") != string::npos) {
  208 + string url = srs_generate_rtmp_url(req->host, ::atoi(req->port.c_str()), req->vhost, req->app, req->stream);
  209 + output = srs_string_replace(output, "[url]", url);
  210 + }
  211 +
  212 + return output;
  213 +}
  214 +
  1 +/*
  2 + The MIT License (MIT)
  3 +
  4 + Copyright (c) 2013-2015 SRS(simple-rtmp-server)
  5 +
  6 + Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 + this software and associated documentation files (the "Software"), to deal in
  8 + the Software without restriction, including without limitation the rights to
  9 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 + the Software, and to permit persons to whom the Software is furnished to do so,
  11 + subject to the following conditions:
  12 +
  13 + The above copyright notice and this permission notice shall be included in all
  14 + copies or substantial portions of the Software.
  15 +
  16 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 + */
  23 +
  24 +#ifndef SRS_APP_NG_EXEC_HPP
  25 +#define SRS_APP_NG_EXEC_HPP
  26 +
  27 +/*
  28 +#include <srs_app_ng_exec.hpp>
  29 +*/
  30 +#include <srs_core.hpp>
  31 +
  32 +#include <vector>
  33 +#include <string>
  34 +
  35 +#include <srs_app_thread.hpp>
  36 +
  37 +class SrsRequest;
  38 +class SrsPithyPrint;
  39 +class SrsProcess;
  40 +
  41 +/**
  42 + * the ng-exec is the exec feature introduced by nginx-rtmp,
  43 + * @see https://github.com/arut/nginx-rtmp-module/wiki/Directives#exec_push
  44 + * @see https://github.com/simple-rtmp-server/srs/issues/367
  45 + */
  46 +class SrsNgExec : public ISrsReusableThreadHandler
  47 +{
  48 +private:
  49 + SrsReusableThread* pthread;
  50 + SrsPithyPrint* pprint;
  51 + std::string input_stream_name;
  52 + std::vector<SrsProcess*> exec_publishs;
  53 +public:
  54 + SrsNgExec();
  55 + virtual ~SrsNgExec();
  56 +public:
  57 + virtual int on_publish(SrsRequest* req);
  58 + virtual void on_unpublish();
  59 +// interface ISrsReusableThreadHandler.
  60 +public:
  61 + virtual int cycle();
  62 + virtual void on_thread_stop();
  63 +private:
  64 + virtual int parse_exec_publish(SrsRequest* req);
  65 + virtual void clear_exec_publish();
  66 + virtual void show_exec_log_message();
  67 + virtual std::string parse(SrsRequest* req, std::string tmpl);
  68 +};
  69 +
  70 +#endif
  71 +
@@ -108,6 +108,8 @@ SrsPithyPrint::SrsPithyPrint(int _stage_id) @@ -108,6 +108,8 @@ SrsPithyPrint::SrsPithyPrint(int _stage_id)
108 #define SRS_CONSTS_STAGE_HTTP_STREAM 9 108 #define SRS_CONSTS_STAGE_HTTP_STREAM 9
109 // the pithy stage for all http stream cache. 109 // the pithy stage for all http stream cache.
110 #define SRS_CONSTS_STAGE_HTTP_STREAM_CACHE 10 110 #define SRS_CONSTS_STAGE_HTTP_STREAM_CACHE 10
  111 +// for the ng-exec stage.
  112 +#define SRS_CONSTS_STAGE_EXEC 11
111 113
112 SrsPithyPrint* SrsPithyPrint::create_rtmp_play() 114 SrsPithyPrint* SrsPithyPrint::create_rtmp_play()
113 { 115 {
@@ -134,6 +136,11 @@ SrsPithyPrint* SrsPithyPrint::create_encoder() @@ -134,6 +136,11 @@ SrsPithyPrint* SrsPithyPrint::create_encoder()
134 return new SrsPithyPrint(SRS_CONSTS_STAGE_ENCODER); 136 return new SrsPithyPrint(SRS_CONSTS_STAGE_ENCODER);
135 } 137 }
136 138
  139 +SrsPithyPrint* SrsPithyPrint::create_exec()
  140 +{
  141 + return new SrsPithyPrint(SRS_CONSTS_STAGE_EXEC);
  142 +}
  143 +
137 SrsPithyPrint* SrsPithyPrint::create_ingester() 144 SrsPithyPrint* SrsPithyPrint::create_ingester()
138 { 145 {
139 return new SrsPithyPrint(SRS_CONSTS_STAGE_INGESTER); 146 return new SrsPithyPrint(SRS_CONSTS_STAGE_INGESTER);
@@ -90,6 +90,7 @@ public: @@ -90,6 +90,7 @@ public:
90 static SrsPithyPrint* create_hls(); 90 static SrsPithyPrint* create_hls();
91 static SrsPithyPrint* create_forwarder(); 91 static SrsPithyPrint* create_forwarder();
92 static SrsPithyPrint* create_encoder(); 92 static SrsPithyPrint* create_encoder();
  93 + static SrsPithyPrint* create_exec();
93 static SrsPithyPrint* create_ingester(); 94 static SrsPithyPrint* create_ingester();
94 static SrsPithyPrint* create_edge(); 95 static SrsPithyPrint* create_edge();
95 static SrsPithyPrint* create_caster(); 96 static SrsPithyPrint* create_caster();
@@ -62,7 +62,7 @@ int SrsProcess::initialize(string binary, vector<string> argv) @@ -62,7 +62,7 @@ int SrsProcess::initialize(string binary, vector<string> argv)
62 { 62 {
63 int ret = ERROR_SUCCESS; 63 int ret = ERROR_SUCCESS;
64 64
65 - cli = bin = binary; 65 + bin = binary;
66 66
67 for (int i = 0; i < (int)argv.size(); i++) { 67 for (int i = 0; i < (int)argv.size(); i++) {
68 std::string ffp = argv[i]; 68 std::string ffp = argv[i];
@@ -38,7 +38,7 @@ @@ -38,7 +38,7 @@
38 * // the binary is the process to fork. 38 * // the binary is the process to fork.
39 * binary = "./objs/ffmpeg/bin/ffmpeg"; 39 * binary = "./objs/ffmpeg/bin/ffmpeg";
40 * // where argv is a array contains each params. 40 * // where argv is a array contains each params.
41 - * argv = ["-i", "in.flv", "1", ">", "/dev/null", "2", ">", "/dev/null"]; 41 + * argv = ["./objs/ffmpeg/bin/ffmpeg", "-i", "in.flv", "1", ">", "/dev/null", "2", ">", "/dev/null"];
42 * 42 *
43 * process = new SrsProcess(); 43 * process = new SrsProcess();
44 * if ((ret = process->initialize(binary, argv)) != ERROR_SUCCESS) { return ret; } 44 * if ((ret = process->initialize(binary, argv)) != ERROR_SUCCESS) { return ret; }
@@ -71,6 +71,8 @@ public: @@ -71,6 +71,8 @@ public:
71 virtual bool started(); 71 virtual bool started();
72 /** 72 /**
73 * initialize the process with binary and argv. 73 * initialize the process with binary and argv.
  74 + * @param binary the binary path to exec.
  75 + * @param argv the argv for binary path, the argv[0] generally is the binary.
74 */ 76 */
75 virtual int initialize(std::string binary, std::vector<std::string> argv); 77 virtual int initialize(std::string binary, std::vector<std::string> argv);
76 public: 78 public:
@@ -205,6 +205,11 @@ int ISrsReloadHandler::on_reload_vhost_transcode(string /*vhost*/) @@ -205,6 +205,11 @@ int ISrsReloadHandler::on_reload_vhost_transcode(string /*vhost*/)
205 return ERROR_SUCCESS; 205 return ERROR_SUCCESS;
206 } 206 }
207 207
  208 +int ISrsReloadHandler::on_reload_vhost_exec(string /*vhost*/)
  209 +{
  210 + return ERROR_SUCCESS;
  211 +}
  212 +
208 int ISrsReloadHandler::on_reload_ingest_removed(string /*vhost*/, string /*ingest_id*/) 213 int ISrsReloadHandler::on_reload_ingest_removed(string /*vhost*/, string /*ingest_id*/)
209 { 214 {
210 return ERROR_SUCCESS; 215 return ERROR_SUCCESS;
@@ -80,6 +80,7 @@ public: @@ -80,6 +80,7 @@ public:
80 virtual int on_reload_vhost_pnt(std::string vhost); 80 virtual int on_reload_vhost_pnt(std::string vhost);
81 virtual int on_reload_vhost_chunk_size(std::string vhost); 81 virtual int on_reload_vhost_chunk_size(std::string vhost);
82 virtual int on_reload_vhost_transcode(std::string vhost); 82 virtual int on_reload_vhost_transcode(std::string vhost);
  83 + virtual int on_reload_vhost_exec(std::string vhost);
83 virtual int on_reload_ingest_removed(std::string vhost, std::string ingest_id); 84 virtual int on_reload_ingest_removed(std::string vhost, std::string ingest_id);
84 virtual int on_reload_ingest_added(std::string vhost, std::string ingest_id); 85 virtual int on_reload_ingest_added(std::string vhost, std::string ingest_id);
85 virtual int on_reload_ingest_updated(std::string vhost, std::string ingest_id); 86 virtual int on_reload_ingest_updated(std::string vhost, std::string ingest_id);
@@ -46,6 +46,7 @@ using namespace std; @@ -46,6 +46,7 @@ using namespace std;
46 #include <srs_app_statistic.hpp> 46 #include <srs_app_statistic.hpp>
47 #include <srs_core_autofree.hpp> 47 #include <srs_core_autofree.hpp>
48 #include <srs_rtmp_utility.hpp> 48 #include <srs_rtmp_utility.hpp>
  49 +#include <srs_app_ng_exec.hpp>
49 50
50 #define CONST_MAX_JITTER_MS 250 51 #define CONST_MAX_JITTER_MS 250
51 #define CONST_MAX_JITTER_MS_NEG -250 52 #define CONST_MAX_JITTER_MS_NEG -250
@@ -921,6 +922,7 @@ SrsSource::SrsSource() @@ -921,6 +922,7 @@ SrsSource::SrsSource()
921 publish_edge = new SrsPublishEdge(); 922 publish_edge = new SrsPublishEdge();
922 gop_cache = new SrsGopCache(); 923 gop_cache = new SrsGopCache();
923 aggregate_stream = new SrsStream(); 924 aggregate_stream = new SrsStream();
  925 + ng_exec = new SrsNgExec();
924 926
925 is_monotonically_increase = false; 927 is_monotonically_increase = false;
926 last_packet_time = 0; 928 last_packet_time = 0;
@@ -955,6 +957,7 @@ SrsSource::~SrsSource() @@ -955,6 +957,7 @@ SrsSource::~SrsSource()
955 srs_freep(publish_edge); 957 srs_freep(publish_edge);
956 srs_freep(gop_cache); 958 srs_freep(gop_cache);
957 srs_freep(aggregate_stream); 959 srs_freep(aggregate_stream);
  960 + srs_freep(ng_exec);
958 961
959 #ifdef SRS_AUTO_HLS 962 #ifdef SRS_AUTO_HLS
960 srs_freep(hls); 963 srs_freep(hls);
@@ -1259,6 +1262,24 @@ int SrsSource::on_reload_vhost_transcode(string vhost) @@ -1259,6 +1262,24 @@ int SrsSource::on_reload_vhost_transcode(string vhost)
1259 return ret; 1262 return ret;
1260 } 1263 }
1261 1264
  1265 +int SrsSource::on_reload_vhost_exec(string vhost)
  1266 +{
  1267 + int ret = ERROR_SUCCESS;
  1268 +
  1269 + if (_req->vhost != vhost) {
  1270 + return ret;
  1271 + }
  1272 +
  1273 + ng_exec->on_unpublish();
  1274 + if ((ret = ng_exec->on_publish(_req)) != ERROR_SUCCESS) {
  1275 + srs_error("start exec failed. ret=%d", ret);
  1276 + return ret;
  1277 + }
  1278 + srs_trace("vhost %s exec reload success", vhost.c_str());
  1279 +
  1280 + return ret;
  1281 +}
  1282 +
1262 int SrsSource::on_forwarder_start(SrsForwarder* forwarder) 1283 int SrsSource::on_forwarder_start(SrsForwarder* forwarder)
1263 { 1284 {
1264 int ret = ERROR_SUCCESS; 1285 int ret = ERROR_SUCCESS;
@@ -2058,6 +2079,12 @@ int SrsSource::on_publish() @@ -2058,6 +2079,12 @@ int SrsSource::on_publish()
2058 } 2079 }
2059 #endif 2080 #endif
2060 2081
  2082 + // TODO: FIXME: use initialize to set req.
  2083 + if ((ret = ng_exec->on_publish(_req)) != ERROR_SUCCESS) {
  2084 + srs_error("start exec failed. ret=%d", ret);
  2085 + return ret;
  2086 + }
  2087 +
2061 // notify the handler. 2088 // notify the handler.
2062 srs_assert(handler); 2089 srs_assert(handler);
2063 if ((ret = handler->on_publish(this, _req)) != ERROR_SUCCESS) { 2090 if ((ret = handler->on_publish(this, _req)) != ERROR_SUCCESS) {
@@ -2090,6 +2117,8 @@ void SrsSource::on_unpublish() @@ -2090,6 +2117,8 @@ void SrsSource::on_unpublish()
2090 hds->on_unpublish(); 2117 hds->on_unpublish();
2091 #endif 2118 #endif
2092 2119
  2120 + ng_exec->on_unpublish();
  2121 +
2093 // only clear the gop cache, 2122 // only clear the gop cache,
2094 // donot clear the sequence header, for it maybe not changed, 2123 // donot clear the sequence header, for it maybe not changed,
2095 // when drop dup sequence header, drop the metadata also. 2124 // when drop dup sequence header, drop the metadata also.
@@ -51,6 +51,7 @@ class SrsStSocket; @@ -51,6 +51,7 @@ class SrsStSocket;
51 class SrsRtmpServer; 51 class SrsRtmpServer;
52 class SrsEdgeProxyContext; 52 class SrsEdgeProxyContext;
53 class SrsMessageArray; 53 class SrsMessageArray;
  54 +class SrsNgExec;
54 #ifdef SRS_AUTO_HLS 55 #ifdef SRS_AUTO_HLS
55 class SrsHls; 56 class SrsHls;
56 #endif 57 #endif
@@ -469,8 +470,11 @@ private: @@ -469,8 +470,11 @@ private:
469 SrsEncoder* encoder; 470 SrsEncoder* encoder;
470 #endif 471 #endif
471 #ifdef SRS_AUTO_HDS 472 #ifdef SRS_AUTO_HDS
  473 + // adobe hds(http dynamic streaming).
472 SrsHds *hds; 474 SrsHds *hds;
473 #endif 475 #endif
  476 + // nginx-rtmp exec feature.
  477 + SrsNgExec* ng_exec;
474 // edge control service 478 // edge control service
475 SrsPlayEdge* play_edge; 479 SrsPlayEdge* play_edge;
476 SrsPublishEdge* publish_edge; 480 SrsPublishEdge* publish_edge;
@@ -524,6 +528,7 @@ public: @@ -524,6 +528,7 @@ public:
524 virtual int on_reload_vhost_hds(std::string vhost); 528 virtual int on_reload_vhost_hds(std::string vhost);
525 virtual int on_reload_vhost_dvr(std::string vhost); 529 virtual int on_reload_vhost_dvr(std::string vhost);
526 virtual int on_reload_vhost_transcode(std::string vhost); 530 virtual int on_reload_vhost_transcode(std::string vhost);
  531 + virtual int on_reload_vhost_exec(std::string vhost);
527 // for the tools callback 532 // for the tools callback
528 public: 533 public:
529 // for the SrsForwarder to callback to request the sequence headers. 534 // for the SrsForwarder to callback to request the sequence headers.
@@ -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 3 32 #define VERSION_MAJOR 3
33 #define VERSION_MINOR 0 33 #define VERSION_MINOR 0
34 -#define VERSION_REVISION 0 34 +#define VERSION_REVISION 1
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"
@@ -168,6 +168,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -168,6 +168,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
168 #define SRS_CONSTS_LOG_HTTP_STREAM_CACHE "HTC" 168 #define SRS_CONSTS_LOG_HTTP_STREAM_CACHE "HTC"
169 // stream caster log id. 169 // stream caster log id.
170 #define SRS_CONSTS_LOG_STREAM_CASTER "SCS" 170 #define SRS_CONSTS_LOG_STREAM_CASTER "SCS"
  171 +// the nginx exec log id.
  172 +#define SRS_CONSTS_LOG_EXEC "EXE"
171 173
172 /////////////////////////////////////////////////////////// 174 ///////////////////////////////////////////////////////////
173 /////////////////////////////////////////////////////////// 175 ///////////////////////////////////////////////////////////
@@ -35,6 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -35,6 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include <sys/stat.h> 35 #include <sys/stat.h>
36 #include <fcntl.h> 36 #include <fcntl.h>
37 37
  38 +#include <vector>
38 using namespace std; 39 using namespace std;
39 40
40 #include <srs_kernel_log.hpp> 41 #include <srs_kernel_log.hpp>
@@ -284,6 +285,25 @@ bool srs_string_contains(string str, string flag) @@ -284,6 +285,25 @@ bool srs_string_contains(string str, string flag)
284 return str.find(flag) != string::npos; 285 return str.find(flag) != string::npos;
285 } 286 }
286 287
  288 +vector<string> srs_string_split(string str, string flag)
  289 +{
  290 + vector<string> arr;
  291 +
  292 + size_t pos;
  293 + string s = str;
  294 +
  295 + while ((pos = s.find(flag)) != string::npos) {
  296 + arr.push_back(s.substr(0, pos));
  297 + s = s.substr(pos + 1);
  298 + }
  299 +
  300 + if (!s.empty()) {
  301 + arr.push_back(s);
  302 + }
  303 +
  304 + return arr;
  305 +}
  306 +
287 int srs_do_create_dir_recursively(string dir) 307 int srs_do_create_dir_recursively(string dir)
288 { 308 {
289 int ret = ERROR_SUCCESS; 309 int ret = ERROR_SUCCESS;
@@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,6 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #include <srs_core.hpp> 31 #include <srs_core.hpp>
32 32
33 #include <string> 33 #include <string>
  34 +#include <vector>
34 35
35 class SrsStream; 36 class SrsStream;
36 class SrsBitStream; 37 class SrsBitStream;
@@ -69,6 +70,8 @@ extern bool srs_string_ends_with(std::string str, std::string flag); @@ -69,6 +70,8 @@ extern bool srs_string_ends_with(std::string str, std::string flag);
69 extern bool srs_string_starts_with(std::string str, std::string flag); 70 extern bool srs_string_starts_with(std::string str, std::string flag);
70 // whether string contains with 71 // whether string contains with
71 extern bool srs_string_contains(std::string str, std::string flag); 72 extern bool srs_string_contains(std::string str, std::string flag);
  73 +// split the string by flag to array.
  74 +extern std::vector<std::string> srs_string_split(std::string str, std::string flag);
72 75
73 // create dir recursively 76 // create dir recursively
74 extern int srs_create_dir_recursively(std::string dir); 77 extern int srs_create_dir_recursively(std::string dir);
@@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #endif 29 #endif
30 30
31 #include <stdlib.h> 31 #include <stdlib.h>
  32 +#include <sstream>
32 using namespace std; 33 using namespace std;
33 34
34 #include <srs_kernel_log.hpp> 35 #include <srs_kernel_log.hpp>
@@ -239,6 +240,22 @@ std::string srs_generate_stream_url(std::string vhost, std::string app, std::str @@ -239,6 +240,22 @@ std::string srs_generate_stream_url(std::string vhost, std::string app, std::str
239 return url; 240 return url;
240 } 241 }
241 242
  243 +string srs_generate_rtmp_url(string server, int port, string vhost, string app, string stream)
  244 +{
  245 + std::stringstream ss;
  246 +
  247 + ss << "rtmp://" << server << ":" << std::dec << port << "/" << app;
  248 +
  249 + // when default or server is vhost, donot specifies the vhost in params.
  250 + if (SRS_CONSTS_RTMP_DEFAULT_VHOST != vhost && server != vhost) {
  251 + ss << "...vhost..." << vhost;
  252 + }
  253 +
  254 + ss << "/" << stream;
  255 +
  256 + return ss.str();
  257 +}
  258 +
242 int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite) 259 int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite)
243 { 260 {
244 int ret = ERROR_SUCCESS; 261 int ret = ERROR_SUCCESS;
@@ -106,6 +106,9 @@ extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int s @@ -106,6 +106,9 @@ extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int s
106 // get the stream identify, vhost/app/stream. 106 // get the stream identify, vhost/app/stream.
107 extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream); 107 extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream);
108 108
  109 +// genereate the rtmp url, for instance, rtmp://server:port/app...vhost...vhost/stream
  110 +extern std::string srs_generate_rtmp_url(std::string server, int port, std::string vhost, std::string app, std::string stream);
  111 +
109 // write large numbers of iovs. 112 // write large numbers of iovs.
110 extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL); 113 extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL);
111 114