wenjiegit

merge upstream

@@ -9,21 +9,41 @@ see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/w @@ -9,21 +9,41 @@ see also: [https://github.com/winlinvip/simple-rtmp-server](https://github.com/w
9 see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server) 9 see also: [http://winlinvip.github.io/simple-rtmp-server](http://winlinvip.github.io/simple-rtmp-server)
10 10
11 ### Contributors 11 ### Contributors
12 -winlin(winterserver): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) 12 +winlin([winterserver](#)): [http://blog.csdn.net/win_lin](http://blog.csdn.net/win_lin) <br/>
  13 +wenjie([wenjiegit](https://github.com/wenjiegit/simple-rtmp-server)): [http://blog.chinaunix.net/uid/25006789.html](http://blog.chinaunix.net/uid/25006789.html) <br/>
  14 +who is the contributors: <br/>
  15 +1. contribute important features to srs. <br/>
  16 +2. the name of all contributors will send in the response of NetConnection.connect and metadata.
13 17
14 -### Usage  
15 -<strong>step 1:</strong> build srs <br/> 18 +### Usage(simple)
  19 +<strong>step -1:</strong> get srs<br/>
  20 +<pre>
  21 +git clone https://github.com/winlinvip/simple-rtmp-server &&
  22 +cd simple-rtmp-server/trunk
  23 +</pre>
  24 +<strong>step 0:</strong> build srs system.<br/>
  25 +<pre>
  26 +bash scripts/build.sh
  27 +</pre>
  28 +<strong>step 1:</strong> start srs all demo features.<br/>
16 <pre> 29 <pre>
17 -tar xf simple-rtmp-server-*.*.tar.gz  
18 -cd simple-rtmp-server-*.*/trunk  
19 -./configure --with-ssl --with-hls --with-ffmpeg --with-http  
20 -make 30 +bash scripts/run.sh
21 </pre> 31 </pre>
22 -or get the latest code:<br/> 32 +<strong>step 2:</strong> srs live show: [http://your-server-ip](http://your-server-ip) <br/>
  33 +<strong>step 3:</strong> stop srs demo<br/>
23 <pre> 34 <pre>
24 -git clone https://github.com/winlinvip/simple-rtmp-server 35 +bash scripts/stop.sh
  36 +</pre>
  37 +
  38 +### Usage(detail)
  39 +<strong>step 0:</strong> get srs <br/>
  40 +<pre>
  41 +git clone https://github.com/winlinvip/simple-rtmp-server &&
25 cd simple-rtmp-server/trunk 42 cd simple-rtmp-server/trunk
26 -./configure --with-ssl --with-hls --with-ffmpeg --with-http 43 +</pre>
  44 +<strong>step 1:</strong> build srs <br/>
  45 +<pre>
  46 +./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
27 </pre> 47 </pre>
28 <strong>step 2:</strong> start srs <br/> 48 <strong>step 2:</strong> start srs <br/>
29 <pre> 49 <pre>
@@ -41,11 +61,11 @@ sudo ./objs/nginx/sbin/nginx @@ -41,11 +61,11 @@ sudo ./objs/nginx/sbin/nginx
41 <pre> 61 <pre>
42 python ./research/api-server/server.py 8085 62 python ./research/api-server/server.py 8085
43 </pre> 63 </pre>
44 -<strong>step 6:</strong> publish live stream <br/> 64 +<strong>step 6:</strong> publish demo live stream <br/>
45 <pre> 65 <pre>
46 -FMS URL: rtmp://127.0.0.1/live 66 +FMS URL: rtmp://127.0.0.1/live?vhost=demo.srs.com
47 Stream: livestream 67 Stream: livestream
48 -For example, use ffmpeg to publish: 68 +FFMPEG to publish the default demo stream:
49 for((;;)); do \ 69 for((;;)); do \
50 ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \ 70 ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
51 -vcodec copy -acodec copy \ 71 -vcodec copy -acodec copy \
@@ -53,7 +73,19 @@ For example, use ffmpeg to publish: @@ -53,7 +73,19 @@ For example, use ffmpeg to publish:
53 sleep 1; \ 73 sleep 1; \
54 done 74 done
55 </pre> 75 </pre>
56 -<strong>step 7:</strong> add server ip to client hosts as demo. <br/> 76 +<strong>step 7:</strong> publish players live stream <br/>
  77 +<pre>
  78 +FMS URL: rtmp://127.0.0.1/live?vhost=players
  79 +Stream: livestream
  80 +FFMPEG to publish the players demo stream:
  81 + for((;;)); do \
  82 + ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
  83 + -vcodec copy -acodec copy \
  84 + -f flv -y rtmp://127.0.0.1/live?vhost=players/livestream; \
  85 + sleep 1; \
  86 + done
  87 +</pre>
  88 +<strong>step 8:</strong> add server ip to client hosts as demo. <br/>
57 <pre> 89 <pre>
58 # edit the folowing file: 90 # edit the folowing file:
59 # linux: /etc/hosts 91 # linux: /etc/hosts
@@ -61,35 +93,35 @@ For example, use ffmpeg to publish: @@ -61,35 +93,35 @@ For example, use ffmpeg to publish:
61 # where server ip is 192.168.2.111 93 # where server ip is 192.168.2.111
62 192.168.2.111 demo.srs.com 94 192.168.2.111 demo.srs.com
63 </pre> 95 </pre>
64 -<strong>step 8:</strong> play live stream. <br/> 96 +<strong>step 9:</strong> play live stream. <br/>
65 <pre> 97 <pre>
66 -players: [http://demo.srs.com/players](http://demo.srs.com/players) 98 +players: http://demo.srs.com/players
67 rtmp url: rtmp://demo.srs.com/live/livestream 99 rtmp url: rtmp://demo.srs.com/live/livestream
68 m3u8 url: http://demo.srs.com/live/livestream.m3u8 100 m3u8 url: http://demo.srs.com/live/livestream.m3u8
69 for android: http://demo.srs.com/live/livestream.html 101 for android: http://demo.srs.com/live/livestream.html
70 </pre> 102 </pre>
71 -<strong>step 9(optinal):</strong> play live stream auto transcoded<br/> 103 +<strong>step 10(optinal):</strong> play live stream auto transcoded<br/>
72 <pre> 104 <pre>
73 rtmp url: rtmp://demo.srs.com/live/livestream_ld 105 rtmp url: rtmp://demo.srs.com/live/livestream_ld
74 m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8 106 m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8
75 -for android: [http://demo.srs.com/live/livestream_ld.html](http://demo.srs.com/live/livestream_ld.html) 107 +for android: http://demo.srs.com/live/livestream_ld.html
76 rtmp url: rtmp://demo.srs.com/live/livestream_sd 108 rtmp url: rtmp://demo.srs.com/live/livestream_sd
77 m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8 109 m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8
78 -for android: [http://demo.srs.com/live/livestream_sd.html](http://demo.srs.com/live/livestream_sd.html) 110 +for android: http://demo.srs.com/live/livestream_sd.html
79 </pre> 111 </pre>
80 -<strong>step 10(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/> 112 +<strong>step 11(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
81 <pre> 113 <pre>
82 rtmp url: rtmp://demo.srs.com:19350/live/livestream 114 rtmp url: rtmp://demo.srs.com:19350/live/livestream
83 m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8 115 m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8
84 -for android: [http://demo.srs.com/forward/live/livestream.html](http://demo.srs.com/forward/live/livestream.html) 116 +for android: http://demo.srs.com/forward/live/livestream.html
85 rtmp url: rtmp://demo.srs.com:19350/live/livestream_ld 117 rtmp url: rtmp://demo.srs.com:19350/live/livestream_ld
86 m3u8 url: http://demo.srs.com/forward/live/livestream_ld.m3u8 118 m3u8 url: http://demo.srs.com/forward/live/livestream_ld.m3u8
87 -for android: [http://demo.srs.com/forward/live/livestream_ld.html](http://demo.srs.com/forward/live/livestream_ld.html) 119 +for android: http://demo.srs.com/forward/live/livestream_ld.html
88 rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd 120 rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd
89 m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8 121 m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8
90 -for android: [http://demo.srs.com/forward/live/livestream_sd.html](http://demo.srs.com/forward/live/livestream_sd.html) 122 +for android: http://demo.srs.com/forward/live/livestream_sd.html
91 </pre> 123 </pre>
92 -<strong>step 11(optinal):</strong> modify the config and reload it (all features support reload)<br/> 124 +<strong>step 12(optinal):</strong> modify the config and reload it (all features support reload)<br/>
93 <pre> 125 <pre>
94 killall -1 srs 126 killall -1 srs
95 </pre> 127 </pre>
@@ -125,8 +157,36 @@ Stream Architecture: @@ -125,8 +157,36 @@ Stream Architecture:
125 | Flash, | +-> Fowarder ---------+-> RTMP Server | 157 | Flash, | +-> Fowarder ---------+-> RTMP Server |
126 | XSPLIT, | +-> Transcoder -------+-> RTMP Server | 158 | XSPLIT, | +-> Transcoder -------+-> RTMP Server |
127 | ...) | +-> DVR --------------+-> FILE | 159 | ...) | +-> DVR --------------+-> FILE |
  160 +| | +-> BandwidthTest-----+-> Flash/StLoad |
128 +-----------+-------------------------+----------------+ 161 +-----------+-------------------------+----------------+
129 </pre> 162 </pre>
  163 +Bandwidth Test Workflow:
  164 +<pre>
  165 + +------------+ +----------+
  166 + | Client | | Server |
  167 + +-----+------+ +-----+----+
  168 + | |
  169 + | connect vhost-------------> |
  170 + | &lt;-----------result(success) |
  171 + | |
  172 + | &lt;----------call(start play) |
  173 + | result(playing)----------> |
  174 + | &lt;-------------data(playing) |
  175 + | &lt;-----------call(stop play) |
  176 + | result(stopped)----------> |
  177 + | |
  178 + | &lt;-------call(start publish) |
  179 + | result(publishing)-------> |
  180 + | data(publishing)---------> |
  181 + | &lt;--------call(stop publish) |
  182 + | result(stopped)(1)-------> |
  183 + | |
  184 + | &lt;--------------------report |
  185 + | final(2)-----------------> |
  186 + | &lt;END> |
  187 +
  188 +@see: class SrsBandwidth comments.
  189 +</pre>
130 190
131 ### System Requirements 191 ### System Requirements
132 Supported operating systems and hardware: 192 Supported operating systems and hardware:
@@ -154,15 +214,17 @@ Supported operating systems and hardware: @@ -154,15 +214,17 @@ Supported operating systems and hardware:
154 18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/> 214 18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
155 19. support audio transcode only, speex/mp3 to aac<br/> 215 19. support audio transcode only, speex/mp3 to aac<br/>
156 20. support http callback api hooks(for authentication and injection).<br/> 216 20. support http callback api hooks(for authentication and injection).<br/>
157 -21. [plan] support network based cli and json result.<br/>  
158 -22. [plan] support bandwidth test api and flash client.<br/>  
159 -23. [plan] support adobe flash refer/token/swf verification.<br/>  
160 -24. [plan] support adobe amf3 codec.<br/>  
161 -25. [plan] support dvr(record live to vod file)<br/>  
162 -26. [plan] support FMS edge protocol<br/>  
163 -27. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>  
164 -28. [plan] support RTMPT, http to tranverse firewalls<br/>  
165 -29. [plan] support file source, transcoding file to live stream<br/> 217 +21. support bandwidth test api and flash client.<br/>
  218 +22. player, publisher(encoder), and demo pages(jquery+bootstrap). <br/>
  219 +23. demo video meeting or chat(srs+cherrypy+jquery+bootstrap). <br/>
  220 +24. [plan] support network based cli and json result.<br/>
  221 +25. [plan] support adobe flash refer/token/swf verification.<br/>
  222 +26. [plan] support adobe amf3 codec.<br/>
  223 +27. [plan] support dvr(record live to vod file)<br/>
  224 +28. [plan] support FMS edge protocol<br/>
  225 +29. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
  226 +30. [plan] support RTMPT, http to tranverse firewalls<br/>
  227 +31. [plan] support file source, transcoding file to live stream<br/>
166 228
167 ### Performance 229 ### Performance
168 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. 230 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
@@ -213,6 +275,10 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw @@ -213,6 +275,10 @@ usr sys idl wai hiq siq| read writ| recv send| in out | int csw
213 * nginx v1.5.0: 139524 lines <br/> 275 * nginx v1.5.0: 139524 lines <br/>
214 276
215 ### History 277 ### History
  278 +* v0.9, 2013-12-22, demo video meeting or chat(srs+cherrypy+jquery+bootstrap).
  279 +* v0.9, 2013-12-22, merge from wenjie, support banwidth test.
  280 +* v0.9, 2013-12-22, merge from wenjie: support set chunk size at vhost level
  281 +* v0.9, 2013-12-21, add [players](http://demo.srs.com/players) for play and publish.
216 * v0.9, 2013-12-15, ensure the HLS(ts) is continous when republish stream. 282 * v0.9, 2013-12-15, ensure the HLS(ts) is continous when republish stream.
217 * v0.9, 2013-12-15, fix the hls reload bug, feed it the sequence header. 283 * v0.9, 2013-12-15, fix the hls reload bug, feed it the sequence header.
218 * v0.9, 2013-12-15, refine protocol, use int64_t timestamp for ts and jitter. 284 * v0.9, 2013-12-15, refine protocol, use int64_t timestamp for ts and jitter.
不能预览此文件类型
不能预览此文件类型
@@ -28,10 +28,14 @@ vhost __defaultVhost__ { @@ -28,10 +28,14 @@ vhost __defaultVhost__ {
28 # for which cannot identify the required vhost. 28 # for which cannot identify the required vhost.
29 # for default demo. 29 # for default demo.
30 vhost demo.srs.com { 30 vhost demo.srs.com {
  31 + chunk_size 4096;
31 enabled on; 32 enabled on;
32 gop_cache on; 33 gop_cache on;
33 queue_length 30; 34 queue_length 30;
34 forward 127.0.0.1:19350; 35 forward 127.0.0.1:19350;
  36 + bandcheck {
  37 + enabled off;
  38 + }
35 hls { 39 hls {
36 enabled on; 40 enabled on;
37 hls_path ./objs/nginx/html; 41 hls_path ./objs/nginx/html;
@@ -140,6 +144,14 @@ vhost players_pub { @@ -140,6 +144,14 @@ vhost players_pub {
140 hls_window 30; 144 hls_window 30;
141 } 145 }
142 } 146 }
  147 +# rtmp only, no hls, for chat(low latecy)
  148 +vhost players_pub_rtmp {
  149 + gop_cache off;
  150 + hls {
  151 + enabled off;
  152 + }
  153 +}
  154 +
143 # for development 155 # for development
144 vhost dev { 156 vhost dev {
145 chunk_size 65000; 157 chunk_size 65000;
@@ -201,16 +213,38 @@ vhost dev { @@ -201,16 +213,38 @@ vhost dev {
201 } 213 }
202 } 214 }
203 215
  216 +# vhost for bandwidth check
  217 +# generally, the bandcheck vhost must be: bandcheck.srs.com,
  218 +# or need to modify the vhost of client.
204 vhost bandcheck.srs.com { 219 vhost bandcheck.srs.com {
205 - chunk_size 65000;  
206 - enabled on;  
207 - # vhost for band width check  
208 - bandcheck{  
209 - enabled on;  
210 - key test kate;  
211 - interval 5;  
212 - limit_kbps 4000;  
213 - } 220 + enabled on;
  221 + chunk_size 65000;
  222 + # bandwidth check config.
  223 + bandcheck {
  224 + # whether support bandwidth check,
  225 + # default: off.
  226 + enabled on;
  227 + # the key for server to valid,
  228 + # if invalid key, server disconnect and abort the bandwidth check.
  229 + key 35c9b402c12a7246868752e2878f7e0e;
  230 + # the interval in seconds for bandwidth check,
  231 + # server donot allow new test request.
  232 + # default: 30
  233 + interval 30;
  234 + # the max available check bandwidth in kbps.
  235 + # to avoid attack of bandwidth check.
  236 + # default: 1000
  237 + limit_kbps 4000;
  238 + }
  239 +}
  240 +
  241 +# set the chunk size of vhost.
  242 +vhost chunksize.vhost.com {
  243 + # the default chunk size is 128, max is 65536,
  244 + # some client does not support chunk size change,
  245 + # vhost chunk size will override the global value.
  246 + # default: global chunk size.
  247 + chunk_size 128;
214 } 248 }
215 249
216 # the http hook callback vhost, srs will invoke the hooks for specified events. 250 # the http hook callback vhost, srs will invoke the hooks for specified events.
@@ -304,6 +338,7 @@ vhost hooks.callback.vhost.com { @@ -304,6 +338,7 @@ vhost hooks.callback.vhost.com {
304 on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions; 338 on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
305 } 339 }
306 } 340 }
  341 +
307 # the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction 342 # the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
308 vhost mirror.transcode.vhost.com { 343 vhost mirror.transcode.vhost.com {
309 transcode { 344 transcode {
@@ -683,6 +718,7 @@ vhost stream.transcode.vhost.com { @@ -683,6 +718,7 @@ vhost stream.transcode.vhost.com {
683 } 718 }
684 } 719 }
685 } 720 }
  721 +
686 # the vhost which forward publish streams. 722 # the vhost which forward publish streams.
687 vhost same.vhost.forward.vhost.com { 723 vhost same.vhost.forward.vhost.com {
688 # forward all publish stream to the specified server. 724 # forward all publish stream to the specified server.
@@ -698,6 +734,7 @@ vhost same.vhost.forward.vhost.com { @@ -698,6 +734,7 @@ vhost same.vhost.forward.vhost.com {
698 vhost change.vhost.forward.vhost.com { 734 vhost change.vhost.forward.vhost.com {
699 forward 127.0.0.1:1936?vhost=forward2.vhost.com 127.0.0.1:1937?vhost=forward3.vhost.com; 735 forward 127.0.0.1:1936?vhost=forward2.vhost.com 127.0.0.1:1937?vhost=forward3.vhost.com;
700 } 736 }
  737 +
701 # the vhost disabled. 738 # the vhost disabled.
702 vhost removed.vhost.com { 739 vhost removed.vhost.com {
703 # whether the vhost is enabled. 740 # whether the vhost is enabled.
@@ -705,6 +742,7 @@ vhost removed.vhost.com { @@ -705,6 +742,7 @@ vhost removed.vhost.com {
705 # default: on 742 # default: on
706 enabled off; 743 enabled off;
707 } 744 }
  745 +
708 # the vhost with hls specified. 746 # the vhost with hls specified.
709 vhost with-hls.vhost.com { 747 vhost with-hls.vhost.com {
710 hls { 748 hls {
@@ -741,6 +779,7 @@ vhost no-hls.vhost.com { @@ -741,6 +779,7 @@ vhost no-hls.vhost.com {
741 enabled off; 779 enabled off;
742 } 780 }
743 } 781 }
  782 +
744 # the vhost for min delay, donot cache any stream. 783 # the vhost for min delay, donot cache any stream.
745 vhost min.delay.com { 784 vhost min.delay.com {
746 # whether cache the last gop. 785 # whether cache the last gop.
@@ -758,6 +797,7 @@ vhost min.delay.com { @@ -758,6 +797,7 @@ vhost min.delay.com {
758 # default: 30 797 # default: 30
759 queue_length 10; 798 queue_length 10;
760 } 799 }
  800 +
761 # the vhost for antisuck. 801 # the vhost for antisuck.
762 vhost refer.anti_suck.com { 802 vhost refer.anti_suck.com {
763 # the common refer for play and publish. 803 # the common refer for play and publish.
@@ -776,6 +816,7 @@ vhost refer.anti_suck.com { @@ -776,6 +816,7 @@ vhost refer.anti_suck.com {
776 # default: not specified. 816 # default: not specified.
777 refer_play github.com github.io; 817 refer_play github.com github.io;
778 } 818 }
  819 +
779 # config for the pithy print, 820 # config for the pithy print,
780 # which always print constant message specified by interval, 821 # which always print constant message specified by interval,
781 # whatever the clients in concurrency. 822 # whatever the clients in concurrency.
@@ -116,7 +116,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server" @@ -116,7 +116,7 @@ MODULE_FILES=("srs_core" "srs_core_log" "srs_core_server"
116 "srs_core_handshake" "srs_core_pithy_print" 116 "srs_core_handshake" "srs_core_pithy_print"
117 "srs_core_config" "srs_core_refer" "srs_core_reload" 117 "srs_core_config" "srs_core_refer" "srs_core_reload"
118 "srs_core_hls" "srs_core_forward" "srs_core_encoder" 118 "srs_core_hls" "srs_core_forward" "srs_core_encoder"
119 - "srs_core_http" "srs_core_thread") 119 + "srs_core_http" "srs_core_thread" "srs_core_bandwidth")
120 MODULE_DIR="src/core" . auto/modules.sh 120 MODULE_DIR="src/core" . auto/modules.sh
121 CORE_OBJS="${MODULE_OBJS[@]}" 121 CORE_OBJS="${MODULE_OBJS[@]}"
122 122
@@ -36,7 +36,7 @@ reload(sys) @@ -36,7 +36,7 @@ reload(sys)
36 exec("sys.setdefaultencoding('utf-8')") 36 exec("sys.setdefaultencoding('utf-8')")
37 assert sys.getdefaultencoding().lower() == "utf-8" 37 assert sys.getdefaultencoding().lower() == "utf-8"
38 38
39 -import json, datetime, cherrypy 39 +import os, json, time, datetime, cherrypy, threading
40 40
41 # simple log functions. 41 # simple log functions.
42 def trace(msg): 42 def trace(msg):
@@ -320,6 +320,123 @@ class RESTSessions(object): @@ -320,6 +320,123 @@ class RESTSessions(object):
320 320
321 return code 321 return code
322 322
  323 +global_chat_id = os.getpid();
  324 +'''
  325 +the chat streams, public chat room.
  326 +'''
  327 +class RESTChats(object):
  328 + exposed = True
  329 + global_id = 100
  330 +
  331 + def __init__(self):
  332 + # object fields:
  333 + # id: an int value indicates the id of user.
  334 + # username: a str indicates the user name.
  335 + # url: a str indicates the url of user stream.
  336 + # agent: a str indicates the agent of user.
  337 + # join_date: a number indicates the join timestamp in seconds.
  338 + # join_date_str: a str specifies the formated friendly time.
  339 + # heatbeat: a number indicates the heartbeat timestamp in seconds.
  340 + # vcodec: a dict indicates the video codec info.
  341 + # acodec: a dict indicates the audio codec info.
  342 + self.__chats = [];
  343 + self.__chat_lock = threading.Lock();
  344 +
  345 + # dead time in seconds, if exceed, remove the chat.
  346 + self.__dead_time = 30;
  347 +
  348 + def GET(self):
  349 + enable_crossdomain()
  350 +
  351 + try:
  352 + self.__chat_lock.acquire();
  353 +
  354 + chats = [];
  355 + copy = self.__chats[:];
  356 + for chat in copy:
  357 + if time.time() - chat["heartbeat"] > self.__dead_time:
  358 + self.__chats.remove(chat);
  359 + continue;
  360 +
  361 + chats.append({
  362 + "id": chat["id"],
  363 + "username": chat["username"],
  364 + "url": chat["url"],
  365 + "join_date_str": chat["join_date_str"],
  366 + "heartbeat": chat["heartbeat"],
  367 + });
  368 + finally:
  369 + self.__chat_lock.release();
  370 +
  371 + return json.dumps({"code":0, "data": {"now": time.time(), "chats": chats}})
  372 +
  373 + def POST(self):
  374 + enable_crossdomain()
  375 +
  376 + req = cherrypy.request.body.read()
  377 + chat = json.loads(req)
  378 +
  379 + global global_chat_id;
  380 + chat["id"] = global_chat_id
  381 + global_chat_id += 1
  382 +
  383 + chat["join_date"] = time.time();
  384 + chat["heartbeat"] = time.time();
  385 + chat["join_date_str"] = time.strftime("%Y-%m-%d %H:%M:%S");
  386 +
  387 + try:
  388 + self.__chat_lock.acquire();
  389 +
  390 + self.__chats.append(chat)
  391 + finally:
  392 + self.__chat_lock.release();
  393 +
  394 + trace("create chat success, id=%s"%(chat["id"]))
  395 +
  396 + return json.dumps({"code":0, "data": chat["id"]})
  397 +
  398 + def DELETE(self, id):
  399 + enable_crossdomain()
  400 +
  401 + try:
  402 + self.__chat_lock.acquire();
  403 +
  404 + for chat in self.__chats:
  405 + if str(id) != str(chat["id"]):
  406 + continue
  407 +
  408 + self.__chats.remove(chat)
  409 + trace("delete chat success, id=%s"%(id))
  410 +
  411 + return json.dumps({"code":0, "data": None})
  412 + finally:
  413 + self.__chat_lock.release();
  414 +
  415 + raise cherrypy.HTTPError(405, "Not allowed.")
  416 +
  417 + def PUT(self, id):
  418 + enable_crossdomain()
  419 +
  420 + try:
  421 + self.__chat_lock.acquire();
  422 +
  423 + for chat in self.__chats:
  424 + if str(id) != str(chat["id"]):
  425 + continue
  426 +
  427 + chat["heartbeat"] = time.time();
  428 + trace("heartbeat chat success, id=%s"%(id))
  429 +
  430 + return json.dumps({"code":0, "data": None})
  431 + finally:
  432 + self.__chat_lock.release();
  433 +
  434 + raise cherrypy.HTTPError(405, "Not allowed.")
  435 +
  436 +
  437 + def OPTIONS(self, id=None):
  438 + enable_crossdomain()
  439 +
323 # HTTP RESTful path. 440 # HTTP RESTful path.
324 class Root(object): 441 class Root(object):
325 def __init__(self): 442 def __init__(self):
@@ -335,6 +452,7 @@ class V1(object): @@ -335,6 +452,7 @@ class V1(object):
335 self.clients = RESTClients() 452 self.clients = RESTClients()
336 self.streams = RESTStreams() 453 self.streams = RESTStreams()
337 self.sessions = RESTSessions() 454 self.sessions = RESTSessions()
  455 + self.chats = RESTChats()
338 456
339 ''' 457 '''
340 main code start. 458 main code start.
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 <ul class="nav"> 31 <ul class="nav">
32 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 32 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
33 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 33 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  34 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
34 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 35 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
35 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 36 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
36 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 37 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
1 ////////////////////////////////////////////////////////////////////////////////// 1 //////////////////////////////////////////////////////////////////////////////////
2 ////////////////////////////////////////////////////////////////////////////////// 2 //////////////////////////////////////////////////////////////////////////////////
3 ////////////////////////////////////////////////////////////////////////////////// 3 //////////////////////////////////////////////////////////////////////////////////
  4 +
  5 +/**
  6 +* player specified size.
  7 +*/
  8 +function srs_get_player_modal() { return 740; }
  9 +function srs_get_player_width() { return srs_get_player_modal() - 30; }
  10 +function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
  11 +
  12 +// to query the swf anti cache.
  13 +function srs_get_version_code() { return "1.9"; }
  14 +// get the default vhost for players.
  15 +function srs_get_player_vhost() { return "players"; }
  16 +// the api server port, for chat room.
  17 +function srs_get_api_server_port() { return 8085; }
  18 +// get the stream published to vhost,
  19 +// generally we need to transcode the stream to support HLS and filters.
  20 +// for example, src_vhost is "players", we transcode stream to vhost "players_pub".
  21 +// if not equals to the player vhost, return the orignal vhost.
  22 +function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); }
  23 +// for chat, use rtmp only vhost, low latecy, without gop cache.
  24 +function srs_get_player_chat_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub_rtmp"); }
  25 +
  26 +//////////////////////////////////////////////////////////////////////////////////
  27 +//////////////////////////////////////////////////////////////////////////////////
  28 +//////////////////////////////////////////////////////////////////////////////////
4 /** 29 /**
5 * padding the output. 30 * padding the output.
6 * padding(3, 5, '0') is 00003 31 * padding(3, 5, '0') is 00003
@@ -20,6 +45,7 @@ function padding(number, length, prefix) { @@ -20,6 +45,7 @@ function padding(number, length, prefix) {
20 function update_nav() { 45 function update_nav() {
21 $("#nav_srs_player").attr("href", "srs_player.html" + window.location.search); 46 $("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
22 $("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search); 47 $("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
  48 + $("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search);
23 $("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search); 49 $("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
24 $("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search); 50 $("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search);
25 $("#nav_osmf").attr("href", "osmf.html" + window.location.search); 51 $("#nav_osmf").attr("href", "osmf.html" + window.location.search);
@@ -27,6 +53,31 @@ function update_nav() { @@ -27,6 +53,31 @@ function update_nav() {
27 } 53 }
28 54
29 /** 55 /**
  56 +* log specified, there must be a log element as:
  57 + <!-- for the log -->
  58 + <div class="alert alert-info fade in" id="txt_log">
  59 + <button type="button" class="close" data-dismiss="alert">×</button>
  60 + <strong><span id="txt_log_title">Usage:</span></strong>
  61 + <span id="txt_log_msg">创建会议室,或者加入会议室</span>
  62 + </div>
  63 +*/
  64 +function info(desc) {
  65 + $("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
  66 + $("#txt_log_title").text("Info:");
  67 + $("#txt_log_msg").text(desc);
  68 +}
  69 +function warn(code, desc) {
  70 + $("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
  71 + $("#txt_log_title").text("Warn:");
  72 + $("#txt_log_msg").text("code: " + code + ", " + desc);
  73 +}
  74 +function error(code, desc) {
  75 + $("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
  76 + $("#txt_log_title").text("Error:");
  77 + $("#txt_log_msg").text("code: " + code + ", " + desc);
  78 +}
  79 +
  80 +/**
30 * parse the query string to object. 81 * parse the query string to object.
31 */ 82 */
32 function parse_query_string(){ 83 function parse_query_string(){
@@ -82,6 +133,23 @@ function build_default_rtmp_url() { @@ -82,6 +133,23 @@ function build_default_rtmp_url() {
82 return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream; 133 return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream;
83 } 134 }
84 } 135 }
  136 +// for the chat to init the publish url.
  137 +function build_default_publish_rtmp_url() {
  138 + var query = parse_query_string();
  139 +
  140 + var server = (query.server == undefined)? window.location.hostname:query.server;
  141 + var port = (query.port == undefined)? 1935:query.port;
  142 + var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
  143 + var app = (query.app == undefined)? "live":query.app;
  144 + var stream = (query.stream == undefined)? "livestream":query.stream;
  145 +
  146 + if (server == vhost || vhost == "") {
  147 + return "rtmp://" + server + ":" + port + "/" + app + "/" + stream;
  148 + } else {
  149 + vhost = srs_get_player_chat_vhost(vhost);
  150 + return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream;
  151 + }
  152 +}
85 153
86 /** 154 /**
87 @param server the ip of server. default to window.location.hostname 155 @param server the ip of server. default to window.location.hostname
@@ -151,23 +219,6 @@ function srs_parse_rtmp_url(rtmp_url) { @@ -151,23 +219,6 @@ function srs_parse_rtmp_url(rtmp_url) {
151 } 219 }
152 220
153 /** 221 /**
154 -* player specified size.  
155 -*/  
156 -function srs_get_player_modal() { return 740; }  
157 -function srs_get_player_width() { return srs_get_player_modal() - 30; }  
158 -function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }  
159 -  
160 -// to query the swf anti cache.  
161 -function srs_get_version_code() { return "1.5"; }  
162 -// get the default vhost for players.  
163 -function srs_get_player_vhost() { return "players"; }  
164 -// get the stream published to vhost,  
165 -// generally we need to transcode the stream to support HLS and filters.  
166 -// for example, src_vhost is "players", we transcode stream to vhost "players_pub".  
167 -// if not equals to the player vhost, return the orignal vhost.  
168 -function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); }  
169 -  
170 -/**  
171 * initialize the page. 222 * initialize the page.
172 * @param rtmp_url the div id contains the rtmp stream url to play 223 * @param rtmp_url the div id contains the rtmp stream url to play
173 * @param hls_url the div id contains the hls stream url to play 224 * @param hls_url the div id contains the hls stream url to play
@@ -187,6 +238,190 @@ function srs_init(rtmp_url, hls_url, modal_player) { @@ -187,6 +238,190 @@ function srs_init(rtmp_url, hls_url, modal_player) {
187 $(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px"); 238 $(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px");
188 } 239 }
189 } 240 }
  241 +// for the chat to init the publish url.
  242 +function srs_init_publish(rtmp_url) {
  243 + update_nav();
  244 +
  245 + if (rtmp_url) {
  246 + $(rtmp_url).val(build_default_publish_rtmp_url());
  247 + }
  248 +}
  249 +
  250 +/**
  251 +* when publisher ready, init the page elements.
  252 +*/
  253 +function srs_publisher_initialize_page(
  254 + cameras, microphones,
  255 + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
  256 +) {
  257 + $(sl_cameras).empty();
  258 + for (var i = 0; i < cameras.length; i++) {
  259 + $(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
  260 + }
  261 + // optional: select the first no "virtual" signed.
  262 + for (var i = 0; i < cameras.length; i++) {
  263 + if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
  264 + $(sl_cameras + " option[value='" + i + "']").attr("selected", true);
  265 + break;
  266 + }
  267 + }
  268 +
  269 + $(sl_microphones).empty();
  270 + for (var i = 0; i < microphones.length; i++) {
  271 + $(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
  272 + }
  273 +
  274 + $(sl_vcodec).empty();
  275 + var vcodecs = ["h264", "vp6"];
  276 + for (var i = 0; i < vcodecs.length; i++) {
  277 + $(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
  278 + }
  279 +
  280 + $(sl_profile).empty();
  281 + var profiles = ["baseline", "main"];
  282 + for (var i = 0; i < profiles.length; i++) {
  283 + $(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
  284 + }
  285 + $(sl_profile + " option[value='main']").attr("selected", true);
  286 +
  287 + $(sl_level).empty();
  288 + var levels = ["1", "1b", "1.1", "1.2", "1.3",
  289 + "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
  290 + for (var i = 0; i < levels.length; i++) {
  291 + $(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
  292 + }
  293 + $(sl_level + " option[value='4.1']").attr("selected", true);
  294 +
  295 + $(sl_gop).empty();
  296 + var gops = ["0.3", "0.5", "1", "2", "3", "4",
  297 + "5", "6", "7", "8", "9", "10", "15", "20"];
  298 + for (var i = 0; i < gops.length; i++) {
  299 + $(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
  300 + }
  301 + $(sl_gop + " option[value='10']").attr("selected", true);
  302 +
  303 + $(sl_size).empty();
  304 + var sizes = ["176x144", "320x240", "352x240",
  305 + "352x288", "460x240", "640x480", "720x480", "720x576", "800x600",
  306 + "1024x768", "1280x720", "1360x768", "1920x1080"];
  307 + for (i = 0; i < sizes.length; i++) {
  308 + $(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
  309 + }
  310 + $(sl_size + " option[value='640x480']").attr("selected", true);
  311 +
  312 + $(sl_fps).empty();
  313 + var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
  314 + for (i = 0; i < fpses.length; i++) {
  315 + $(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
  316 + }
  317 + $(sl_fps + " option[value='20']").attr("selected", true);
  318 +
  319 + $(sl_bitrate).empty();
  320 + var bitrates = ["50", "200", "350", "500", "650", "800",
  321 + "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
  322 + for (i = 0; i < bitrates.length; i++) {
  323 + $(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
  324 + }
  325 + $(sl_bitrate + " option[value='500']").attr("selected", true);
  326 +}
  327 +/**
  328 +* for chat, use low latecy settings.
  329 +*/
  330 +function srs_chat_initialize_page(
  331 + cameras, microphones,
  332 + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
  333 +) {
  334 + $(sl_cameras).empty();
  335 + for (var i = 0; i < cameras.length; i++) {
  336 + $(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
  337 + }
  338 + // optional: select the first no "virtual" signed.
  339 + for (var i = 0; i < cameras.length; i++) {
  340 + if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
  341 + $(sl_cameras + " option[value='" + i + "']").attr("selected", true);
  342 + break;
  343 + }
  344 + }
  345 +
  346 + $(sl_microphones).empty();
  347 + for (var i = 0; i < microphones.length; i++) {
  348 + $(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
  349 + }
  350 +
  351 + $(sl_vcodec).empty();
  352 + var vcodecs = ["h264", "vp6"];
  353 + for (var i = 0; i < vcodecs.length; i++) {
  354 + $(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
  355 + }
  356 +
  357 + $(sl_profile).empty();
  358 + var profiles = ["baseline", "main"];
  359 + for (var i = 0; i < profiles.length; i++) {
  360 + $(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
  361 + }
  362 + $(sl_profile + " option[value='baseline']").attr("selected", true);
  363 +
  364 + $(sl_level).empty();
  365 + var levels = ["1", "1b", "1.1", "1.2", "1.3",
  366 + "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
  367 + for (var i = 0; i < levels.length; i++) {
  368 + $(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
  369 + }
  370 + $(sl_level + " option[value='3.1']").attr("selected", true);
  371 +
  372 + $(sl_gop).empty();
  373 + var gops = ["0.3", "0.5", "1", "2", "3", "4",
  374 + "5", "6", "7", "8", "9", "10", "15", "20"];
  375 + for (var i = 0; i < gops.length; i++) {
  376 + $(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
  377 + }
  378 + $(sl_gop + " option[value='0.5']").attr("selected", true);
  379 +
  380 + $(sl_size).empty();
  381 + var sizes = ["176x144", "320x240", "352x240",
  382 + "352x288", "460x240", "640x480", "720x480", "720x576", "800x600",
  383 + "1024x768", "1280x720", "1360x768", "1920x1080"];
  384 + for (i = 0; i < sizes.length; i++) {
  385 + $(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
  386 + }
  387 + $(sl_size + " option[value='460x240']").attr("selected", true);
  388 +
  389 + $(sl_fps).empty();
  390 + var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
  391 + for (i = 0; i < fpses.length; i++) {
  392 + $(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
  393 + }
  394 + $(sl_fps + " option[value='15']").attr("selected", true);
  395 +
  396 + $(sl_bitrate).empty();
  397 + var bitrates = ["50", "200", "350", "500", "650", "800",
  398 + "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
  399 + for (i = 0; i < bitrates.length; i++) {
  400 + $(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
  401 + }
  402 + $(sl_bitrate + " option[value='350']").attr("selected", true);
  403 +}
  404 +/**
  405 +* get the vcodec and acodec.
  406 +*/
  407 +function srs_publiser_get_codec(
  408 + vcodec, acodec,
  409 + sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate
  410 +) {
  411 + acodec.device_code = $(sl_microphones).val();
  412 + acodec.device_name = $(sl_microphones).text();
  413 +
  414 + vcodec.device_code = $(sl_cameras).find("option:selected").val();
  415 + vcodec.device_name = $(sl_cameras).find("option:selected").text();
  416 +
  417 + vcodec.codec = $(sl_vcodec).find("option:selected").val();
  418 + vcodec.profile = $(sl_profile).find("option:selected").val();
  419 + vcodec.level = $(sl_level).find("option:selected").val();
  420 + vcodec.fps = $(sl_fps).find("option:selected").val();
  421 + vcodec.gop = $(sl_gop).find("option:selected").val();
  422 + vcodec.size = $(sl_size).find("option:selected").val();
  423 + vcodec.bitrate = $(sl_bitrate).find("option:selected").val();
  424 +}
190 425
191 ////////////////////////////////////////////////////////////////////////////////// 426 //////////////////////////////////////////////////////////////////////////////////
192 ////////////////////////////////////////////////////////////////////////////////// 427 //////////////////////////////////////////////////////////////////////////////////
@@ -196,8 +431,10 @@ function srs_init(rtmp_url, hls_url, modal_player) { @@ -196,8 +431,10 @@ function srs_init(rtmp_url, hls_url, modal_player) {
196 * @param container the html container id. 431 * @param container the html container id.
197 * @param width a float value specifies the width of player. 432 * @param width a float value specifies the width of player.
198 * @param height a float value specifies the height of player. 433 * @param height a float value specifies the height of player.
  434 +* @param private_object [optional] an object that used as private object,
  435 +* for example, the logic chat object which owner this player.
199 */ 436 */
200 -function SrsPlayer(container, width, height) { 437 +function SrsPlayer(container, width, height, private_object) {
201 if (!SrsPlayer.__id) { 438 if (!SrsPlayer.__id) {
202 SrsPlayer.__id = 100; 439 SrsPlayer.__id = 100;
203 } 440 }
@@ -207,6 +444,7 @@ function SrsPlayer(container, width, height) { @@ -207,6 +444,7 @@ function SrsPlayer(container, width, height) {
207 444
208 SrsPlayer.__players.push(this); 445 SrsPlayer.__players.push(this);
209 446
  447 + this.private_object = private_object;
210 this.container = container; 448 this.container = container;
211 this.width = width; 449 this.width = width;
212 this.height = height; 450 this.height = height;
@@ -222,11 +460,16 @@ function SrsPlayer(container, width, height) { @@ -222,11 +460,16 @@ function SrsPlayer(container, width, height) {
222 } 460 }
223 /** 461 /**
224 * user can set some callback, then start the player. 462 * user can set some callback, then start the player.
  463 +* @param url the default url.
225 * callbacks: 464 * callbacks:
226 * on_player_ready():int, when srs player ready, user can play. 465 * on_player_ready():int, when srs player ready, user can play.
227 * on_player_metadata(metadata:Object):int, when srs player get metadata. 466 * on_player_metadata(metadata:Object):int, when srs player get metadata.
228 */ 467 */
229 -SrsPlayer.prototype.start = function() { 468 +SrsPlayer.prototype.start = function(url) {
  469 + if (url) {
  470 + this.stream_url = url;
  471 + }
  472 +
230 // embed the flash. 473 // embed the flash.
231 var flashvars = {}; 474 var flashvars = {};
232 flashvars.id = this.id; 475 flashvars.id = this.id;
@@ -261,7 +504,9 @@ SrsPlayer.prototype.start = function() { @@ -261,7 +504,9 @@ SrsPlayer.prototype.start = function() {
261 * @param stream_url the url of stream, rtmp or http. 504 * @param stream_url the url of stream, rtmp or http.
262 */ 505 */
263 SrsPlayer.prototype.play = function(url) { 506 SrsPlayer.prototype.play = function(url) {
264 - this.stream_url = url; 507 + if (url) {
  508 + this.stream_url = url;
  509 + }
265 this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time); 510 this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time);
266 } 511 }
267 SrsPlayer.prototype.stop = function() { 512 SrsPlayer.prototype.stop = function() {
@@ -373,8 +618,10 @@ function __srs_on_player_timer(id, time, buffer_length) { @@ -373,8 +618,10 @@ function __srs_on_player_timer(id, time, buffer_length) {
373 * @param container the html container id. 618 * @param container the html container id.
374 * @param width a float value specifies the width of publisher. 619 * @param width a float value specifies the width of publisher.
375 * @param height a float value specifies the height of publisher. 620 * @param height a float value specifies the height of publisher.
  621 +* @param private_object [optional] an object that used as private object,
  622 +* for example, the logic chat object which owner this publisher.
376 */ 623 */
377 -function SrsPublisher(container, width, height) { 624 +function SrsPublisher(container, width, height, private_object) {
378 if (!SrsPublisher.__id) { 625 if (!SrsPublisher.__id) {
379 SrsPublisher.__id = 100; 626 SrsPublisher.__id = 100;
380 } 627 }
@@ -384,6 +631,7 @@ function SrsPublisher(container, width, height) { @@ -384,6 +631,7 @@ function SrsPublisher(container, width, height) {
384 631
385 SrsPublisher.__publishers.push(this); 632 SrsPublisher.__publishers.push(this);
386 633
  634 + this.private_object = private_object;
387 this.container = container; 635 this.container = container;
388 this.width = width; 636 this.width = width;
389 this.height = height; 637 this.height = height;
@@ -83,6 +83,7 @@ @@ -83,6 +83,7 @@
83 <ul class="nav"> 83 <ul class="nav">
84 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 84 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
85 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 85 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  86 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
86 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 87 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
87 <li class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 88 <li class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
88 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 89 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
@@ -79,6 +79,7 @@ @@ -79,6 +79,7 @@
79 <ul class="nav"> 79 <ul class="nav">
80 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 80 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
81 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 81 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  82 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
82 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 83 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
83 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 84 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
84 <li class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 85 <li class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 <ul class="nav"> 28 <ul class="nav">
29 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 29 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
30 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 30 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  31 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
31 <li class="active"><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 32 <li class="active"><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
32 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 33 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
33 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 34 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <title>SRS</title>
  5 + <meta charset="utf-8">
  6 + <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
  7 + <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
  8 + <script type="text/javascript" src="js/bootstrap.min.js"></script>
  9 + <script type="text/javascript" src="js/swfobject.js"></script>
  10 + <script type="text/javascript" src="js/srs.js"></script>
  11 + <style>
  12 + body{
  13 + padding-top: 55px;
  14 + }
  15 + </style>
  16 + <script type="text/javascript">
  17 + var srs_publisher = null;
  18 + var realtime_player = null;
  19 + var api_server = null;
  20 + var self_chat = null;
  21 + var global_chat_user_id = 200;
  22 + var previous_chats = [];
  23 + var no_play = false;
  24 +
  25 + $(function(){
  26 + // get the vhost and port to set the default url.
  27 + // for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
  28 + // url set to: rtmp://demo:1935/live/livestream
  29 + srs_init_publish("#txt_url");
  30 +
  31 + $("#realtime_player_url").tooltip({
  32 + title: "右键复制RTMP地址"
  33 + });
  34 +
  35 + // if no play specified, donot show the player, for debug the publisher.
  36 + var query = parse_query_string();
  37 + if (query.no_play == "true") {
  38 + no_play = true;
  39 + }
  40 +
  41 + $("#btn_video_settings").click(function(){
  42 + $("#video_modal").modal({show:true});
  43 + });
  44 + $("#btn_audio_settings").click(function(){
  45 + $("#audio_modal").modal({show:true});
  46 + });
  47 + $("#btn_join").click(on_user_publish);
  48 +
  49 + // for publish, we use randome stream name.
  50 + $("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
  51 +
  52 + // start the publisher.
  53 + srs_publisher = new SrsPublisher("local_publisher", 430, 185);
  54 + srs_publisher.on_publisher_ready = function(cameras, microphones) {
  55 + srs_chat_initialize_page(
  56 + cameras, microphones,
  57 + "#sl_cameras", "#sl_microphones",
  58 + "#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
  59 + "#sl_fps", "#sl_bitrate"
  60 + );
  61 + };
  62 + srs_publisher.on_publisher_error = function(code, desc) {
  63 + error(code, desc);
  64 + };
  65 + srs_publisher.on_publisher_warn = function(code, desc) {
  66 + warn(code, desc);
  67 + };
  68 + srs_publisher.start();
  69 +
  70 + update_play_url();
  71 +
  72 + if (!no_play) {
  73 + // start the realtime player.
  74 + realtime_player = new SrsPlayer("realtime_player", 430, 185);
  75 + realtime_player.on_player_ready = function() {
  76 + this.set_bt(0.5);
  77 + this.set_fs("screen", 100);
  78 + };
  79 + realtime_player.start();
  80 + }
  81 +
  82 + api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats";
  83 + refresh();
  84 + });
  85 +
  86 + function update_play_url() {
  87 + var url = $("#txt_url").val();
  88 +
  89 + $("#realtime_player_url").attr("href", url).attr("target", "_blank");
  90 + }
  91 +
  92 + function refresh() {
  93 + if (!self_chat) {
  94 + setTimeout(refresh, 1000);
  95 + return;
  96 + }
  97 +
  98 + $.ajax({
  99 + type : "GET",
  100 + async : true,
  101 + url : api_server,
  102 + contentType: "text/html",
  103 + data : "",
  104 + dataType : "json",
  105 + complete : function() {
  106 + },
  107 + error : function(ret) {
  108 + setTimeout(refresh, 5000);
  109 + warn(101, "查询会议室失败:" + JSON.stringify(ret));
  110 + },
  111 + success : function(ret) {
  112 + if(0 != ret["code"]) {
  113 + warn(102, "查询会议室失败: " + JSON.stringify(ret));
  114 + setTimeout(refresh, 5000);
  115 + return;
  116 + }
  117 +
  118 + var chats = ret["data"]["chats"];
  119 + var server_time = ret["now"];
  120 +
  121 + on_get_chats(chats);
  122 + setTimeout(refresh, 5000);
  123 + }
  124 + });
  125 + }
  126 + function on_get_chats(chats) {
  127 + if (!self_chat) {
  128 + return;
  129 + }
  130 +
  131 + // get self, check self is valid?
  132 + var _self_chat = null;
  133 + for (var i = 0; i < chats.length; i++) {
  134 + var chat = chats[i];
  135 + if (self_chat && self_chat.id == chat.id) {
  136 + _self_chat = chat;
  137 + break;
  138 + }
  139 + }
  140 + // rejoin if invalid.
  141 + if (!_self_chat) {
  142 + on_user_exit_chat(function(){
  143 + on_user_join_chat(function(){
  144 + info("重新加入会议室成功");
  145 + });
  146 + });
  147 + return;
  148 + }
  149 +
  150 + render_chat_room(chats);
  151 +
  152 + if (!self_chat) {
  153 + return;
  154 + }
  155 +
  156 + // update self heartbeat.
  157 + var url = api_server + "/" + self_chat.id;
  158 +
  159 + var chat = {};
  160 + chat.localtime = new Date().getTime();
  161 +
  162 + // publish to api server to requires an id.
  163 + $.ajax({
  164 + type : "PUT",
  165 + async : true,
  166 + url : url,
  167 + contentType: "text/html",
  168 + data : JSON.stringify(chat),
  169 + dataType : "json",
  170 + complete : function() {
  171 + },
  172 + error : function(ret) {
  173 + warn(105, "更新会议室信息失败:" + JSON.stringify(ret));
  174 + },
  175 + success : function(ret) {
  176 + if(0 != ret["code"]) {
  177 + warn(106, "更新会议室信息失败:: " + JSON.stringify(ret));
  178 + return;
  179 + }
  180 + }
  181 + });
  182 + }
  183 + function render_chat_room(chats) {
  184 + if (!self_chat) {
  185 + return;
  186 + }
  187 +
  188 + // new added chat
  189 + for (var i = 0; i < chats.length; i++) {
  190 + var chat = chats[i];
  191 + // ignore the self.
  192 + if (self_chat && self_chat.id == chat.id) {
  193 + continue;
  194 + }
  195 +
  196 + // if previous exists, ignore, only add new here.
  197 + var previous_chat = get_previous_chat_user(previous_chats, chat.id);
  198 + if (previous_chat) {
  199 + // update reference.
  200 + chat.player = previous_chat.player;
  201 + chat.player.private_object = chat;
  202 + continue;
  203 + }
  204 +
  205 + global_chat_user_id++;
  206 +
  207 + // username: a str indicates the user name.
  208 + // url: a str indicates the url of user stream.
  209 + // join_date: a str indicates the join timestamp in seconds.
  210 + // join_date_str: friendly formated time.
  211 + var obj = $("<div/>").html($("#template").html());
  212 + $(obj).attr("chat_id", chat.id);
  213 + $(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id)
  214 + $(obj).attr("class", "div_chat"); // for all chats: $(".div_chat")
  215 + $(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id)
  216 + $(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id)
  217 + $(obj).find("#user_name").text(chat.username);
  218 + $(obj).find("#join_date").text(chat.join_date_str);
  219 + $(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id);
  220 + $(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id);
  221 +
  222 + $("#lst_chats").append(obj);
  223 +
  224 + if (!no_play) {
  225 + // start the realtime player.
  226 + var _player = new SrsPlayer("rp_raw_" + chat.id, 600, 300, chat);
  227 + _player.on_player_ready = function() {
  228 + this.set_bt(0.5);
  229 + this.set_fs("screen", 100);
  230 + };
  231 + _player.start(chat.url);
  232 +
  233 + chat.player = _player;
  234 + } else {
  235 + chat.player = null;
  236 + }
  237 +
  238 + $(obj).find("#collapse_main").on('hidden', function(){
  239 + var id = $(this).parent().attr("chat_id");
  240 + var chat = get_previous_chat_user(previous_chats, id);
  241 + if (!chat || !chat.player) {
  242 + return;
  243 + }
  244 + chat.player.stop();
  245 + });
  246 + $(obj).find("#collapse_main").on('shown', function(){
  247 + var id = $(this).parent().attr("chat_id");
  248 + var chat = get_previous_chat_user(previous_chats, id);
  249 + if (!chat || !chat.player) {
  250 + return;
  251 + }
  252 + chat.player.play();
  253 + });
  254 + }
  255 +
  256 + // removed chat
  257 + for (var i = 0; i < previous_chats.length; i++) {
  258 + var chat = previous_chats[i];
  259 + // ignore the self.
  260 + if (self_chat && self_chat.id == chat.id) {
  261 + continue;
  262 + }
  263 +
  264 + var new_chat = get_previous_chat_user(chats, chat.id);
  265 + if (new_chat) {
  266 + continue;
  267 + }
  268 +
  269 + if (chat.player) {
  270 + chat.player.stop();
  271 + }
  272 + $("#div_" + chat.id).remove();
  273 + }
  274 +
  275 + previous_chats = chats;
  276 + }
  277 + function get_previous_chat_user(arr, id) {
  278 + for (var i = 0; i < arr.length; i++) {
  279 + var chat = arr[i];
  280 + if (id == chat.id) {
  281 + return chat;
  282 + }
  283 + }
  284 + return null;
  285 + }
  286 +
  287 + function on_user_publish() {
  288 + if ($("#txt_name").val().trim() == "") {
  289 + $("#txt_name").focus().parent().parent().addClass("error");
  290 + warn(100, "请输入您的名字");
  291 + return;
  292 + }
  293 +
  294 + $("#txt_name").parent().parent().removeClass("error");
  295 +
  296 + // join chat.
  297 + if (!self_chat) {
  298 + on_user_join_chat();
  299 + } else {
  300 + on_user_exit_chat();
  301 + }
  302 + }
  303 + function on_user_exit_chat(complete_pfn) {
  304 + srs_publisher.stop();
  305 + $("#btn_join").text("加入会议");
  306 +
  307 + if (realtime_player) {
  308 + realtime_player.stop();
  309 + }
  310 +
  311 + if (!self_chat) {
  312 + return;
  313 + }
  314 +
  315 + // removed chat
  316 + for (var i = 0; i < previous_chats.length; i++) {
  317 + var chat = previous_chats[i];
  318 + // ignore the self.
  319 + if (self_chat && self_chat.id == chat.id) {
  320 + continue;
  321 + }
  322 +
  323 + if (chat.player) {
  324 + chat.player.stop();
  325 + }
  326 + $("#div_" + chat.id).remove();
  327 + }
  328 + previous_chats = [];
  329 +
  330 + var url = api_server + "/" + self_chat.id;
  331 + // whatever, cleanup local chat.
  332 + self_chat = null;
  333 +
  334 + $("#btn_join").attr("disabled", true);
  335 +
  336 + // publish to api server to requires an id.
  337 + $.ajax({
  338 + type : "DELETE",
  339 + async : true,
  340 + url : url,
  341 + contentType: "text/html",
  342 + data : "",
  343 + dataType : "json",
  344 + complete : function() {
  345 + $("#btn_join").attr("disabled", false);
  346 + if (complete_pfn) {
  347 + complete_pfn();
  348 + }
  349 + },
  350 + error : function(ret) {
  351 + warn(103, "退出会议室失败");
  352 + },
  353 + success : function(ret) {
  354 + if(0 != ret["code"]) {
  355 + warn(104, "退出会议室失败");
  356 + return;
  357 + }
  358 + info("退出会议室成功");
  359 + }
  360 + });
  361 + }
  362 + function on_user_join_chat(complete_pfn) {
  363 + if (self_chat) {
  364 + return;
  365 + }
  366 +
  367 + var url = $("#txt_url").val();
  368 + var vcodec = {};
  369 + var acodec = {};
  370 + srs_publiser_get_codec(
  371 + vcodec, acodec,
  372 + "#sl_cameras", "#sl_microphones",
  373 + "#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
  374 + "#sl_fps", "#sl_bitrate"
  375 + );
  376 +
  377 + var chat = {};
  378 + chat.id = -1;
  379 + chat.username = $("#txt_name").val().trim();
  380 + chat.agent = navigator.userAgent;
  381 + chat.url = url;
  382 + chat.vcodec = vcodec;
  383 + chat.acodec = acodec;
  384 +
  385 + $("#btn_join").attr("disabled", true);
  386 +
  387 + // publish to api server to requires an id.
  388 + $.ajax({
  389 + type : "POST",
  390 + async : true,
  391 + url : api_server,
  392 + contentType: "text/html",
  393 + data : JSON.stringify(chat),
  394 + dataType : "json",
  395 + complete : function() {
  396 + $("#btn_join").attr("disabled", false);
  397 + if (complete_pfn) {
  398 + complete_pfn();
  399 + }
  400 + },
  401 + error : function(ret) {
  402 + warn(105, "创建会议室失败:" + JSON.stringify(ret));
  403 + },
  404 + success : function(ret) {
  405 + if(0 != ret["code"]) {
  406 + warn(106, "创建会议室失败: " + JSON.stringify(ret));
  407 + return;
  408 + }
  409 +
  410 + chat.id = ret["data"];
  411 +
  412 + // success, start publish.
  413 + self_chat = chat;
  414 +
  415 + $("#btn_join").text("退出会议");
  416 +
  417 + info("开始推流到服务器");
  418 + srs_publisher.publish(url, vcodec, acodec);
  419 +
  420 + if (realtime_player) {
  421 + // directly play the url for the realtime player.
  422 + realtime_player.stop();
  423 + realtime_player.play(url);
  424 + }
  425 + }
  426 + });
  427 + }
  428 + </script>
  429 +</head>
  430 +<body>
  431 +<div class="navbar navbar-fixed-top">
  432 + <div class="navbar-inner">
  433 + <div class="container">
  434 + <a class="brand" href="index.html">SRS</a>
  435 + <div class="nav-collapse collapse">
  436 + <ul class="nav">
  437 + <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
  438 + <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  439 + <li class="active"><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
  440 + <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
  441 + <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
  442 + <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
  443 + <li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
  444 + </ul>
  445 + </div>
  446 + </div>
  447 + </div>
  448 +</div>
  449 +<div class="container">
  450 + <!-- for the log -->
  451 + <div class="alert alert-info fade in" id="txt_log">
  452 + <button type="button" class="close" data-dismiss="alert">×</button>
  453 + <strong><span id="txt_log_title">Usage:</span></strong>
  454 + <span id="txt_log_msg">输入名字,设置编码参数后,加入会议室</span>
  455 + </div>
  456 +
  457 + <div class="control-group">
  458 + <div class="form-inline">
  459 + <input type="text" id="txt_name" class="input-small" placeholder="您的名字..." value=""></input>
  460 + <button class="btn input-medium" id="btn_video_settings">视频编码配置</button>
  461 + <button class="btn input-medium" id="btn_audio_settings">音频编码配置</button>
  462 + <button class="btn input-medium btn-primary" id="btn_join">加入会议</button>
  463 + <input type="text" id="txt_url" class="input-mini hide" value=""></input>
  464 + </div>
  465 + </div>
  466 + <div class="container">
  467 + <div class="row-fluid">
  468 + <div class="span6">
  469 + <div class="accordion-group">
  470 + <div class="accordion-heading">
  471 + <span class="accordion-toggle">
  472 + <strong>[我的] 本地摄像头</strong>
  473 + </span>
  474 + </div>
  475 + <div class="accordion-body collapse in">
  476 + <div class="accordion-inner">
  477 + <div id="local_publisher"></div>
  478 + </div>
  479 + </div>
  480 + </div>
  481 + </div>
  482 + <div class="span6">
  483 + <div class="accordion-group">
  484 + <div class="accordion-heading">
  485 + <span class="accordion-toggle">
  486 + <strong>[我的] 远程服务器流</strong>
  487 + <a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
  488 + 播放地址<img src="img/tooltip.png"/>
  489 + </a>
  490 + </span>
  491 + </div>
  492 + <div class="accordion-body collapse in">
  493 + <div class="accordion-inner">
  494 + <div id="realtime_player"></div>
  495 + </div>
  496 + </div>
  497 + </div>
  498 + </div>
  499 + </div>
  500 + </div>
  501 + <div class="container hide" id="template">
  502 + <div class="accordion-group" id="collapse_main">
  503 + <div class="accordion-heading" title="点击展开或收起,收起后停止播放流,展开时从服务器请求流">
  504 + <span id="headerN" class="accordion-toggle" data-toggle="collapse" href="#collapseN">
  505 + <strong>[<a href="#"><span id="user_name">XX</span></a>]</strong>
  506 + <strong>加入时间</strong>[<span id="join_date"></span>]
  507 + <img src="img/tooltip.png"/>
  508 + </span>
  509 + </div>
  510 + <div id="collapseM" class="accordion-body collapse">
  511 + <div class="accordion-inner">
  512 + <div class="row-fluid">
  513 + <div class="span2">
  514 + </div>
  515 + <div class="span8">
  516 + <div id="chat_player">
  517 + <div id="chat_player_raw">
  518 + </div>
  519 + </div>
  520 + </div>
  521 + <div class="span2">
  522 + </div>
  523 + </div>
  524 + </div>
  525 + </div>
  526 + </div>
  527 + </div>
  528 + <div class="container" id="lst_chats">
  529 + </div>
  530 + <div id="video_modal" class="modal hide fade">
  531 + <div class="modal-header">
  532 + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
  533 + <h3>视频编码</h3>
  534 + </div>
  535 + <div class="modal-body">
  536 + <div class="form-horizontal">
  537 + <div class="control-group">
  538 + <label class="control-label" for="sl_cameras">
  539 + 摄像头
  540 + <a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  541 + <img src="img/tooltip.png"/>
  542 + </a>
  543 + </label>
  544 + <div class="controls">
  545 + <select class="span4" id="sl_cameras"></select>
  546 + </div>
  547 + </div>
  548 + <div class="control-group">
  549 + <label class="control-label" for="sl_vcodec">
  550 + Codec
  551 + <a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  552 + <img src="img/tooltip.png"/>
  553 + </a>
  554 + </label>
  555 + <div class="controls">
  556 + <select class="span2" id="sl_vcodec"></select>
  557 + </div>
  558 + </div>
  559 + <div class="control-group">
  560 + <label class="control-label" for="sl_profile">
  561 + Profile
  562 + <a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  563 + <img src="img/tooltip.png"/>
  564 + </a>
  565 + </label>
  566 + <div class="controls">
  567 + <select class="span2" id="sl_profile"></select>
  568 + </div>
  569 + </div>
  570 + <div class="control-group">
  571 + <label class="control-label" for="sl_level">
  572 + Level
  573 + <a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  574 + <img src="img/tooltip.png"/>
  575 + </a>
  576 + </label>
  577 + <div class="controls">
  578 + <select class="span2" id="sl_level"></select>
  579 + </div>
  580 + </div>
  581 + <div class="control-group">
  582 + <label class="control-label" for="sl_gop">
  583 + GOP
  584 + <a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  585 + <img src="img/tooltip.png"/>
  586 + </a>
  587 + </label>
  588 + <div class="controls">
  589 + <select class="span2" id="sl_gop"></select>
  590 + </div>
  591 + </div>
  592 + <div class="control-group">
  593 + <label class="control-label" for="sl_size">
  594 + 尺寸
  595 + <a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  596 + <img src="img/tooltip.png"/>
  597 + </a>
  598 + </label>
  599 + <div class="controls">
  600 + <select class="span2" id="sl_size"></select>
  601 + </div>
  602 + </div>
  603 + <div class="control-group">
  604 + <label class="control-label" for="sl_fps">
  605 + 帧率
  606 + <a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  607 + <img src="img/tooltip.png"/>
  608 + </a>
  609 + </label>
  610 + <div class="controls">
  611 + <select class="span2" id="sl_fps"></select>
  612 + </div>
  613 + </div>
  614 + <div class="control-group">
  615 + <label class="control-label" for="sl_bitrate">
  616 + 码率
  617 + <a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  618 + <img src="img/tooltip.png"/>
  619 + </a>
  620 + </label>
  621 + <div class="controls">
  622 + <select class="span2" id="sl_bitrate"></select>
  623 + </div>
  624 + </div>
  625 + </div>
  626 + </div>
  627 + <div class="modal-footer">
  628 + <button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
  629 + </div>
  630 + </div>
  631 + <div id="audio_modal" class="modal hide fade">
  632 + <div class="modal-header">
  633 + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
  634 + <h3>音频编码</h3>
  635 + </div>
  636 + <div class="modal-body">
  637 + <div class="form-horizontal">
  638 + <div class="control-group">
  639 + <label class="control-label" for="sl_microphones">
  640 + 麦克风
  641 + <a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
  642 + <img src="img/tooltip.png"/>
  643 + </a>
  644 + </label>
  645 + <div class="controls">
  646 + <select class="span4" id="sl_microphones"></select>
  647 + </div>
  648 + </div>
  649 + </div>
  650 + </div>
  651 + <div class="modal-footer">
  652 + <button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
  653 + </div>
  654 + </div>
  655 + <hr/>
  656 + <footer>
  657 + <p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team &copy; 2013</a></p>
  658 + </footer>
  659 +</div>
  660 +</body>
  661 +
@@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
92 srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height()); 92 srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
93 srs_player.on_player_ready = function() { 93 srs_player.on_player_ready = function() {
94 select_buffer_time("#btn_bt_0_8", 0.8); 94 select_buffer_time("#btn_bt_0_8", 0.8);
95 - srs_player.play(url); 95 + this.play(url);
96 }; 96 };
97 srs_player.on_player_metadata = function(metadata) { 97 srs_player.on_player_metadata = function(metadata) {
98 $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")"); 98 $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
@@ -100,7 +100,7 @@ @@ -100,7 +100,7 @@
100 select_fs_size("#btn_fs_size_screen_100", "screen", 100); 100 select_fs_size("#btn_fs_size_screen_100", "screen", 100);
101 }; 101 };
102 srs_player.on_player_timer = function(time, buffer_length) { 102 srs_player.on_player_timer = function(time, buffer_length) {
103 - var buffer = buffer_length / srs_player.buffer_time * 100; 103 + var buffer = buffer_length / this.buffer_time * 100;
104 $("#pb_buffer").width(Number(buffer).toFixed(1) + "%"); 104 $("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
105 105
106 $("#pb_buffer_bg").attr("title", 106 $("#pb_buffer_bg").attr("title",
@@ -269,6 +269,7 @@ @@ -269,6 +269,7 @@
269 <ul class="nav"> 269 <ul class="nav">
270 <li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 270 <li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
271 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 271 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  272 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
272 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 273 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
273 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 274 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
274 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 275 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
@@ -316,7 +317,7 @@ @@ -316,7 +317,7 @@
316 </div> 317 </div>
317 <div id="collapse10" class="accordion-body collapse"> 318 <div id="collapse10" class="accordion-body collapse">
318 <div class="accordion-inner"> 319 <div class="accordion-inner">
319 - <a href="#" id="srs_publish_hls">http://demo.srs.com/live/livestream.m3u8</a> <br/> 320 + <a href="#" id="srs_publish_hls" target="_blank">http://demo.srs.com/live/livestream.m3u8</a> <br/>
320 <span>对用户的流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span> 321 <span>对用户的流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
321 </div> 322 </div>
322 </div> 323 </div>
@@ -342,7 +343,7 @@ @@ -342,7 +343,7 @@
342 </div> 343 </div>
343 <div id="collapse11" class="accordion-body collapse"> 344 <div id="collapse11" class="accordion-body collapse">
344 <div class="accordion-inner"> 345 <div class="accordion-inner">
345 - <a href="#" id="srs_publish_ld_hls">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/> 346 + <a href="#" id="srs_publish_ld_hls" target="_blank">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/>
346 <span>对转码配置LD流进行HLS切片。</span> 347 <span>对转码配置LD流进行HLS切片。</span>
347 </div> 348 </div>
348 </div> 349 </div>
@@ -368,7 +369,7 @@ @@ -368,7 +369,7 @@
368 </div> 369 </div>
369 <div id="collapse12" class="accordion-body collapse"> 370 <div id="collapse12" class="accordion-body collapse">
370 <div class="accordion-inner"> 371 <div class="accordion-inner">
371 - <a href="#" id="srs_publish_sd_hls">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/> 372 + <a href="#" id="srs_publish_sd_hls" target="_blank">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/>
372 <span>对转码配置SD流进行HLS切片。</span> 373 <span>对转码配置SD流进行HLS切片。</span>
373 </div> 374 </div>
374 </div> 375 </div>
@@ -394,7 +395,7 @@ @@ -394,7 +395,7 @@
394 </div> 395 </div>
395 <div id="collapse13" class="accordion-body collapse"> 396 <div id="collapse13" class="accordion-body collapse">
396 <div class="accordion-inner"> 397 <div class="accordion-inner">
397 - <a href="#" id="srs_publish_fw_hls">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/> 398 + <a href="#" id="srs_publish_fw_hls" target="_blank">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/>
398 <span>对转发原始流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span> 399 <span>对转发原始流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
399 </div> 400 </div>
400 </div> 401 </div>
@@ -420,7 +421,7 @@ @@ -420,7 +421,7 @@
420 </div> 421 </div>
421 <div id="collapse14" class="accordion-body collapse"> 422 <div id="collapse14" class="accordion-body collapse">
422 <div class="accordion-inner"> 423 <div class="accordion-inner">
423 - <a href="#" id="srs_publish_fw_ld_hls">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/> 424 + <a href="#" id="srs_publish_fw_ld_hls" target="_blank">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/>
424 <span>对转发转码配置LD流进行HLS切片,所有转发的流会自动支持HLS。</span> 425 <span>对转发转码配置LD流进行HLS切片,所有转发的流会自动支持HLS。</span>
425 </div> 426 </div>
426 </div> 427 </div>
@@ -446,7 +447,7 @@ @@ -446,7 +447,7 @@
446 </div> 447 </div>
447 <div id="collapse15" class="accordion-body collapse"> 448 <div id="collapse15" class="accordion-body collapse">
448 <div class="accordion-inner"> 449 <div class="accordion-inner">
449 - <a href="#" id="srs_publish_fw_sd_hls">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/> 450 + <a href="#" id="srs_publish_fw_sd_hls" target="_blank">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/>
450 <span>对转发转码配置SD流进行HLS切片,所有转发的流会自动支持HLS。</span> 451 <span>对转发转码配置SD流进行HLS切片,所有转发的流会自动支持HLS。</span>
451 </div> 452 </div>
452 </div> 453 </div>
@@ -37,6 +37,12 @@ @@ -37,6 +37,12 @@
37 $("#low_latecy_tips").tooltip({ 37 $("#low_latecy_tips").tooltip({
38 title: "服务器不转码直接转发FLASH编码器的流,所以延迟比支持HLS的流要低很多" 38 title: "服务器不转码直接转发FLASH编码器的流,所以延迟比支持HLS的流要低很多"
39 }); 39 });
  40 + $("#realtime_player_url").tooltip({
  41 + title: "右键复制RTMP地址"
  42 + });
  43 + $("#remote_player_url").tooltip({
  44 + title: "右键复制RTMP地址"
  45 + });
40 46
41 $("#btn_publish").click(on_user_publish); 47 $("#btn_publish").click(on_user_publish);
42 48
@@ -46,74 +52,12 @@ @@ -46,74 +52,12 @@
46 // start the publisher. 52 // start the publisher.
47 srs_publisher = new SrsPublisher("local_publisher", 430, 185); 53 srs_publisher = new SrsPublisher("local_publisher", 430, 185);
48 srs_publisher.on_publisher_ready = function(cameras, microphones) { 54 srs_publisher.on_publisher_ready = function(cameras, microphones) {
49 - $("#sl_cameras").empty();  
50 - for (var i = 0; i < cameras.length; i++) {  
51 - $("#sl_cameras").append("<option value='" + i + "'>" + cameras[i] + "</option");  
52 - }  
53 - // optional: select the first no "virtual" signed.  
54 - for (var i = 0; i < cameras.length; i++) {  
55 - if (cameras[i].toLowerCase().indexOf("virtual") == -1) {  
56 - $("#sl_cameras option[value='" + i + "']").attr("selected", true);  
57 - break;  
58 - }  
59 - }  
60 -  
61 - $("#sl_microphones").empty();  
62 - for (var i = 0; i < microphones.length; i++) {  
63 - $("#sl_microphones").append("<option value='" + i + "'>" + microphones[i] + "</option");  
64 - }  
65 -  
66 - $("#sl_vcodec").empty();  
67 - var vcodecs = ["h264", "vp6"];  
68 - for (var i = 0; i < vcodecs.length; i++) {  
69 - $("#sl_vcodec").append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");  
70 - }  
71 -  
72 - $("#sl_profile").empty();  
73 - var profiles = ["baseline", "main"];  
74 - for (var i = 0; i < profiles.length; i++) {  
75 - $("#sl_profile").append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");  
76 - }  
77 -  
78 - $("#sl_level").empty();  
79 - var levels = ["1", "1b", "1.1", "1.2", "1.3",  
80 - "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];  
81 - for (var i = 0; i < levels.length; i++) {  
82 - $("#sl_level").append("<option value='" + levels[i] + "'>" + levels[i] + "</option");  
83 - }  
84 - $("#sl_level option[value='4.1']").attr("selected", true);  
85 -  
86 - $("#sl_gop").empty();  
87 - var gops = ["0.3", "0.5", "1", "2", "3", "4",  
88 - "5", "6", "7", "8", "9", "10", "15", "20"];  
89 - for (var i = 0; i < gops.length; i++) {  
90 - $("#sl_gop").append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");  
91 - }  
92 - $("#sl_gop option[value='5']").attr("selected", true);  
93 -  
94 - $("#sl_size").empty();  
95 - var sizes = ["176x144", "320x240", "352x240",  
96 - "352x288", "460x240", "640x480", "720x480", "720x576", "800x600",  
97 - "1024x768", "1280x720", "1360x768", "1920x1080"];  
98 - for (i = 0; i < sizes.length; i++) {  
99 - $("#sl_size").append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");  
100 - }  
101 - $("#sl_size option[value='460x240']").attr("selected", true);  
102 -  
103 - $("#sl_fps").empty();  
104 - var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];  
105 - for (i = 0; i < fpses.length; i++) {  
106 - $("#sl_fps").append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");  
107 - }  
108 - $("#sl_fps option[value='15']").attr("selected", true);  
109 -  
110 - $("#sl_bitrate").empty();  
111 - var bitrates = ["50", "200", "350", "500", "650", "800",  
112 - "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];  
113 - for (i = 0; i < bitrates.length; i++) {  
114 - $("#sl_bitrate").append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");  
115 - }  
116 - $("#sl_bitrate option[value='350']").attr("selected", true); 55 + srs_publisher_initialize_page(
  56 + cameras, microphones,
  57 + "#sl_cameras", "#sl_microphones",
  58 + "#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
  59 + "#sl_fps", "#sl_bitrate"
  60 + );
117 }; 61 };
118 srs_publisher.on_publisher_error = function(code, desc) { 62 srs_publisher.on_publisher_error = function(code, desc) {
119 error(code, desc); 63 error(code, desc);
@@ -123,22 +67,24 @@ @@ -123,22 +67,24 @@
123 }; 67 };
124 srs_publisher.start(); 68 srs_publisher.start();
125 69
  70 + update_play_url();
  71 +
126 // if no play specified, donot show the player, for debug the publisher. 72 // if no play specified, donot show the player, for debug the publisher.
127 var query = parse_query_string(); 73 var query = parse_query_string();
128 if (query.no_play != "true") { 74 if (query.no_play != "true") {
129 // start the normal player with HLS supported. 75 // start the normal player with HLS supported.
130 remote_player = new SrsPlayer("remote_player", 430, 185); 76 remote_player = new SrsPlayer("remote_player", 430, 185);
131 remote_player.on_player_ready = function() { 77 remote_player.on_player_ready = function() {
132 - remote_player.set_bt(0.8);  
133 - remote_player.set_fs("screen", 100); 78 + this.set_bt(0.8);
  79 + this.set_fs("screen", 100);
134 }; 80 };
135 remote_player.start(); 81 remote_player.start();
136 82
137 // start the realtime player. 83 // start the realtime player.
138 realtime_player = new SrsPlayer("realtime_player", 430, 185); 84 realtime_player = new SrsPlayer("realtime_player", 430, 185);
139 realtime_player.on_player_ready = function() { 85 realtime_player.on_player_ready = function() {
140 - realtime_player.set_bt(0.8);  
141 - realtime_player.set_fs("screen", 100); 86 + this.set_bt(0.8);
  87 + this.set_fs("screen", 100);
142 }; 88 };
143 realtime_player.start(); 89 realtime_player.start();
144 } 90 }
@@ -157,6 +103,10 @@ @@ -157,6 +103,10 @@
157 var ret = srs_parse_rtmp_url(url); 103 var ret = srs_parse_rtmp_url(url);
158 var query = parse_query_string(); 104 var query = parse_query_string();
159 105
  106 + var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "...vhost..." + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream;
  107 + $("#realtime_player_url").attr("href", url).attr("target", "_blank");
  108 + $("#remote_player_url").attr("href", remote_url).attr("target", "_blank");
  109 +
160 var srs_player_url = "http://" + query.host + query.dir + "/srs_player.html?"; 110 var srs_player_url = "http://" + query.host + query.dir + "/srs_player.html?";
161 srs_player_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream; 111 srs_player_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
162 srs_player_url += "&autostart=true"; 112 srs_player_url += "&autostart=true";
@@ -180,10 +130,12 @@ @@ -180,10 +130,12 @@
180 if ($("#btn_publish").text() == "停止发布") { 130 if ($("#btn_publish").text() == "停止发布") {
181 srs_publisher.stop(); 131 srs_publisher.stop();
182 $("#btn_publish").text("发布视频"); 132 $("#btn_publish").text("发布视频");
183 - $("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");  
184 - $("#txt_play_url").text("RTMP已转码(请发布视频)").attr("href", "#").attr("target", "_self");  
185 - $("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self");  
186 - $("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self"); 133 + //$("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");
  134 + //$("#txt_play_realtime").attr("href", "#").attr("target", "_self");
  135 + //$("#txt_play_url").text("RTMP已转码(请发布视频)").attr("href", "#").attr("target", "_self");
  136 + //$("#remote_player_url").attr("href", "#").attr("target", "_self");
  137 + //$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self");
  138 + //$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self");
187 return; 139 return;
188 } 140 }
189 141
@@ -194,20 +146,12 @@ @@ -194,20 +146,12 @@
194 var url = $("#txt_url").val(); 146 var url = $("#txt_url").val();
195 var vcodec = {}; 147 var vcodec = {};
196 var acodec = {}; 148 var acodec = {};
197 -  
198 - acodec.device_code = $("#sl_microphones").val();  
199 - acodec.device_name = $("#sl_microphones").text();  
200 -  
201 - vcodec.device_code = $("#sl_cameras").find("option:selected").val();  
202 - vcodec.device_name = $("#sl_cameras").find("option:selected").text();  
203 -  
204 - vcodec.codec = $("#sl_vcodec").find("option:selected").val();  
205 - vcodec.profile = $("#sl_profile").find("option:selected").val();  
206 - vcodec.level = $("#sl_level").find("option:selected").val();  
207 - vcodec.fps = $("#sl_fps").find("option:selected").val();  
208 - vcodec.gop = $("#sl_gop").find("option:selected").val();  
209 - vcodec.size = $("#sl_size").find("option:selected").val();  
210 - vcodec.bitrate = $("#sl_bitrate").find("option:selected").val(); 149 + srs_publiser_get_codec(
  150 + vcodec, acodec,
  151 + "#sl_cameras", "#sl_microphones",
  152 + "#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
  153 + "#sl_fps", "#sl_bitrate"
  154 + );
211 155
212 info("开始推流到服务器"); 156 info("开始推流到服务器");
213 srs_publisher.publish(url, vcodec, acodec); 157 srs_publisher.publish(url, vcodec, acodec);
@@ -230,22 +174,6 @@ @@ -230,22 +174,6 @@
230 remote_player.play(pub_url); 174 remote_player.play(pub_url);
231 } 175 }
232 } 176 }
233 -  
234 - function info(desc) {  
235 - $("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");  
236 - $("#txt_log_title").text("Info:");  
237 - $("#txt_log_msg").text(desc);  
238 - }  
239 - function warn(code, desc) {  
240 - $("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");  
241 - $("#txt_log_title").text("Warn:");  
242 - $("#txt_log_msg").text("code: " + code + ", " + desc);  
243 - }  
244 - function error(code, desc) {  
245 - $("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");  
246 - $("#txt_log_title").text("Error:");  
247 - $("#txt_log_msg").text("code: " + code + ", " + desc);  
248 - }  
249 </script> 177 </script>
250 </head> 178 </head>
251 <body> 179 <body>
@@ -257,6 +185,7 @@ @@ -257,6 +185,7 @@
257 <ul class="nav"> 185 <ul class="nav">
258 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 186 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
259 <li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 187 <li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  188 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
260 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 189 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
261 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 190 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
262 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 191 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
@@ -424,11 +353,11 @@ @@ -424,11 +353,11 @@
424 <div class="span6"> 353 <div class="span6">
425 <div class="accordion-group"> 354 <div class="accordion-group">
426 <div class="accordion-heading"> 355 <div class="accordion-heading">
427 - <span class="accordion-toggle" data-toggle="collapse" href="#collapse1"> 356 + <span class="accordion-toggle">
428 <strong>本地摄像头</strong> 357 <strong>本地摄像头</strong>
429 </span> 358 </span>
430 </div> 359 </div>
431 - <div id="collapse1" class="accordion-body collapse in"> 360 + <div class="accordion-body collapse in">
432 <div class="accordion-inner"> 361 <div class="accordion-inner">
433 <div id="local_publisher"></div> 362 <div id="local_publisher"></div>
434 </div> 363 </div>
@@ -438,14 +367,17 @@ @@ -438,14 +367,17 @@
438 <div class="span6"> 367 <div class="span6">
439 <div class="accordion-group"> 368 <div class="accordion-group">
440 <div class="accordion-heading"> 369 <div class="accordion-heading">
441 - <span class="accordion-toggle" data-toggle="collapse" href="#collapse2"> 370 + <span class="accordion-toggle">
442 <strong>远程服务器</strong> 371 <strong>远程服务器</strong>
443 <a id="remote_tips" href="#" data-toggle="tooltip" data-placement="top" title=""> 372 <a id="remote_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
444 黑屏<img src="img/tooltip.png"/> 373 黑屏<img src="img/tooltip.png"/>
445 </a> 374 </a>
  375 + <a id="remote_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
  376 + 播放地址<img src="img/tooltip.png"/>
  377 + </a>
446 </span> 378 </span>
447 </div> 379 </div>
448 - <div id="collapse2" class="accordion-body collapse in"> 380 + <div class="accordion-body collapse in">
449 <div class="accordion-inner"> 381 <div class="accordion-inner">
450 <div id="remote_player"></div> 382 <div id="remote_player"></div>
451 </div> 383 </div>
@@ -459,14 +391,17 @@ @@ -459,14 +391,17 @@
459 <div class="span6"> 391 <div class="span6">
460 <div class="accordion-group"> 392 <div class="accordion-group">
461 <div class="accordion-heading"> 393 <div class="accordion-heading">
462 - <span class="accordion-toggle" data-toggle="collapse" href="#collapse3"> 394 + <span class="accordion-toggle">
463 <strong>远程服务器</strong> 395 <strong>远程服务器</strong>
464 <a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title=""> 396 <a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
465 低延时<img src="img/tooltip.png"/> 397 低延时<img src="img/tooltip.png"/>
466 </a> 398 </a>
  399 + <a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
  400 + 播放地址<img src="img/tooltip.png"/>
  401 + </a>
467 </span> 402 </span>
468 </div> 403 </div>
469 - <div id="collapse3" class="accordion-body collapse in"> 404 + <div class="accordion-body collapse in">
470 <div class="accordion-inner"> 405 <div class="accordion-inner">
471 <div id="realtime_player"></div> 406 <div id="realtime_player"></div>
472 </div> 407 </div>
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 <ul class="nav"> 29 <ul class="nav">
30 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li> 30 <li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
31 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li> 31 <li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
  32 + <li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
32 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li> 33 <li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
33 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li> 34 <li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
34 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li> 35 <li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
@@ -39,7 +40,7 @@ @@ -39,7 +40,7 @@
39 </div> 40 </div>
40 </div> 41 </div>
41 <div class="container"> 42 <div class="container">
42 - <iframe id="main_frame" width="100%" height="800" frameBorder="0"></iframe> 43 + <iframe id="main_frame" width="100%" height="600" frameBorder="0"></iframe>
43 </div> 44 </div>
44 <div class="container"> 45 <div class="container">
45 <hr> 46 <hr>
  1 +#!/bin/bash
  2 +for((;;)); do \
  3 + ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
  4 + -vcodec copy -acodec copy \
  5 + -f flv -y rtmp://127.0.0.1/live?vhost=demo.srs.com/livestream; \
  6 + sleep 1; \
  7 +done
  1 +#!/bin/bash
  2 +for((;;)); do \
  3 + ./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
  4 + -vcodec copy -acodec copy \
  5 + -f flv -y rtmp://127.0.0.1/live?vhost=players/livestream; \
  6 + sleep 1; \
  7 +done
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +echo "编译SRS"
  6 +./configure --with-ssl --with-hls --with-ffmpeg --with-http && make
  7 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:编译SRS失败"; exit $ret; fi
  8 +
  9 +echo "编译SRS成功"
  10 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="nohup python ./research/api-server/server.py 8085 >./objs/logs/api-server.log 2>&1 &"
  6 +echo "启动API服务器:$cmd"
  7 +pids=`ps aux|grep python|grep research|grep "api-server"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  8 +nohup python ./research/api-server/server.py 8085 >./objs/logs/api-server.log 2>&1 &
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动API服务器失败"; exit $ret; fi
  10 +
  11 +echo "启动API服务器成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="nohup bash ./scripts/_ffmpeg.demo.sh >./objs/logs/ffmpeg-demo.log 2>&1 &"
  6 +echo "启动FFMPEG推送demo流(播放器上12路演示):$cmd"
  7 +pids=`ps aux|grep scripts|grep "/_ffmpeg.demo.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  8 +nohup bash ./scripts/_ffmpeg.demo.sh >./objs/logs/ffmpeg-demo.log 2>&1 &
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动FFMPEG推送demo流(播放器上12路演示)失败"; exit $ret; fi
  10 +
  11 +echo "启动FFMPEG推送demo流(播放器上12路演示)成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="nohup bash ./scripts/_ffmpeg.players.sh >./objs/logs/ffmpeg-players.log 2>&1 &"
  6 +echo "启动FFMPEG推送players流(播放器上演示用):$cmd"
  7 +pids=`ps aux|grep scripts|grep "/_ffmpeg.players.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  8 +nohup bash ./scripts/_ffmpeg.players.sh >./objs/logs/ffmpeg-players.log 2>&1 &
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动FFMPEG推送players流(播放器上演示用)失败"; exit $ret; fi
  10 +
  11 +echo "启动FFMPEG推送players流(播放器上演示用)成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="sudo ./objs/nginx/sbin/nginx"
  6 +echo "启动NGINX(HLS服务):$cmd"
  7 +pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done
  8 +sudo ./objs/nginx/sbin/nginx
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动NGINX(HLS服务)失败"; exit $ret; fi
  10 +
  11 +echo "启动NGINX(HLS服务)成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="nohup ./objs/srs -c conf/srs.19350.conf > ./objs/logs/srs.19350.log 2>&1 &"
  6 +echo "启动SRS转发服务器:$cmd"
  7 +pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  8 +nohup ./objs/srs -c conf/srs.19350.conf > ./objs/logs/srs.19350.log 2>&1 &
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动SRS转发服务器失败"; exit $ret; fi
  10 +
  11 +echo "启动SRS转发服务器成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +cmd="nohup ./objs/srs -c conf/srs.conf >./objs/logs/srs.log 2>&1 &"
  6 +echo "启动SRS服务器:$cmd"
  7 +pids=`ps aux|grep srs|grep "./objs"|grep "srs.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  8 +nohup ./objs/srs -c conf/srs.conf >./objs/logs/srs.log 2>&1 &
  9 +ret=$?; if [[ 0 -ne $ret ]]; then echo "错误:启动SRS失败"; exit $ret; fi
  10 +
  11 +echo "启动SRS服务器成功"
  12 +exit 0
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +# step 1: build srs
  6 +bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  7 +
  8 +echo "编译SRS成功"
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +# linux shell color support.
  6 +RED="\\e[31m"
  7 +GREEN="\\e[32m"
  8 +YELLOW="\\e[33m"
  9 +BLACK="\\e[0m"
  10 +
  11 +# step 1: build srs
  12 +#bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  13 +
  14 +# step 2: start srs
  15 +bash scripts/_step.start.srs.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  16 +
  17 +# step 3(optinal): start srs listen at 19350 to forward to
  18 +#bash scripts/_step.start.srs.19350.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  19 +
  20 +# step 4(optinal): start nginx for HLS
  21 +bash scripts/_step.start.nginx.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  22 +
  23 +# step 5(optinal): start http hooks for srs callback
  24 +bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  25 +
  26 +# step 6: publish demo live stream
  27 +#bash scripts/_step.start.ffmpeg.demo.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  28 +
  29 +# step 7: publish players live stream
  30 +#bash scripts/_step.start.ffmpeg.players.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  31 +
  32 +# step 8: add server ip to client hosts as demo.
  33 +ip=`ifconfig|grep "inet"|grep "addr"|grep "Mask"|grep -v "127.0.0.1"|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'`
  34 +echo -e "${GREEN}SRS系统开发环境启动成功${BLACK}"
  35 +echo -e "${BLACK}播放器演示:${BLACK}"
  36 +echo -e "${RED} http://$ip/players/srs_player.html?vhost=players${BLACK}"
  37 +echo -e "${BLACK}编码器演示:${BLACK}"
  38 +echo -e "${RED} http://$ip/players/srs_publisher.html?vhost=players${BLACK}"
  39 +echo -e "${BLACK}视频会议演示:${BLACK}"
  40 +echo -e "${RED} http://$ip/players/srs_chat.html?vhost=players${BLACK}"
  41 +echo -e "${BLACK}服务器测速演示:${BLACK}"
  42 +echo -e "${RED} http://$ip/players/srs_bwt.html?vhost=players${BLACK}"
  1 +#!/bin/bash
  2 +src_dir='src'
  3 +if [[ ! -d $src_dir ]]; then echo "错误:必须在src同目录执行脚本"; exit 1; fi
  4 +
  5 +# linux shell color support.
  6 +RED="\\e[31m"
  7 +GREEN="\\e[32m"
  8 +YELLOW="\\e[33m"
  9 +BLACK="\\e[0m"
  10 +
  11 +# step 1: build srs
  12 +#bash scripts/_step.build.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  13 +
  14 +# step 2: start srs
  15 +bash scripts/_step.start.srs.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  16 +
  17 +# step 3(optinal): start srs listen at 19350 to forward to
  18 +bash scripts/_step.start.srs.19350.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  19 +
  20 +# step 4(optinal): start nginx for HLS
  21 +bash scripts/_step.start.nginx.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  22 +
  23 +# step 5(optinal): start http hooks for srs callback
  24 +bash scripts/_step.start.api.server.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  25 +
  26 +# step 6: publish demo live stream
  27 +bash scripts/_step.start.ffmpeg.demo.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  28 +
  29 +# step 7: publish players live stream
  30 +bash scripts/_step.start.ffmpeg.players.sh; ret=$?; if [[ 0 -ne $ret ]]; then exit $ret; fi
  31 +
  32 +# step 8: add server ip to client hosts as demo.
  33 +ip=`ifconfig|grep "inet"|grep "addr"|grep "Mask"|grep -v "127.0.0.1"|awk 'NR==1 {print $2}'|awk -F ':' '{print $2}'`
  34 +cat<<END
  35 +默认的12路流演示:
  36 + http://$ip/players
  37 +默认的播放器流演示:
  38 + http://$ip/players/srs_player.html?vhost=players
  39 +推流(主播)应用演示:
  40 + http://$ip/players/srs_publisher.html?vhost=players
  41 +视频会议(聊天室)应用演示:
  42 + http://$ip/players/srs_chat.html?vhost=players
  43 +END
  44 +echo -e "${GREEN}演示地址:${BLACK}"
  45 +echo -e "${RED} http://$ip${BLACK}"
  1 +#!/bin/bash
  2 +
  3 +echo "停止SRS服务器"
  4 +ps aux|grep srs|grep "./objs"|grep "srs.conf"
  5 +pids=`ps aux|grep srs|grep "./objs"|grep "srs.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  6 +
  7 +echo "停止SRS转发服务器"
  8 +ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"
  9 +pids=`ps aux|grep srs|grep "./objs"|grep "srs.19350.conf"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  10 +
  11 +# step 4(optinal): start nginx for HLS
  12 +echo "停止NGINX(HLS服务)"
  13 +ps aux|grep nginx|grep process
  14 +pids=`ps aux|grep nginx|grep process|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; sudo kill -s SIGKILL $pid; done
  15 +
  16 +# step 5(optinal): start http hooks for srs callback
  17 +echo "停止API服务器"
  18 +ps aux|grep python|grep research|grep "api-server"
  19 +pids=`ps aux|grep python|grep research|grep "api-server"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  20 +
  21 +# step 6: publish demo live stream
  22 +echo "停止FFMPEG推送demo流(播放器上12路演示)"
  23 +ps aux|grep scripts|grep "ffmpeg.demo.sh"
  24 +pids=`ps aux|grep scripts|grep "/ffmpeg.demo.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  25 +
  26 +# step 7: publish players live stream
  27 +echo "停止FFMPEG推送players流(播放器上演示用)"
  28 +ps aux|grep scripts|grep "ffmpeg.players.sh"
  29 +pids=`ps aux|grep scripts|grep "/ffmpeg.players.sh"|awk '{print $2}'`; for pid in $pids; do echo "结束现有进程:$pid"; kill -s SIGKILL $pid; done
  30 +
  31 +echo "SRS系统服务均已停止"
@@ -79,7 +79,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -79,7 +79,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
79 #define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" 79 #define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin"
80 #define RTMP_SIG_SRS_EMAIL "winterserver@126.com" 80 #define RTMP_SIG_SRS_EMAIL "winterserver@126.com"
81 #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" 81 #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)"
82 -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin,wenjiegit" 82 +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin"
83 #define RTMP_SIG_SRS_CONTRIBUTOR "winlin,wenjiegit" 83 #define RTMP_SIG_SRS_CONTRIBUTOR "winlin,wenjiegit"
84 84
85 // compare 85 // compare
@@ -108,4 +108,15 @@ extern void srs_vhost_resolve(std::string& vhost, std::string& app); @@ -108,4 +108,15 @@ extern void srs_vhost_resolve(std::string& vhost, std::string& app);
108 // close the netfd, and close the underlayer fd. 108 // close the netfd, and close the underlayer fd.
109 extern void srs_close_stfd(st_netfd_t& stfd); 109 extern void srs_close_stfd(st_netfd_t& stfd);
110 110
  111 +/**
  112 +* disable copy constructor of class
  113 +*/
  114 +#define disable_default_copy(className)\
  115 + private:\
  116 + /** \
  117 + * disable the copy constructor and operator=, donot allow directly copy. \
  118 + */ \
  119 + className(const className&); \
  120 + className& operator= (const className&)
  121 +
111 #endif 122 #endif
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013 wenjiegit
  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_core_bandwidth.hpp>
  25 +
  26 +#include <arpa/inet.h>
  27 +
  28 +using namespace std;
  29 +
  30 +#include <srs_core_rtmp.hpp>
  31 +#include <srs_core_error.hpp>
  32 +#include <srs_core_amf0.hpp>
  33 +#include <srs_core_protocol.hpp>
  34 +#include <srs_core_config.hpp>
  35 +#include <srs_core_autofree.hpp>
  36 +
  37 +SrsBandwidth::SrsBandwidth()
  38 +{
  39 +}
  40 +
  41 +SrsBandwidth::~SrsBandwidth()
  42 +{
  43 +}
  44 +
  45 +int SrsBandwidth::bandwidth_test(SrsRequest* _req, st_netfd_t stfd, SrsRtmp* _rtmp)
  46 +{
  47 + int ret = ERROR_SUCCESS;
  48 +
  49 + rtmp = _rtmp;
  50 + req = _req;
  51 +
  52 + if (!config->get_bw_check_enabled(req->vhost)) {
  53 + return ret;
  54 + }
  55 +
  56 + // validate the bandwidth check key
  57 + std::string key = "key=" + config->get_bw_check_key(req->vhost);
  58 + if (req->tcUrl.find(key) == std::string::npos) {
  59 + ret = ERROR_SYSTEM_BANDWIDTH_KEY;
  60 + srs_error("check the vhost=%s %s failed, tcUrl=%s, ret=%d",
  61 + req->vhost.c_str(), key.c_str(), req->tcUrl.c_str(), ret);
  62 + return ret;
  63 + }
  64 +
  65 + // shared global last check time,
  66 + // to avoid attach by bandwidth check,
  67 + // if client request check in the window(specifeid by interval),
  68 + // directly reject the request.
  69 + static int64_t last_check_time = 0;
  70 + int interval_ms = config->get_bw_check_interval_ms(req->vhost);
  71 +
  72 + int64_t time_now = srs_get_system_time_ms();
  73 + // reject the connection in the interval window.
  74 + if (last_check_time > 0 && time_now - last_check_time < interval_ms) {
  75 + ret = ERROR_SYSTEM_BANDWIDTH_DENIED;
  76 + srs_trace("bandcheck denied, "
  77 + "last_check=%"PRId64", now=%"PRId64", interval=%d",
  78 + last_check_time, time_now, interval_ms);
  79 +
  80 + rtmp->response_connect_reject(req, "bandcheck rejected");
  81 + return ret;
  82 + }
  83 +
  84 + // accept and do bandwidth check.
  85 + last_check_time = time_now;
  86 +
  87 + char* local_ip = 0;
  88 + if ((ret = get_local_ip(stfd, local_ip)) != ERROR_SUCCESS) {
  89 + srs_error("get local ip failed. ret = %d", ret);
  90 + return ret;
  91 + }
  92 +
  93 + if ((ret = rtmp->response_connect_app(req, local_ip)) != ERROR_SUCCESS) {
  94 + srs_error("response connect app failed. ret=%d", ret);
  95 + return ret;
  96 + }
  97 +
  98 + return do_bandwidth_check();
  99 +}
  100 +
  101 +int SrsBandwidth::get_local_ip(st_netfd_t stfd, char *&local_ip)
  102 +{
  103 + int ret = ERROR_SUCCESS;
  104 +
  105 + int fd = st_netfd_fileno(stfd);
  106 +
  107 + // discovery client information
  108 + sockaddr_in addr;
  109 + socklen_t addrlen = sizeof(addr);
  110 + if (getsockname(fd, (sockaddr*)&addr, &addrlen) == -1) {
  111 + ret = ERROR_SOCKET_GET_LOCAL_IP;
  112 + srs_error("discovery local ip information failed. ret=%d", ret);
  113 + return ret;
  114 + }
  115 + srs_verbose("get local ip success.");
  116 +
  117 + // ip v4 or v6
  118 + char buf[INET6_ADDRSTRLEN];
  119 + memset(buf, 0, sizeof(buf));
  120 +
  121 + if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) {
  122 + ret = ERROR_SOCKET_GET_LOCAL_IP;
  123 + srs_error("convert local ip information failed. ret=%d", ret);
  124 + return ret;
  125 + }
  126 +
  127 + local_ip = new char[strlen(buf) + 1];
  128 + strcpy(local_ip, buf);
  129 +
  130 + srs_verbose("get local ip of client ip=%s, fd=%d", buf, fd);
  131 +
  132 + return ret;
  133 +}
  134 +
  135 +int SrsBandwidth::do_bandwidth_check()
  136 +{
  137 + int ret = ERROR_SUCCESS;
  138 +
  139 + SrsProtocol* protocol = rtmp->get_protocol();
  140 +
  141 + int play_duration_ms = 3000;
  142 + int play_interval_ms = 0;
  143 + int play_actual_duration_ms = 0;
  144 + int play_bytes = 0;
  145 +
  146 + int publish_duration_ms = 3000;
  147 + int publish_interval_ms = 0;
  148 + int publish_actual_duration_ms = 0;
  149 + int publish_bytes = 0;
  150 +
  151 + int limit_kbps = config->get_bw_check_limit_kbps(req->vhost);
  152 +
  153 + int64_t start_time = srs_get_system_time_ms();
  154 +
  155 + ret = check_play(play_duration_ms,
  156 + play_interval_ms, play_actual_duration_ms, play_bytes, limit_kbps);
  157 + if (ret != ERROR_SUCCESS) {
  158 + srs_error("band width play check failed. ret=%d", ret);
  159 + return ret;
  160 + }
  161 +
  162 + ret = check_publish(publish_duration_ms,
  163 + publish_interval_ms, publish_actual_duration_ms, publish_bytes, limit_kbps);
  164 + if (ret != ERROR_SUCCESS) {
  165 + srs_error("band width publish check failed. ret=%d", ret);
  166 + return ret;
  167 + }
  168 +
  169 + int64_t end_time = srs_get_system_time_ms();
  170 + int play_kbps = play_bytes * 8 / play_actual_duration_ms;
  171 + int publish_kbps = publish_bytes * 8 / publish_actual_duration_ms;
  172 +
  173 + srs_trace("bandwidth check finished. start=%"PRId64"ms, end=%"PRId64"ms, "
  174 + "duartion=%dms, play=%dkbps, publish=%dkbps, tcUrl=%s, ret=%#x",
  175 + start_time, end_time, (int)(end_time - start_time), play_kbps, publish_kbps,
  176 + req->tcUrl.c_str(), ret);
  177 +
  178 + // send finished msg
  179 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_finish();
  180 + pkt->data->set("code", new SrsAmf0Number(ERROR_SUCCESS));
  181 + pkt->data->set("start_time", new SrsAmf0Number(start_time));
  182 + pkt->data->set("end_time", new SrsAmf0Number(end_time));
  183 + pkt->data->set("play_kbps", new SrsAmf0Number(play_kbps));
  184 + pkt->data->set("publish_kbps", new SrsAmf0Number(publish_kbps));
  185 + pkt->data->set("play_bytes", new SrsAmf0Number(play_bytes));
  186 + pkt->data->set("play_time", new SrsAmf0Number(play_actual_duration_ms));
  187 + pkt->data->set("publish_bytes", new SrsAmf0Number(publish_bytes));
  188 + pkt->data->set("publish_time", new SrsAmf0Number(publish_actual_duration_ms));
  189 +
  190 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  191 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  192 + srs_error("send bandwidth check finish message failed. ret=%d", ret);
  193 + return ret;
  194 + }
  195 +
  196 + // if flash, we notice the result, and expect a final packet.
  197 + while (true) {
  198 + SrsCommonMessage* msg = NULL;
  199 + SrsBandwidthPacket* pkt = NULL;
  200 + if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
  201 + // info level to ignore and return success.
  202 + srs_info("expect final message failed. ret=%d", ret);
  203 + return ERROR_SUCCESS;
  204 + }
  205 + SrsAutoFree(SrsCommonMessage, msg, false);
  206 + srs_info("get final message success.");
  207 +
  208 + if (pkt->is_flash_final()) {
  209 + srs_info("BW check recv flash final response.");
  210 + break;
  211 + }
  212 + }
  213 +
  214 + srs_info("BW check finished.");
  215 +
  216 + return ret;
  217 +}
  218 +
  219 +int SrsBandwidth::check_play(
  220 + int duration_ms, int interval_ms, int& actual_duration_ms,
  221 + int& play_bytes, int max_play_kbps)
  222 +{
  223 + int ret = ERROR_SUCCESS;
  224 +
  225 + SrsProtocol* protocol = rtmp->get_protocol();
  226 +
  227 + if (true) {
  228 + // send start play command to client
  229 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_play();
  230 +
  231 + pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
  232 + pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
  233 +
  234 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  235 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  236 + srs_error("send bandwidth check start play message failed. ret=%d", ret);
  237 + return ret;
  238 + }
  239 + srs_info("BW check begin.");
  240 + }
  241 +
  242 + while (true) {
  243 + // recv client's starting play response
  244 + SrsCommonMessage* msg = NULL;
  245 + SrsBandwidthPacket* pkt = NULL;
  246 + if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
  247 + srs_error("expect bandwidth message failed. ret=%d", ret);
  248 + return ret;
  249 + }
  250 + SrsAutoFree(SrsCommonMessage, msg, false);
  251 + srs_info("get bandwidth message succes.");
  252 +
  253 + if (pkt->is_starting_play()) {
  254 + srs_info("BW check recv play begin response.");
  255 + break;
  256 + }
  257 + }
  258 +
  259 + // send play data to client
  260 + int64_t current_time = srs_get_system_time_ms();
  261 + int size = 1024; // TODO: FIXME: magic number
  262 + char random_data[size];
  263 + memset(random_data, 0x01, size);
  264 +
  265 + int interval = 0;
  266 + while ( (srs_get_system_time_ms() - current_time) < duration_ms ) {
  267 + st_usleep(interval);
  268 +
  269 + // TODO: FIXME: use shared ptr message.
  270 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing();
  271 +
  272 + // TODO: FIXME: magic number
  273 + for (int i = 0; i < 100; ++i) {
  274 + char buf[32]; // TODO: FIXME: magic number
  275 + sprintf(buf, "%d", i);
  276 + pkt->data->set(buf, new SrsAmf0String(random_data));
  277 + }
  278 +
  279 + // TODO: FIXME: get length from the rtmp protocol stack.
  280 + play_bytes += pkt->get_payload_length();
  281 +
  282 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  283 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  284 + srs_error("send bandwidth check play messages failed. ret=%d", ret);
  285 + return ret;
  286 + }
  287 +
  288 + // sleep while current kbps <= max_play_kbps
  289 + int kbps = 0;
  290 + while (true) {
  291 + if(srs_get_system_time_ms() - current_time != 0)
  292 + kbps = play_bytes * 8 / (srs_get_system_time_ms() - current_time);
  293 +
  294 + if (kbps > max_play_kbps) {
  295 + st_usleep(500);
  296 + } else {
  297 + break;
  298 + }
  299 + }
  300 + }
  301 + actual_duration_ms = srs_get_system_time_ms() - current_time;
  302 + srs_info("BW check send play bytes over.");
  303 +
  304 + if (true) {
  305 + // notify client to stop play
  306 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_play();
  307 + pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
  308 + pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
  309 + pkt->data->set("duration_delta", new SrsAmf0Number(actual_duration_ms));
  310 + pkt->data->set("bytes_delta", new SrsAmf0Number(play_bytes));
  311 +
  312 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  313 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  314 + srs_error("send bandwidth check stop play message failed. ret=%d", ret);
  315 + return ret;
  316 + }
  317 + srs_info("BW check stop play bytes.");
  318 + }
  319 +
  320 + while (true) {
  321 + // recv client's stop play response.
  322 + SrsCommonMessage* msg = NULL;
  323 + SrsBandwidthPacket* pkt = NULL;
  324 + if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
  325 + srs_error("expect bandwidth message failed. ret=%d", ret);
  326 + return ret;
  327 + }
  328 + SrsAutoFree(SrsCommonMessage, msg, false);
  329 + srs_info("get bandwidth message succes.");
  330 +
  331 + if (pkt->is_stopped_play()) {
  332 + srs_info("BW check recv stop play response.");
  333 + break;
  334 + }
  335 + }
  336 +
  337 + return ret;
  338 +}
  339 +
  340 +int SrsBandwidth::check_publish(
  341 + int duration_ms, int interval_ms, int& actual_duration_ms,
  342 + int& publish_bytes, int max_pub_kbps)
  343 +{
  344 + int ret = ERROR_SUCCESS;
  345 +
  346 + SrsProtocol* protocol = rtmp->get_protocol();
  347 +
  348 + if (true) {
  349 + // notify client to start publish
  350 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_publish();
  351 +
  352 + pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
  353 + pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
  354 +
  355 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  356 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  357 + srs_error("send bandwidth check start publish message failed. ret=%d", ret);
  358 + return ret;
  359 + }
  360 + srs_info("BW check publish begin.");
  361 + }
  362 +
  363 + while (true) {
  364 + // read client's notification of starting publish
  365 + SrsCommonMessage* msg = NULL;
  366 + SrsBandwidthPacket* pkt = NULL;
  367 + if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
  368 + srs_error("expect bandwidth message failed. ret=%d", ret);
  369 + return ret;
  370 + }
  371 + SrsAutoFree(SrsCommonMessage, msg, false);
  372 + srs_info("get bandwidth message succes.");
  373 +
  374 + if (pkt->is_starting_publish()) {
  375 + srs_info("BW check recv publish begin response.");
  376 + break;
  377 + }
  378 + }
  379 +
  380 + // recv publish msgs until @duration_ms ms
  381 + int64_t current_time = srs_get_system_time_ms();
  382 + while ( (srs_get_system_time_ms() - current_time) < duration_ms ) {
  383 + st_usleep(0);
  384 +
  385 + SrsCommonMessage* msg = NULL;
  386 + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
  387 + srs_error("recv message failed. ret=%d", ret);
  388 + return ret;
  389 + }
  390 + SrsAutoFree(SrsCommonMessage, msg, false);
  391 +
  392 + // TODO: FIXME.
  393 + publish_bytes += msg->header.payload_length;
  394 +
  395 + int kbps = 0;
  396 + while (true) {
  397 + if(srs_get_system_time_ms() - current_time != 0)
  398 + kbps = publish_bytes * 8 / (srs_get_system_time_ms() - current_time);
  399 +
  400 + if (kbps > max_pub_kbps) {
  401 + st_usleep(500);
  402 + } else {
  403 + break;
  404 + }
  405 + }
  406 + }
  407 + actual_duration_ms = srs_get_system_time_ms() - current_time;
  408 + srs_info("BW check recv publish data over.");
  409 +
  410 + if (true) {
  411 + // notify client to stop publish
  412 + SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_publish();
  413 + pkt->data->set("duration_ms", new SrsAmf0Number(duration_ms));
  414 + pkt->data->set("interval_ms", new SrsAmf0Number(interval_ms));
  415 + pkt->data->set("duration_delta", new SrsAmf0Number(actual_duration_ms));
  416 + pkt->data->set("bytes_delta", new SrsAmf0Number(publish_bytes));
  417 +
  418 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
  419 + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) {
  420 + srs_error("send bandwidth check stop publish message failed. ret=%d", ret);
  421 + return ret;
  422 + }
  423 + srs_info("BW check stop publish bytes.");
  424 + }
  425 +
  426 + // expect client to stop publish
  427 + // if flash client, we never expect the client stop publish bytes,
  428 + // for the flash send call packet to test publish bandwidth,
  429 + // there are many many packets in the queue.
  430 + // we just ignore the packet and send the bandwidth test data.
  431 + // TODO: FIXME: check whether flash client.
  432 + while (false) {
  433 + // recv client's stop publish response.
  434 + SrsCommonMessage* msg = NULL;
  435 + SrsBandwidthPacket* pkt = NULL;
  436 + if ((ret = srs_rtmp_expect_message<SrsBandwidthPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
  437 + srs_error("expect bandwidth message failed. ret=%d", ret);
  438 + return ret;
  439 + }
  440 + SrsAutoFree(SrsCommonMessage, msg, false);
  441 + srs_info("get bandwidth message succes.");
  442 +
  443 + if (pkt->is_stopped_publish()) {
  444 + srs_info("BW check recv stop publish response.");
  445 + break;
  446 + }
  447 + }
  448 +
  449 + return ret;
  450 +}
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013 wenjiegit
  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_CORE_BANDWIDTH_HPP
  25 +#define SRS_CORE_BANDWIDTH_HPP
  26 +
  27 +/*
  28 +#include <srs_core_bandwidth.hpp>
  29 +*/
  30 +#include <srs_core.hpp>
  31 +
  32 +class SrsRequest;
  33 +class SrsRtmp;
  34 +
  35 +/**
  36 +* bandwidth test agent which provides the interfaces for bandwidth check.
  37 +* 1. if vhost disabled bandwidth check, ignore.
  38 +* 2. otherwise, check the key, error if verify failed.
  39 +* 3. check the interval limit, error if bandwidth in the interval window.
  40 +* 4. check the bandwidth under the max kbps.
  41 +* 5. send the bandwidth data to client.
  42 +* bandwidth workflow:
  43 +* +------------+ +----------+
  44 +* | Client | | Server |
  45 +* +-----+------+ +-----+----+
  46 +* | |
  47 +* | connect vhost------> | if vhost enable bandwidth,
  48 +* | <-----result(success) | do bandwidth check.
  49 +* | |
  50 +* | <----call(start play) | onSrsBandCheckStartPlayBytes
  51 +* | result(playing)-----> | onSrsBandCheckStartingPlayBytes
  52 +* | <-------data(playing) | onSrsBandCheckStartingPlayBytes
  53 +* | <-----call(stop play) | onSrsBandCheckStopPlayBytes
  54 +* | result(stopped)-----> | onSrsBandCheckStoppedPlayBytes
  55 +* | |
  56 +* | <-call(start publish) | onSrsBandCheckStartPublishBytes
  57 +* | result(publishing)--> | onSrsBandCheckStartingPublishBytes
  58 +* | data(publishing)----> | onSrsBandCheckStartingPublishBytes
  59 +* | <--call(stop publish) | onSrsBandCheckStopPublishBytes
  60 +* | result(stopped)(1)--> | onSrsBandCheckStoppedPublishBytes
  61 +* | |
  62 +* | <--------------report |
  63 +* | final(2)------------> | finalClientPacket
  64 +* | <END> |
  65 +*
  66 +* 1. when flash client, server ignore the publish stopped result,
  67 +* and directly send the report to flash client.
  68 +* 2. flash client only. when got report, flash client should send out
  69 +* a final packet and close the connection immediately.
  70 +*/
  71 +class SrsBandwidth
  72 +{
  73 +private:
  74 + SrsRequest* req;
  75 + SrsRtmp* rtmp;
  76 +public:
  77 + SrsBandwidth();
  78 + virtual ~SrsBandwidth();
  79 +public:
  80 + /**
  81 + * do the bandwidth test.
  82 + */
  83 + virtual int bandwidth_test(SrsRequest* _req, st_netfd_t stfd, SrsRtmp* _rtmp);
  84 +private:
  85 + virtual int get_local_ip(st_netfd_t stfd, char *&local_ip);
  86 + /**
  87 + * used to process band width check from client.
  88 + */
  89 + virtual int do_bandwidth_check();
  90 + virtual int check_play(int duration_ms, int interval_ms, int& actual_duration_ms, int& play_bytes, int max_play_kbps);
  91 + virtual int check_publish(int duration_ms, int interval_ms, int& actual_duration_ms, int& publish_bytes, int max_pub_kbps);
  92 +};
  93 +
  94 +#endif
@@ -41,6 +41,7 @@ using namespace std; @@ -41,6 +41,7 @@ using namespace std;
41 #include <srs_core_refer.hpp> 41 #include <srs_core_refer.hpp>
42 #include <srs_core_hls.hpp> 42 #include <srs_core_hls.hpp>
43 #include <srs_core_http.hpp> 43 #include <srs_core_http.hpp>
  44 +#include <srs_core_bandwidth.hpp>
44 45
45 #define SRS_PULSE_TIMEOUT_MS 100 46 #define SRS_PULSE_TIMEOUT_MS 100
46 #define SRS_SEND_TIMEOUT_US 5000000L 47 #define SRS_SEND_TIMEOUT_US 5000000L
@@ -58,6 +59,7 @@ SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) @@ -58,6 +59,7 @@ SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd)
58 #ifdef SRS_HTTP 59 #ifdef SRS_HTTP
59 http_hooks = new SrsHttpHooks(); 60 http_hooks = new SrsHttpHooks();
60 #endif 61 #endif
  62 + bandwidth = new SrsBandwidth();
61 63
62 config->subscribe(this); 64 config->subscribe(this);
63 } 65 }
@@ -74,6 +76,7 @@ SrsClient::~SrsClient() @@ -74,6 +76,7 @@ SrsClient::~SrsClient()
74 #ifdef SRS_HTTP 76 #ifdef SRS_HTTP
75 srs_freep(http_hooks); 77 srs_freep(http_hooks);
76 #endif 78 #endif
  79 + srs_freep(bandwidth);
77 } 80 }
78 81
79 // TODO: return detail message when error for client. 82 // TODO: return detail message when error for client.
@@ -154,38 +157,11 @@ int SrsClient::service_cycle() @@ -154,38 +157,11 @@ int SrsClient::service_cycle()
154 } 157 }
155 srs_verbose("set peer bandwidth success"); 158 srs_verbose("set peer bandwidth success");
156 159
157 - if (config->get_bw_check_enabled(req->vhost, req->bw_key)) {  
158 - static int64_t last_check_time_ms = srs_get_system_time_ms();  
159 - int64_t interval_ms = 0;  
160 - int limit_kbps = 0;  
161 -  
162 - config->get_bw_check_settings(req->vhost, interval_ms, limit_kbps);  
163 -  
164 - if((srs_get_system_time_ms() - last_check_time_ms) < interval_ms  
165 - && last_check_time_ms != srs_get_system_time_ms())  
166 - {  
167 - srs_trace("bandcheck interval less than limted interval. last time=%lld, current time=%lld"  
168 - , last_check_time_ms, srs_get_system_time_ms());  
169 -  
170 - return rtmp->response_connect_reject(req, "your bandcheck frequency is too high!");  
171 - } else {  
172 - last_check_time_ms = srs_get_system_time_ms(); // update last check time  
173 - char* local_ip = 0;  
174 -  
175 - if ((ret = get_local_ip(local_ip)) != ERROR_SUCCESS) {  
176 - srs_error("get local ip failed. ret = %d", ret);  
177 - return ret;  
178 - }  
179 -  
180 - if ((ret = rtmp->response_connect_app(req, local_ip)) != ERROR_SUCCESS) {  
181 - srs_error("response connect app failed. ret=%d", ret);  
182 - return ret;  
183 - }  
184 -  
185 - return rtmp->start_bandwidth_check(limit_kbps);  
186 - }  
187 - }  
188 - 160 + // do bandwidth test if connect to the vhost which is for bandwidth check.
  161 + if (config->get_bw_check_enabled(req->vhost)) {
  162 + return bandwidth->bandwidth_test(req, stfd, rtmp);
  163 + }
  164 +
189 if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) { 165 if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) {
190 srs_error("response connect app failed. ret=%d", ret); 166 srs_error("response connect app failed. ret=%d", ret);
191 return ret; 167 return ret;
@@ -44,6 +44,7 @@ class SrsCommonMessage; @@ -44,6 +44,7 @@ class SrsCommonMessage;
44 #ifdef SRS_HTTP 44 #ifdef SRS_HTTP
45 class SrsHttpHooks; 45 class SrsHttpHooks;
46 #endif 46 #endif
  47 +class SrsBandwidth;
47 48
48 /** 49 /**
49 * the client provides the main logic control for RTMP clients. 50 * the client provides the main logic control for RTMP clients.
@@ -59,6 +60,7 @@ private: @@ -59,6 +60,7 @@ private:
59 #ifdef SRS_HTTP 60 #ifdef SRS_HTTP
60 SrsHttpHooks* http_hooks; 61 SrsHttpHooks* http_hooks;
61 #endif 62 #endif
  63 + SrsBandwidth* bandwidth;
62 public: 64 public:
63 SrsClient(SrsServer* srs_server, st_netfd_t client_stfd); 65 SrsClient(SrsServer* srs_server, st_netfd_t client_stfd);
64 virtual ~SrsClient(); 66 virtual ~SrsClient();
@@ -1443,30 +1443,27 @@ SrsConfDirective* SrsConfig::get_listen() @@ -1443,30 +1443,27 @@ SrsConfDirective* SrsConfig::get_listen()
1443 return root->get("listen"); 1443 return root->get("listen");
1444 } 1444 }
1445 1445
1446 -int SrsConfig::get_chunk_size()  
1447 -{  
1448 - SrsConfDirective* conf = root->get("chunk_size");  
1449 - if (!conf) {  
1450 - return SRS_CONF_DEFAULT_CHUNK_SIZE;  
1451 - }  
1452 -  
1453 - return ::atoi(conf->arg0().c_str());  
1454 -}  
1455 -  
1456 int SrsConfig::get_chunk_size(const std::string &vhost) 1446 int SrsConfig::get_chunk_size(const std::string &vhost)
1457 { 1447 {
1458 SrsConfDirective* conf = get_vhost(vhost); 1448 SrsConfDirective* conf = get_vhost(vhost);
1459 1449
1460 if (!conf) { 1450 if (!conf) {
1461 - return get_chunk_size(); 1451 + return SRS_CONF_DEFAULT_CHUNK_SIZE;
1462 } 1452 }
1463 1453
1464 - SrsConfDirective* conf_vhost = conf->get("chunk_size");  
1465 - if (!conf_vhost) {  
1466 - return get_chunk_size(); 1454 + conf = conf->get("chunk_size");
  1455 + if (!conf) {
  1456 + // vhost does not specify the chunk size,
  1457 + // use the global instead.
  1458 + conf = root->get("chunk_size");
  1459 + if (!conf) {
  1460 + return SRS_CONF_DEFAULT_CHUNK_SIZE;
  1461 + }
  1462 +
  1463 + return ::atoi(conf->arg0().c_str());
1467 } 1464 }
1468 1465
1469 - return ::atoi(conf_vhost->arg0().c_str()); 1466 + return ::atoi(conf->arg0().c_str());
1470 } 1467 }
1471 1468
1472 int SrsConfig::get_pithy_print_publish() 1469 int SrsConfig::get_pithy_print_publish()
@@ -1568,6 +1565,90 @@ void SrsConfig::get_bw_check_settings(const std::string &vhost, int64_t &interva @@ -1568,6 +1565,90 @@ void SrsConfig::get_bw_check_settings(const std::string &vhost, int64_t &interva
1568 } 1565 }
1569 } 1566 }
1570 1567
  1568 +bool SrsConfig::get_bw_check_enabled(const string &vhost)
  1569 +{
  1570 + SrsConfDirective* conf = get_vhost(vhost);
  1571 +
  1572 + if (!conf) {
  1573 + return false;
  1574 + }
  1575 +
  1576 + conf = conf->get("bandcheck");
  1577 + if (!conf) {
  1578 + return false;
  1579 + }
  1580 +
  1581 + conf = conf->get("enabled");
  1582 + if (!conf || conf->arg0() != "on") {
  1583 + return false;
  1584 + }
  1585 +
  1586 + return true;
  1587 +}
  1588 +
  1589 +string SrsConfig::get_bw_check_key(const string &vhost)
  1590 +{
  1591 + SrsConfDirective* conf = get_vhost(vhost);
  1592 +
  1593 + if (!conf) {
  1594 + return "";
  1595 + }
  1596 +
  1597 + conf = conf->get("bandcheck");
  1598 + if (!conf) {
  1599 + return "";
  1600 + }
  1601 +
  1602 + conf = conf->get("key");
  1603 + if (!conf) {
  1604 + return "";
  1605 + }
  1606 +
  1607 + return conf->arg0();
  1608 +}
  1609 +
  1610 +int SrsConfig::get_bw_check_interval_ms(const string &vhost)
  1611 +{
  1612 + SrsConfDirective* conf = get_vhost(vhost);
  1613 +
  1614 + if (!conf) {
  1615 + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
  1616 + }
  1617 +
  1618 + conf = conf->get("bandcheck");
  1619 + if (!conf) {
  1620 + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
  1621 + }
  1622 +
  1623 + conf = conf->get("interval_ms");
  1624 + if (!conf) {
  1625 + return SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL;
  1626 + }
  1627 +
  1628 + return ::atoi(conf->arg0().c_str()) * 1000;
  1629 +}
  1630 +
  1631 +int SrsConfig::get_bw_check_limit_kbps(const string &vhost)
  1632 +{
  1633 + SrsConfDirective* conf = get_vhost(vhost);
  1634 +
  1635 + if (!conf) {
  1636 + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
  1637 + }
  1638 +
  1639 + conf = conf->get("bandcheck");
  1640 + if (!conf) {
  1641 + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
  1642 + }
  1643 +
  1644 + conf = conf->get("limit_kbps");
  1645 + if (!conf) {
  1646 + return SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS;
  1647 + }
  1648 +
  1649 + return ::atoi(conf->arg0().c_str());
  1650 +}
  1651 +
1571 int SrsConfig::get_pithy_print_encoder() 1652 int SrsConfig::get_pithy_print_encoder()
1572 { 1653 {
1573 SrsConfDirective* pithy = root->get("encoder"); 1654 SrsConfDirective* pithy = root->get("encoder");
@@ -53,6 +53,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -53,6 +53,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
53 #define SRS_CONF_DEFAULT_QUEUE_LENGTH 30 53 #define SRS_CONF_DEFAULT_QUEUE_LENGTH 30
54 // in seconds, the paused queue length. 54 // in seconds, the paused queue length.
55 #define SRS_CONF_DEFAULT_PAUSED_LENGTH 10 55 #define SRS_CONF_DEFAULT_PAUSED_LENGTH 10
  56 +// the interval in seconds for bandwidth check
  57 +#define SRS_CONF_DEFAULT_BANDWIDTH_INTERVAL 30
  58 +// the interval in seconds for bandwidth check
  59 +#define SRS_CONF_DEFAULT_BANDWIDTH_LIMIT_KBPS 1000
56 60
57 #define SRS_CONF_DEFAULT_CHUNK_SIZE 4096 61 #define SRS_CONF_DEFAULT_CHUNK_SIZE 4096
58 62
@@ -164,15 +168,16 @@ public: @@ -164,15 +168,16 @@ public:
164 virtual SrsConfDirective* get_refer_play(std::string vhost); 168 virtual SrsConfDirective* get_refer_play(std::string vhost);
165 virtual SrsConfDirective* get_refer_publish(std::string vhost); 169 virtual SrsConfDirective* get_refer_publish(std::string vhost);
166 virtual SrsConfDirective* get_listen(); 170 virtual SrsConfDirective* get_listen();
167 - virtual int get_chunk_size();  
168 virtual int get_chunk_size(const std::string& vhost); 171 virtual int get_chunk_size(const std::string& vhost);
169 virtual int get_pithy_print_publish(); 172 virtual int get_pithy_print_publish();
170 virtual int get_pithy_print_forwarder(); 173 virtual int get_pithy_print_forwarder();
171 virtual int get_pithy_print_encoder(); 174 virtual int get_pithy_print_encoder();
172 virtual int get_pithy_print_hls(); 175 virtual int get_pithy_print_hls();
173 virtual int get_pithy_print_play(); 176 virtual int get_pithy_print_play();
174 - virtual bool get_bw_check_enabled(const std::string &vhost, const std::string &key);  
175 - virtual void get_bw_check_settings(const std::string &vhost, int64_t &interval_ms, int &limit_kbps); 177 + virtual bool get_bw_check_enabled(const std::string& vhost);
  178 + virtual std::string get_bw_check_key(const std::string& vhost);
  179 + virtual int get_bw_check_interval_ms(const std::string& vhost);
  180 + virtual int get_bw_check_limit_kbps(const std::string& vhost);
176 }; 181 };
177 182
178 /** 183 /**
@@ -87,6 +87,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -87,6 +87,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
87 #define ERROR_SYSTEM_IP_INVALID 411 87 #define ERROR_SYSTEM_IP_INVALID 411
88 #define ERROR_SYSTEM_FORWARD_LOOP 412 88 #define ERROR_SYSTEM_FORWARD_LOOP 412
89 #define ERROR_SYSTEM_WAITPID 413 89 #define ERROR_SYSTEM_WAITPID 413
  90 +#define ERROR_SYSTEM_BANDWIDTH_KEY 414
  91 +#define ERROR_SYSTEM_BANDWIDTH_DENIED 415
90 92
91 // see librtmp. 93 // see librtmp.
92 // failed when open ssl create the dh 94 // failed when open ssl create the dh
@@ -32,6 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -32,6 +32,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #include <srs_core_stream.hpp> 32 #include <srs_core_stream.hpp>
33 #include <srs_core_autofree.hpp> 33 #include <srs_core_autofree.hpp>
34 34
  35 +using namespace std;
  36 +
35 /**************************************************************************** 37 /****************************************************************************
36 ***************************************************************************** 38 *****************************************************************************
37 ****************************************************************************/ 39 ****************************************************************************/
@@ -209,6 +211,35 @@ messages. @@ -209,6 +211,35 @@ messages.
209 #define RTMP_AMF0_DATA_SET_DATAFRAME "@setDataFrame" 211 #define RTMP_AMF0_DATA_SET_DATAFRAME "@setDataFrame"
210 #define RTMP_AMF0_DATA_ON_METADATA "onMetaData" 212 #define RTMP_AMF0_DATA_ON_METADATA "onMetaData"
211 213
  214 +/**
  215 +* band width check method name, which will be invoked by client.
  216 +* band width check mothods use SrsBandwidthPacket as its internal packet type,
  217 +* so ensure you set command name when you use it.
  218 +*/
  219 +// server play control
  220 +#define SRS_BW_CHECK_START_PLAY "onSrsBandCheckStartPlayBytes"
  221 +#define SRS_BW_CHECK_STARTING_PLAY "onSrsBandCheckStartingPlayBytes"
  222 +#define SRS_BW_CHECK_STOP_PLAY "onSrsBandCheckStopPlayBytes"
  223 +#define SRS_BW_CHECK_STOPPED_PLAY "onSrsBandCheckStoppedPlayBytes"
  224 +
  225 +// server publish control
  226 +#define SRS_BW_CHECK_START_PUBLISH "onSrsBandCheckStartPublishBytes"
  227 +#define SRS_BW_CHECK_STARTING_PUBLISH "onSrsBandCheckStartingPublishBytes"
  228 +#define SRS_BW_CHECK_STOP_PUBLISH "onSrsBandCheckStopPublishBytes"
  229 +#define SRS_BW_CHECK_STOPPED_PUBLISH "onSrsBandCheckStoppedPublishBytes"
  230 +
  231 +// EOF control.
  232 +#define SRS_BW_CHECK_FINISHED "onSrsBandCheckFinished"
  233 +// for flash, it will sendout a final call,
  234 +// used to confirm got the report.
  235 +// actually, client send out this packet and close the connection,
  236 +// so server may cannot got this packet, ignore is ok.
  237 +#define SRS_BW_CHECK_FLASH_FINAL "finalClientPacket"
  238 +
  239 +// client only
  240 +#define SRS_BW_CHECK_PLAYING "onSrsBandCheckPlaying"
  241 +#define SRS_BW_CHECK_PUBLISHING "onSrsBandCheckPublishing"
  242 +
212 /**************************************************************************** 243 /****************************************************************************
213 ***************************************************************************** 244 *****************************************************************************
214 ****************************************************************************/ 245 ****************************************************************************/
@@ -284,7 +315,7 @@ SrsProtocol::~SrsProtocol() @@ -284,7 +315,7 @@ SrsProtocol::~SrsProtocol()
284 srs_freep(skt); 315 srs_freep(skt);
285 } 316 }
286 317
287 -std::string SrsProtocol::get_request_name(double transcationId) 318 +string SrsProtocol::get_request_name(double transcationId)
288 { 319 {
289 if (requests.find(transcationId) == requests.end()) { 320 if (requests.find(transcationId) == requests.end()) {
290 return ""; 321 return "";
@@ -1324,19 +1355,21 @@ int SrsCommonMessage::decode_packet(SrsProtocol* protocol) @@ -1324,19 +1355,21 @@ int SrsCommonMessage::decode_packet(SrsProtocol* protocol)
1324 srs_info("decode the AMF0/AMF3 data(onMetaData message)."); 1355 srs_info("decode the AMF0/AMF3 data(onMetaData message).");
1325 packet = new SrsOnMetaDataPacket(); 1356 packet = new SrsOnMetaDataPacket();
1326 return packet->decode(stream); 1357 return packet->decode(stream);
1327 - } else if( command == SRS_BW_CHECK_FINISHED  
1328 - || command == SRS_BW_CHECK_PLAYING  
1329 - || command == SRS_BW_CHECK_PUBLISHING  
1330 - || command == SRS_BW_CHECK_STARTING_PLAY  
1331 - || command == SRS_BW_CHECK_STARTING_PUBLISH  
1332 - || command == SRS_BW_CHECK_START_PLAY  
1333 - || command == SRS_BW_CHECK_START_PUBLISH  
1334 - || command == SRS_BW_CHECK_STOPPED_PLAY  
1335 - || command == SRS_BW_CHECK_STOP_PLAY  
1336 - || command == SRS_BW_CHECK_STOP_PUBLISH) 1358 + } else if(command == SRS_BW_CHECK_FINISHED
  1359 + || command == SRS_BW_CHECK_PLAYING
  1360 + || command == SRS_BW_CHECK_PUBLISHING
  1361 + || command == SRS_BW_CHECK_STARTING_PLAY
  1362 + || command == SRS_BW_CHECK_STARTING_PUBLISH
  1363 + || command == SRS_BW_CHECK_START_PLAY
  1364 + || command == SRS_BW_CHECK_START_PUBLISH
  1365 + || command == SRS_BW_CHECK_STOPPED_PLAY
  1366 + || command == SRS_BW_CHECK_STOP_PLAY
  1367 + || command == SRS_BW_CHECK_STOP_PUBLISH
  1368 + || command == SRS_BW_CHECK_STOPPED_PUBLISH
  1369 + || command == SRS_BW_CHECK_FLASH_FINAL)
1337 { 1370 {
1338 srs_info("decode the AMF0/AMF3 band width check message."); 1371 srs_info("decode the AMF0/AMF3 band width check message.");
1339 - packet = new SrsOnStatusCallPacket(); 1372 + packet = new SrsBandwidthPacket();
1340 return packet->decode(stream); 1373 return packet->decode(stream);
1341 } 1374 }
1342 1375
@@ -1390,7 +1423,7 @@ int SrsCommonMessage::get_perfer_cid() @@ -1390,7 +1423,7 @@ int SrsCommonMessage::get_perfer_cid()
1390 return packet->get_perfer_cid(); 1423 return packet->get_perfer_cid();
1391 } 1424 }
1392 1425
1393 -void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id) 1426 +SrsCommonMessage* SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id)
1394 { 1427 {
1395 srs_freep(packet); 1428 srs_freep(packet);
1396 1429
@@ -1399,6 +1432,8 @@ void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id) @@ -1399,6 +1432,8 @@ void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id)
1399 header.message_type = packet->get_message_type(); 1432 header.message_type = packet->get_message_type();
1400 header.payload_length = packet->get_payload_length(); 1433 header.payload_length = packet->get_payload_length();
1401 header.stream_id = stream_id; 1434 header.stream_id = stream_id;
  1435 +
  1436 + return this;
1402 } 1437 }
1403 1438
1404 int SrsCommonMessage::encode_packet() 1439 int SrsCommonMessage::encode_packet()
@@ -1803,10 +1838,14 @@ int SrsConnectAppResPacket::get_message_type() @@ -1803,10 +1838,14 @@ int SrsConnectAppResPacket::get_message_type()
1803 int SrsConnectAppResPacket::get_size() 1838 int SrsConnectAppResPacket::get_size()
1804 { 1839 {
1805 int size = srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size(); 1840 int size = srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size();
1806 - if(props->size() > 0) 1841 +
  1842 + if (props->size() > 0) {
1807 size += srs_amf0_get_object_size(props); 1843 size += srs_amf0_get_object_size(props);
1808 - if(info->size() > 0) 1844 + }
  1845 +
  1846 + if (info->size() > 0) {
1809 size += srs_amf0_get_object_size(info); 1847 size += srs_amf0_get_object_size(info);
  1848 + }
1810 1849
1811 return size; 1850 return size;
1812 } 1851 }
@@ -1827,7 +1866,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream) @@ -1827,7 +1866,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream)
1827 } 1866 }
1828 srs_verbose("encode transaction_id success."); 1867 srs_verbose("encode transaction_id success.");
1829 1868
1830 - if(props->size() > 0){ 1869 + if (props->size() > 0) {
1831 if ((ret = srs_amf0_write_object(stream, props)) != ERROR_SUCCESS) { 1870 if ((ret = srs_amf0_write_object(stream, props)) != ERROR_SUCCESS) {
1832 srs_error("encode props failed. ret=%d", ret); 1871 srs_error("encode props failed. ret=%d", ret);
1833 return ret; 1872 return ret;
@@ -1836,7 +1875,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream) @@ -1836,7 +1875,7 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream)
1836 1875
1837 srs_verbose("encode props success."); 1876 srs_verbose("encode props success.");
1838 1877
1839 - if(info->size() > 0){ 1878 + if (info->size() > 0) {
1840 if ((ret = srs_amf0_write_object(stream, info)) != ERROR_SUCCESS) { 1879 if ((ret = srs_amf0_write_object(stream, info)) != ERROR_SUCCESS) {
1841 srs_error("encode info failed. ret=%d", ret); 1880 srs_error("encode info failed. ret=%d", ret);
1842 return ret; 1881 return ret;
@@ -2651,6 +2690,163 @@ int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) @@ -2651,6 +2690,163 @@ int SrsOnStatusCallPacket::encode_packet(SrsStream* stream)
2651 return ret; 2690 return ret;
2652 } 2691 }
2653 2692
  2693 +SrsBandwidthPacket::SrsBandwidthPacket()
  2694 +{
  2695 + command_name = RTMP_AMF0_COMMAND_ON_STATUS;
  2696 + transaction_id = 0;
  2697 + args = new SrsAmf0Null();
  2698 + data = new SrsAmf0Object();
  2699 +}
  2700 +
  2701 +SrsBandwidthPacket::~SrsBandwidthPacket()
  2702 +{
  2703 + srs_freep(args);
  2704 + srs_freep(data);
  2705 +}
  2706 +
  2707 +int SrsBandwidthPacket::get_perfer_cid()
  2708 +{
  2709 + return RTMP_CID_OverStream;
  2710 +}
  2711 +
  2712 +int SrsBandwidthPacket::get_message_type()
  2713 +{
  2714 + return RTMP_MSG_AMF0CommandMessage;
  2715 +}
  2716 +
  2717 +int SrsBandwidthPacket::get_size()
  2718 +{
  2719 + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size()
  2720 + + srs_amf0_get_null_size() + srs_amf0_get_object_size(data);
  2721 +}
  2722 +
  2723 +int SrsBandwidthPacket::encode_packet(SrsStream* stream)
  2724 +{
  2725 + int ret = ERROR_SUCCESS;
  2726 +
  2727 + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
  2728 + srs_error("encode command_name failed. ret=%d", ret);
  2729 + return ret;
  2730 + }
  2731 + srs_verbose("encode command_name success.");
  2732 +
  2733 + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
  2734 + srs_error("encode transaction_id failed. ret=%d", ret);
  2735 + return ret;
  2736 + }
  2737 + srs_verbose("encode transaction_id success.");
  2738 +
  2739 + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
  2740 + srs_error("encode args failed. ret=%d", ret);
  2741 + return ret;
  2742 + }
  2743 + srs_verbose("encode args success.");;
  2744 +
  2745 + if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) {
  2746 + srs_error("encode data failed. ret=%d", ret);
  2747 + return ret;
  2748 + }
  2749 + srs_verbose("encode data success.");
  2750 +
  2751 + srs_info("encode onStatus(Call) packet success.");
  2752 +
  2753 + return ret;
  2754 +}
  2755 +
  2756 +int SrsBandwidthPacket::decode(SrsStream *stream)
  2757 +{
  2758 + int ret = ERROR_SUCCESS;
  2759 +
  2760 + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
  2761 + srs_error("amf0 decode play command_name failed. ret=%d", ret);
  2762 + return ret;
  2763 + }
  2764 +
  2765 + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
  2766 + srs_error("amf0 decode play transaction_id failed. ret=%d", ret);
  2767 + return ret;
  2768 + }
  2769 +
  2770 + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
  2771 + srs_error("amf0 decode play command_object failed. ret=%d", ret);
  2772 + return ret;
  2773 + }
  2774 +
  2775 + // @remark, for bandwidth test, ignore the data field.
  2776 +
  2777 + srs_info("decode SrsBandwidthPacket success.");
  2778 +
  2779 + return ret;
  2780 +}
  2781 +
  2782 +bool SrsBandwidthPacket::is_starting_play()
  2783 +{
  2784 + return command_name == SRS_BW_CHECK_STARTING_PLAY;
  2785 +}
  2786 +
  2787 +bool SrsBandwidthPacket::is_stopped_play()
  2788 +{
  2789 + return command_name == SRS_BW_CHECK_STOPPED_PLAY;
  2790 +}
  2791 +
  2792 +bool SrsBandwidthPacket::is_starting_publish()
  2793 +{
  2794 + return command_name == SRS_BW_CHECK_STARTING_PUBLISH;
  2795 +}
  2796 +
  2797 +bool SrsBandwidthPacket::is_stopped_publish()
  2798 +{
  2799 + return command_name == SRS_BW_CHECK_STOPPED_PUBLISH;
  2800 +}
  2801 +
  2802 +bool SrsBandwidthPacket::is_flash_final()
  2803 +{
  2804 + return command_name == SRS_BW_CHECK_FLASH_FINAL;
  2805 +}
  2806 +
  2807 +SrsBandwidthPacket* SrsBandwidthPacket::create_finish()
  2808 +{
  2809 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2810 + return pkt->set_command(SRS_BW_CHECK_FINISHED);
  2811 +}
  2812 +
  2813 +SrsBandwidthPacket* SrsBandwidthPacket::create_start_play()
  2814 +{
  2815 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2816 + return pkt->set_command(SRS_BW_CHECK_START_PLAY);
  2817 +}
  2818 +
  2819 +SrsBandwidthPacket* SrsBandwidthPacket::create_playing()
  2820 +{
  2821 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2822 + return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY);
  2823 +}
  2824 +
  2825 +SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play()
  2826 +{
  2827 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2828 + return pkt->set_command(SRS_BW_CHECK_STOP_PLAY);
  2829 +}
  2830 +
  2831 +SrsBandwidthPacket* SrsBandwidthPacket::create_start_publish()
  2832 +{
  2833 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2834 + return pkt->set_command(SRS_BW_CHECK_START_PUBLISH);
  2835 +}
  2836 +
  2837 +SrsBandwidthPacket* SrsBandwidthPacket::create_stop_publish()
  2838 +{
  2839 + SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
  2840 + return pkt->set_command(SRS_BW_CHECK_STOP_PUBLISH);
  2841 +}
  2842 +
  2843 +SrsBandwidthPacket* SrsBandwidthPacket::set_command(string command)
  2844 +{
  2845 + command_name = command;
  2846 +
  2847 + return this;
  2848 +}
  2849 +
2654 SrsOnStatusDataPacket::SrsOnStatusDataPacket() 2850 SrsOnStatusDataPacket::SrsOnStatusDataPacket()
2655 { 2851 {
2656 command_name = RTMP_AMF0_COMMAND_ON_STATUS; 2852 command_name = RTMP_AMF0_COMMAND_ON_STATUS;
@@ -331,6 +331,7 @@ class SrsCommonMessage : public ISrsMessage @@ -331,6 +331,7 @@ class SrsCommonMessage : public ISrsMessage
331 { 331 {
332 private: 332 private:
333 typedef ISrsMessage super; 333 typedef ISrsMessage super;
  334 + disable_default_copy(SrsCommonMessage);
334 // decoded message payload. 335 // decoded message payload.
335 private: 336 private:
336 SrsStream* stream; 337 SrsStream* stream;
@@ -366,9 +367,10 @@ public: @@ -366,9 +367,10 @@ public:
366 * set the encoded packet to encode_packet() to payload. 367 * set the encoded packet to encode_packet() to payload.
367 * @stream_id, the id of stream which is created by createStream. 368 * @stream_id, the id of stream which is created by createStream.
368 * @remark, user never free the pkt, the message will auto free it. 369 * @remark, user never free the pkt, the message will auto free it.
  370 + * @return message itself.
369 */ 371 */
370 // TODO: refine the send methods. 372 // TODO: refine the send methods.
371 - virtual void set_packet(SrsPacket* pkt, int stream_id); 373 + virtual SrsCommonMessage* set_packet(SrsPacket* pkt, int stream_id);
372 /** 374 /**
373 * encode the packet to message payload bytes. 375 * encode the packet to message payload bytes.
374 * @remark there exists empty packet, so maybe the payload is NULL. 376 * @remark there exists empty packet, so maybe the payload is NULL.
@@ -857,6 +859,55 @@ protected: @@ -857,6 +859,55 @@ protected:
857 }; 859 };
858 860
859 /** 861 /**
  862 +* the special packet for the bandwidth test.
  863 +* actually, it's a SrsOnStatusCallPacket, but
  864 +* 1. encode with data field, to send data to client.
  865 +* 2. decode ignore the data field, donot care.
  866 +*/
  867 +class SrsBandwidthPacket : public SrsPacket
  868 +{
  869 +private:
  870 + typedef SrsPacket super;
  871 + disable_default_copy(SrsBandwidthPacket);
  872 +protected:
  873 + virtual const char* get_class_name()
  874 + {
  875 + return CLASS_NAME_STRING(SrsBandwidthPacket);
  876 + }
  877 +public:
  878 + std::string command_name;
  879 + double transaction_id;
  880 + SrsAmf0Null* args;
  881 + SrsAmf0Object* data;
  882 +public:
  883 + SrsBandwidthPacket();
  884 + virtual ~SrsBandwidthPacket();
  885 +public:
  886 + virtual int get_perfer_cid();
  887 +public:
  888 + virtual int get_message_type();
  889 +protected:
  890 + virtual int get_size();
  891 + virtual int encode_packet(SrsStream* stream);
  892 +public:
  893 + virtual int decode(SrsStream* stream);
  894 +public:
  895 + virtual bool is_starting_play();
  896 + virtual bool is_stopped_play();
  897 + virtual bool is_starting_publish();
  898 + virtual bool is_stopped_publish();
  899 + virtual bool is_flash_final();
  900 + static SrsBandwidthPacket* create_finish();
  901 + static SrsBandwidthPacket* create_start_play();
  902 + static SrsBandwidthPacket* create_playing();
  903 + static SrsBandwidthPacket* create_stop_play();
  904 + static SrsBandwidthPacket* create_start_publish();
  905 + static SrsBandwidthPacket* create_stop_publish();
  906 +private:
  907 + virtual SrsBandwidthPacket* set_command(std::string command);
  908 +};
  909 +
  910 +/**
860 * onStatus data, AMF0 Data 911 * onStatus data, AMF0 Data
861 * @remark, user must set the stream_id by SrsMessage.set_packet(). 912 * @remark, user must set the stream_id by SrsMessage.set_packet().
862 */ 913 */
@@ -594,7 +594,7 @@ int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) @@ -594,7 +594,7 @@ int SrsRtmp::set_peer_bandwidth(int bandwidth, int type)
594 return ret; 594 return ret;
595 } 595 }
596 596
597 -int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip) 597 +int SrsRtmp::response_connect_app(SrsRequest *req, const char* server_ip)
598 { 598 {
599 int ret = ERROR_SUCCESS; 599 int ret = ERROR_SUCCESS;
600 600
@@ -621,13 +621,12 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip) @@ -621,13 +621,12 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
621 data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); 621 data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB));
622 data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); 622 data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL));
623 data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); 623 data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT));
624 -  
625 - if (ip) {  
626 - data->set("srs_server_ip", new SrsAmf0String(ip)); 624 + data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));
  625 +
  626 + if (server_ip) {
  627 + data->set("srs_server_ip", new SrsAmf0String(server_ip));
627 } 628 }
628 -  
629 - data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));  
630 - 629 +
631 msg->set_packet(pkt, 0); 630 msg->set_packet(pkt, 0);
632 631
633 if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { 632 if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
@@ -639,27 +638,25 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip) @@ -639,27 +638,25 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
639 return ret; 638 return ret;
640 } 639 }
641 640
642 -int SrsRtmp::response_connect_reject(SrsRequest *req, const std::string &description) 641 +void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc)
643 { 642 {
644 int ret = ERROR_SUCCESS; 643 int ret = ERROR_SUCCESS;
645 644
646 - SrsCommonMessage* msg = new SrsCommonMessage();  
647 SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); 645 SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
648 pkt->command_name = "_error"; 646 pkt->command_name = "_error";
649 pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); 647 pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError));
650 pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); 648 pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected));
651 - pkt->props->set(StatusDescription, new SrsAmf0String(description.c_str())); 649 + pkt->props->set(StatusDescription, new SrsAmf0String(desc));
652 //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); 650 //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding));
653 651
654 - msg->set_packet(pkt, 0);  
655 - 652 + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0);
656 if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { 653 if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
657 srs_error("send connect app response rejected message failed. ret=%d", ret); 654 srs_error("send connect app response rejected message failed. ret=%d", ret);
658 - return ret; 655 + return;
659 } 656 }
660 srs_info("send connect app response rejected message success."); 657 srs_info("send connect app response rejected message success.");
661 658
662 - return ret; 659 + return;
663 } 660 }
664 661
665 int SrsRtmp::on_bw_done() 662 int SrsRtmp::on_bw_done()
@@ -169,8 +169,16 @@ public: @@ -169,8 +169,16 @@ public:
169 * using the Limit type field. 169 * using the Limit type field.
170 */ 170 */
171 virtual int set_peer_bandwidth(int bandwidth, int type); 171 virtual int set_peer_bandwidth(int bandwidth, int type);
  172 +<<<<<<< HEAD
172 virtual int response_connect_app(SrsRequest* req, const char *ip = 0); 173 virtual int response_connect_app(SrsRequest* req, const char *ip = 0);
173 virtual int response_connect_reject(SrsRequest* req, const std::string& description); 174 virtual int response_connect_reject(SrsRequest* req, const std::string& description);
  175 +=======
  176 + /**
  177 + * @param server_ip the ip of server.
  178 + */
  179 + virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL);
  180 + virtual void response_connect_reject(SrsRequest* req, const char* desc);
  181 +>>>>>>> upstream/master
174 virtual int on_bw_done(); 182 virtual int on_bw_done();
175 /** 183 /**
176 * recv some message to identify the client. 184 * recv some message to identify the client.
@@ -10,6 +10,8 @@ file @@ -10,6 +10,8 @@ file
10 ..\core\srs_core_amf0.cpp, 10 ..\core\srs_core_amf0.cpp,
11 ..\core\srs_core_autofree.hpp, 11 ..\core\srs_core_autofree.hpp,
12 ..\core\srs_core_autofree.cpp, 12 ..\core\srs_core_autofree.cpp,
  13 + ..\core\srs_core_bandwidth.hpp,
  14 + ..\core\srs_core_bandwidth.cpp,
13 ..\core\srs_core_buffer.hpp, 15 ..\core\srs_core_buffer.hpp,
14 ..\core\srs_core_buffer.cpp, 16 ..\core\srs_core_buffer.cpp,
15 ..\core\srs_core_client.hpp, 17 ..\core\srs_core_client.hpp,