正在显示
41 个修改的文件
包含
2441 行增加
和
269 行删除
| @@ -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 | + | <-----------result(success) | | ||
| 171 | + | | | ||
| 172 | + | <----------call(start play) | | ||
| 173 | + | result(playing)----------> | | ||
| 174 | + | <-------------data(playing) | | ||
| 175 | + | <-----------call(stop play) | | ||
| 176 | + | result(stopped)----------> | | ||
| 177 | + | | | ||
| 178 | + | <-------call(start publish) | | ||
| 179 | + | result(publishing)-------> | | ||
| 180 | + | data(publishing)---------> | | ||
| 181 | + | <--------call(stop publish) | | ||
| 182 | + | result(stopped)(1)-------> | | ||
| 183 | + | | | ||
| 184 | + | <--------------------report | | ||
| 185 | + | final(2)-----------------> | | ||
| 186 | + | <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. |
trunk/3rdparty/bootstrap.2.3.2.zip
0 → 100644
不能预览此文件类型
trunk/3rdparty/jquery-1.10.2.zip
0 → 100644
不能预览此文件类型
| @@ -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> |
trunk/research/players/srs_chat.html
0 → 100755
| 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 © 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> |
trunk/scripts/_ffmpeg.demo.sh
0 → 100755
trunk/scripts/_ffmpeg.players.sh
0 → 100755
trunk/scripts/_step.build.sh
0 → 100755
trunk/scripts/_step.start.api.server.sh
0 → 100755
| 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 |
trunk/scripts/_step.start.ffmpeg.demo.sh
0 → 100755
| 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 |
trunk/scripts/_step.start.ffmpeg.players.sh
0 → 100755
| 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 |
trunk/scripts/_step.start.nginx.sh
0 → 100755
| 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 |
trunk/scripts/_step.start.srs.19350.sh
0 → 100755
| 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 |
trunk/scripts/_step.start.srs.sh
0 → 100755
| 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 |
trunk/scripts/build.sh
0 → 100755
trunk/scripts/dev.sh
0 → 100755
| 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}" |
trunk/scripts/run.sh
0 → 100755
| 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}" |
trunk/scripts/stop.sh
0 → 100755
| 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 |
trunk/src/core/srs_core_bandwidth.cpp
0 → 100644
| 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 | +} |
trunk/src/core/srs_core_bandwidth.hpp
0 → 100644
| 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, |
-
请 注册 或 登录 后发表评论