正在显示
16 个修改的文件
包含
213 行增加
和
11 行删除
1 | -#Simple-RTMP-Server | 1 | +# Simple-RTMP-Server |
2 | 2 | ||
3 | [![CircleCI](https://circleci.com/gh/ossrs/srs/tree/2.0release.svg?style=svg)](https://circleci.com/gh/ossrs/srs/tree/2.0release) | 3 | [![CircleCI](https://circleci.com/gh/ossrs/srs/tree/2.0release.svg?style=svg)](https://circleci.com/gh/ossrs/srs/tree/2.0release) |
4 | -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ossrs/srs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
5 | [![Wechat](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://github.com/ossrs/srs/wiki/v1_CN_Contact#wechat) | 4 | [![Wechat](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://github.com/ossrs/srs/wiki/v1_CN_Contact#wechat) |
5 | +[<img width="52" alt="Skype" src="https://cloud.githubusercontent.com/assets/2777660/24329166/3821a328-1230-11e7-844a-506a5d17dd3d.png">](https://github.com/ossrs/srs/wiki/v1_EN_Contact#skype-or-gitter) | ||
6 | 6 | ||
7 | SRS/2.0, [ZhouGuowen][release2] | 7 | SRS/2.0, [ZhouGuowen][release2] |
8 | 8 | ||
@@ -300,6 +300,8 @@ Remark: | @@ -300,6 +300,8 @@ Remark: | ||
300 | 300 | ||
301 | ## Releases | 301 | ## Releases |
302 | 302 | ||
303 | +* 2017-04-18, [Release v2.0-r1][r2.0r1], 2.0 release1, 2.0.239, 86515 lines. | ||
304 | +* 2017-03-03, [Release v2.0-r0][r2.0r0], 2.0 release0, 2.0.234, 86373 lines. | ||
303 | * 2017-01-18, [Release v2.0-b4][r2.0b4], 2.0 beta4, 2.0.230, 86334 lines. | 305 | * 2017-01-18, [Release v2.0-b4][r2.0b4], 2.0 beta4, 2.0.230, 86334 lines. |
304 | * 2016-11-13, [Release v2.0-b3][r2.0b3], 2.0 beta3, 2.0.223, 86685 lines. | 306 | * 2016-11-13, [Release v2.0-b3][r2.0b3], 2.0 beta3, 2.0.223, 86685 lines. |
305 | * 2016-11-09, [Release v2.0-b2][r2.0b2], 2.0 beta2, 2.0.221, 86691 lines. | 307 | * 2016-11-09, [Release v2.0-b2][r2.0b2], 2.0 beta2, 2.0.221, 86691 lines. |
@@ -335,6 +337,14 @@ Remark: | @@ -335,6 +337,14 @@ Remark: | ||
335 | 337 | ||
336 | ## History | 338 | ## History |
337 | 339 | ||
340 | +* v2.0, 2017-04-23, Fix [#851][bug #851], HTTP API support number of video frames for FPS. 2.0.240 | ||
341 | +* <strong>v2.0, 2017-04-18, [2.0 release1(2.0.239)][r2.0r1] released. 86515 lines.</strong> | ||
342 | +* v2.0, 2017-04-18, Fix [#848][bug #848], crash at HTTP fast buffer grow. 2.0.239 | ||
343 | +* v2.0, 2017-04-15, Fix [#844][bug #844], support Haivision encoder. 2.0.238 | ||
344 | +* v2.0, 2017-04-15, Merge [#846][bug #846], fix fd leak for FLV stream caster. 2.0.237 | ||
345 | +* v2.0, 2017-04-15, Merge [#841][bug #841], avoid the duplicated sps/pps in ts. 2.0.236 | ||
346 | +* v2.0, 2017-04-09, Fix [#834][bug #834], crash for TS context corrupt. 2.0.235 | ||
347 | +* <strong>v2.0, 2017-03-03, [2.0 release0(2.0.234)][r2.0r0] released. 86373 lines.</strong> | ||
338 | * v2.0, 2017-02-25, for [#730][bug #730], remove the test code. 2.0.234 | 348 | * v2.0, 2017-02-25, for [#730][bug #730], remove the test code. 2.0.234 |
339 | * v2.0, 2017-02-09, fix [#503][bug #503] disable utilities when reload a source. 2.0.233 | 349 | * v2.0, 2017-02-09, fix [#503][bug #503] disable utilities when reload a source. 2.0.233 |
340 | * v2.0, 2017-01-22, for [#752][bug #752] release the io then free it for kbps. 2.0.232 | 350 | * v2.0, 2017-01-22, for [#752][bug #752] release the io then free it for kbps. 2.0.232 |
@@ -1277,10 +1287,18 @@ Winlin | @@ -1277,10 +1287,18 @@ Winlin | ||
1277 | [bug #750]: https://github.com/ossrs/srs/issues/750 | 1287 | [bug #750]: https://github.com/ossrs/srs/issues/750 |
1278 | [bug #752]: https://github.com/ossrs/srs/issues/752 | 1288 | [bug #752]: https://github.com/ossrs/srs/issues/752 |
1279 | [bug #503]: https://github.com/ossrs/srs/issues/503 | 1289 | [bug #503]: https://github.com/ossrs/srs/issues/503 |
1290 | +[bug #834]: https://github.com/ossrs/srs/issues/834 | ||
1291 | +[bug #841]: https://github.com/ossrs/srs/issues/841 | ||
1292 | +[bug #846]: https://github.com/ossrs/srs/issues/846 | ||
1293 | +[bug #844]: https://github.com/ossrs/srs/issues/844 | ||
1294 | +[bug #848]: https://github.com/ossrs/srs/issues/848 | ||
1295 | +[bug #851]: https://github.com/ossrs/srs/issues/851 | ||
1280 | [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx | 1296 | [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx |
1281 | 1297 | ||
1282 | [exo #828]: https://github.com/google/ExoPlayer/pull/828 | 1298 | [exo #828]: https://github.com/google/ExoPlayer/pull/828 |
1283 | 1299 | ||
1300 | +[r2.0r1]: https://github.com/ossrs/srs/releases/tag/v2.0-r1 | ||
1301 | +[r2.0r0]: https://github.com/ossrs/srs/releases/tag/v2.0-r0 | ||
1284 | [r2.0b4]: https://github.com/ossrs/srs/releases/tag/v2.0-b4 | 1302 | [r2.0b4]: https://github.com/ossrs/srs/releases/tag/v2.0-b4 |
1285 | [r2.0b3]: https://github.com/ossrs/srs/releases/tag/v2.0-b3 | 1303 | [r2.0b3]: https://github.com/ossrs/srs/releases/tag/v2.0-b3 |
1286 | [r2.0b2]: https://github.com/ossrs/srs/releases/tag/v2.0-b2 | 1304 | [r2.0b2]: https://github.com/ossrs/srs/releases/tag/v2.0-b2 |
@@ -1324,6 +1342,6 @@ Winlin | @@ -1324,6 +1342,6 @@ Winlin | ||
1324 | [branch2]: https://github.com/ossrs/srs/tree/2.0release | 1342 | [branch2]: https://github.com/ossrs/srs/tree/2.0release |
1325 | [release2]: https://github.com/ossrs/srs/wiki/v1_CN_Product#release20 | 1343 | [release2]: https://github.com/ossrs/srs/wiki/v1_CN_Product#release20 |
1326 | [release3]: https://github.com/ossrs/srs/wiki/v1_CN_Product#release30 | 1344 | [release3]: https://github.com/ossrs/srs/wiki/v1_CN_Product#release30 |
1327 | -[centos0]: http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-2.0.230.zip | ||
1328 | -[centos1]: http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-2.0.230.zip | 1345 | +[centos0]: http://winlinvip.github.io/srs.release/releases/files/SRS-CentOS6-x86_64-2.0.239.zip |
1346 | +[centos1]: http://www.ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-2.0.239.zip | ||
1329 | 1347 |
@@ -42,7 +42,7 @@ else | @@ -42,7 +42,7 @@ else | ||
42 | echo "build fdk-aac-0.1.3" | 42 | echo "build fdk-aac-0.1.3" |
43 | cd $ff_current_dir && | 43 | cd $ff_current_dir && |
44 | rm -rf fdk-aac-0.1.3 && unzip -q ${ff_src_dir}/fdk-aac-0.1.3.zip && | 44 | rm -rf fdk-aac-0.1.3 && unzip -q ${ff_src_dir}/fdk-aac-0.1.3.zip && |
45 | - cd fdk-aac-0.1.3 && bash autogen.sh && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install && | 45 | + cd fdk-aac-0.1.3 && bash autogen.sh && ./configure --prefix=${ff_release_dir} --enable-static && make ${SRS_JOBS} && make install |
46 | ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build fdk-aac-0.1.3 failed"; exit 1; fi | 46 | ret=$?; if [[ 0 -ne ${ret} ]]; then echo "build fdk-aac-0.1.3 failed"; exit 1; fi |
47 | fi | 47 | fi |
48 | 48 |
@@ -86,6 +86,11 @@ void SrsAppCasterFlv::remove(SrsConnection* c) | @@ -86,6 +86,11 @@ void SrsAppCasterFlv::remove(SrsConnection* c) | ||
86 | if ((it = std::find(conns.begin(), conns.end(), c)) != conns.end()) { | 86 | if ((it = std::find(conns.begin(), conns.end(), c)) != conns.end()) { |
87 | conns.erase(it); | 87 | conns.erase(it); |
88 | } | 88 | } |
89 | + | ||
90 | + // fixbug: SrsHttpConn for CasterFlv is not freed, which could cause memory leak | ||
91 | + // so, free conn which is not managed by SrsServer->conns; | ||
92 | + // @see: https://github.com/ossrs/srs/issues/826 | ||
93 | + srs_freep(c); | ||
89 | } | 94 | } |
90 | 95 | ||
91 | int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | 96 | int SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) |
@@ -257,6 +257,7 @@ SrsPublishRecvThread::SrsPublishRecvThread( | @@ -257,6 +257,7 @@ SrsPublishRecvThread::SrsPublishRecvThread( | ||
257 | 257 | ||
258 | recv_error_code = ERROR_SUCCESS; | 258 | recv_error_code = ERROR_SUCCESS; |
259 | _nb_msgs = 0; | 259 | _nb_msgs = 0; |
260 | + video_frames = 0; | ||
260 | error = st_cond_new(); | 261 | error = st_cond_new(); |
261 | ncid = cid = 0; | 262 | ncid = cid = 0; |
262 | 263 | ||
@@ -298,6 +299,11 @@ int64_t SrsPublishRecvThread::nb_msgs() | @@ -298,6 +299,11 @@ int64_t SrsPublishRecvThread::nb_msgs() | ||
298 | return _nb_msgs; | 299 | return _nb_msgs; |
299 | } | 300 | } |
300 | 301 | ||
302 | +uint64_t SrsPublishRecvThread::nb_video_frames() | ||
303 | +{ | ||
304 | + return video_frames; | ||
305 | +} | ||
306 | + | ||
301 | int SrsPublishRecvThread::error_code() | 307 | int SrsPublishRecvThread::error_code() |
302 | { | 308 | { |
303 | return recv_error_code; | 309 | return recv_error_code; |
@@ -378,6 +384,10 @@ int SrsPublishRecvThread::handle(SrsCommonMessage* msg) | @@ -378,6 +384,10 @@ int SrsPublishRecvThread::handle(SrsCommonMessage* msg) | ||
378 | 384 | ||
379 | _nb_msgs++; | 385 | _nb_msgs++; |
380 | 386 | ||
387 | + if (msg->header.is_video()) { | ||
388 | + video_frames++; | ||
389 | + } | ||
390 | + | ||
381 | // log to show the time of recv thread. | 391 | // log to show the time of recv thread. |
382 | srs_verbose("recv thread now=%"PRId64"us, got msg time=%"PRId64"ms, size=%d", | 392 | srs_verbose("recv thread now=%"PRId64"us, got msg time=%"PRId64"ms, size=%d", |
383 | srs_update_system_time_ms(), msg->header.timestamp, msg->size); | 393 | srs_update_system_time_ms(), msg->header.timestamp, msg->size); |
@@ -154,6 +154,8 @@ private: | @@ -154,6 +154,8 @@ private: | ||
154 | SrsRequest* req; | 154 | SrsRequest* req; |
155 | // the msgs already got. | 155 | // the msgs already got. |
156 | int64_t _nb_msgs; | 156 | int64_t _nb_msgs; |
157 | + // The video frames we got. | ||
158 | + uint64_t video_frames; | ||
157 | // for mr(merged read), | 159 | // for mr(merged read), |
158 | // @see https://github.com/ossrs/srs/issues/241 | 160 | // @see https://github.com/ossrs/srs/issues/241 |
159 | bool mr; | 161 | bool mr; |
@@ -186,6 +188,7 @@ public: | @@ -186,6 +188,7 @@ public: | ||
186 | */ | 188 | */ |
187 | virtual int wait(int timeout_ms); | 189 | virtual int wait(int timeout_ms); |
188 | virtual int64_t nb_msgs(); | 190 | virtual int64_t nb_msgs(); |
191 | + virtual uint64_t nb_video_frames(); | ||
189 | virtual int error_code(); | 192 | virtual int error_code(); |
190 | virtual void set_cid(int v); | 193 | virtual void set_cid(int v); |
191 | virtual int get_cid(); | 194 | virtual int get_cid(); |
@@ -482,6 +482,14 @@ int SrsRtmpConn::stream_service_cycle() | @@ -482,6 +482,14 @@ int SrsRtmpConn::stream_service_cycle() | ||
482 | } | 482 | } |
483 | srs_info("security check ok"); | 483 | srs_info("security check ok"); |
484 | 484 | ||
485 | + // Never allow the empty stream name, for HLS may write to a file with empty name. | ||
486 | + // @see https://github.com/ossrs/srs/issues/834 | ||
487 | + if (req->stream.empty()) { | ||
488 | + ret = ERROR_RTMP_STREAM_NAME_EMPTY; | ||
489 | + srs_error("RTMP: Empty stream name not allowed, ret=%d", ret); | ||
490 | + return ret; | ||
491 | + } | ||
492 | + | ||
485 | // client is identified, set the timeout to service timeout. | 493 | // client is identified, set the timeout to service timeout. |
486 | rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); | 494 | rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); |
487 | rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); | 495 | rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); |
@@ -538,6 +546,16 @@ int SrsRtmpConn::stream_service_cycle() | @@ -538,6 +546,16 @@ int SrsRtmpConn::stream_service_cycle() | ||
538 | 546 | ||
539 | return publishing(source); | 547 | return publishing(source); |
540 | } | 548 | } |
549 | + case SrsRtmpConnHaivisionPublish: { | ||
550 | + srs_verbose("Haivision start to publish stream %s.", req->stream.c_str()); | ||
551 | + | ||
552 | + if ((ret = rtmp->start_haivision_publish(res->stream_id)) != ERROR_SUCCESS) { | ||
553 | + srs_error("start to publish stream failed. ret=%d", ret); | ||
554 | + return ret; | ||
555 | + } | ||
556 | + | ||
557 | + return publishing(source); | ||
558 | + } | ||
541 | case SrsRtmpConnFlashPublish: { | 559 | case SrsRtmpConnFlashPublish: { |
542 | srs_verbose("flash start to publish stream %s.", req->stream.c_str()); | 560 | srs_verbose("flash start to publish stream %s.", req->stream.c_str()); |
543 | 561 | ||
@@ -831,7 +849,7 @@ int SrsRtmpConn::publishing(SrsSource* source) | @@ -831,7 +849,7 @@ int SrsRtmpConn::publishing(SrsSource* source) | ||
831 | // @see: https://github.com/ossrs/srs/issues/237 | 849 | // @see: https://github.com/ossrs/srs/issues/237 |
832 | SrsPublishRecvThread trd(rtmp, req, | 850 | SrsPublishRecvThread trd(rtmp, req, |
833 | st_netfd_fileno(stfd), 0, this, source, | 851 | st_netfd_fileno(stfd), 0, this, source, |
834 | - client_type == SrsRtmpConnFMLEPublish, | 852 | + client_type != SrsRtmpConnFlashPublish, |
835 | vhost_is_edge); | 853 | vhost_is_edge); |
836 | 854 | ||
837 | srs_info("start to publish stream %s success", req->stream.c_str()); | 855 | srs_info("start to publish stream %s success", req->stream.c_str()); |
@@ -888,6 +906,7 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) | @@ -888,6 +906,7 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) | ||
888 | } | 906 | } |
889 | 907 | ||
890 | int64_t nb_msgs = 0; | 908 | int64_t nb_msgs = 0; |
909 | + uint64_t nb_frames = 0; | ||
891 | while (!disposed) { | 910 | while (!disposed) { |
892 | pprint->elapse(); | 911 | pprint->elapse(); |
893 | 912 | ||
@@ -924,6 +943,14 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) | @@ -924,6 +943,14 @@ int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* trd) | ||
924 | } | 943 | } |
925 | nb_msgs = trd->nb_msgs(); | 944 | nb_msgs = trd->nb_msgs(); |
926 | 945 | ||
946 | + // Update the stat for video fps. | ||
947 | + // @remark https://github.com/ossrs/srs/issues/851 | ||
948 | + SrsStatistic* stat = SrsStatistic::instance(); | ||
949 | + if ((ret = stat->on_video_frames(req, (int)(trd->nb_video_frames() - nb_frames))) != ERROR_SUCCESS) { | ||
950 | + return ret; | ||
951 | + } | ||
952 | + nb_frames = trd->nb_video_frames(); | ||
953 | + | ||
927 | // reportable | 954 | // reportable |
928 | if (pprint->can_print()) { | 955 | if (pprint->can_print()) { |
929 | kbps->sample(); | 956 | kbps->sample(); |
@@ -90,6 +90,7 @@ int SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType type, std: | @@ -90,6 +90,7 @@ int SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType type, std: | ||
90 | break; | 90 | break; |
91 | case SrsRtmpConnFMLEPublish: | 91 | case SrsRtmpConnFMLEPublish: |
92 | case SrsRtmpConnFlashPublish: | 92 | case SrsRtmpConnFlashPublish: |
93 | + case SrsRtmpConnHaivisionPublish: | ||
93 | if (rule->arg0() != "publish") { | 94 | if (rule->arg0() != "publish") { |
94 | break; | 95 | break; |
95 | } | 96 | } |
@@ -135,6 +136,7 @@ int SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType type, std:: | @@ -135,6 +136,7 @@ int SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType type, std:: | ||
135 | break; | 136 | break; |
136 | case SrsRtmpConnFMLEPublish: | 137 | case SrsRtmpConnFMLEPublish: |
137 | case SrsRtmpConnFlashPublish: | 138 | case SrsRtmpConnFlashPublish: |
139 | + case SrsRtmpConnHaivisionPublish: | ||
138 | if (rule->arg0() != "publish") { | 140 | if (rule->arg0() != "publish") { |
139 | break; | 141 | break; |
140 | } | 142 | } |
@@ -111,6 +111,7 @@ SrsStatisticStream::SrsStatisticStream() | @@ -111,6 +111,7 @@ SrsStatisticStream::SrsStatisticStream() | ||
111 | kbps->set_io(NULL, NULL); | 111 | kbps->set_io(NULL, NULL); |
112 | 112 | ||
113 | nb_clients = 0; | 113 | nb_clients = 0; |
114 | + nb_frames = 0; | ||
114 | } | 115 | } |
115 | 116 | ||
116 | SrsStatisticStream::~SrsStatisticStream() | 117 | SrsStatisticStream::~SrsStatisticStream() |
@@ -129,6 +130,7 @@ int SrsStatisticStream::dumps(stringstream& ss) | @@ -129,6 +130,7 @@ int SrsStatisticStream::dumps(stringstream& ss) | ||
129 | << SRS_JFIELD_STR("app", app) << SRS_JFIELD_CONT | 130 | << SRS_JFIELD_STR("app", app) << SRS_JFIELD_CONT |
130 | << SRS_JFIELD_ORG("live_ms", srs_get_system_time_ms()) << SRS_JFIELD_CONT | 131 | << SRS_JFIELD_ORG("live_ms", srs_get_system_time_ms()) << SRS_JFIELD_CONT |
131 | << SRS_JFIELD_ORG("clients", nb_clients) << SRS_JFIELD_CONT | 132 | << SRS_JFIELD_ORG("clients", nb_clients) << SRS_JFIELD_CONT |
133 | + << SRS_JFIELD_ORG("frames", nb_frames) << SRS_JFIELD_CONT | ||
132 | << SRS_JFIELD_ORG("send_bytes", kbps->get_send_bytes()) << SRS_JFIELD_CONT | 134 | << SRS_JFIELD_ORG("send_bytes", kbps->get_send_bytes()) << SRS_JFIELD_CONT |
133 | << SRS_JFIELD_ORG("recv_bytes", kbps->get_recv_bytes()) << SRS_JFIELD_CONT | 135 | << SRS_JFIELD_ORG("recv_bytes", kbps->get_recv_bytes()) << SRS_JFIELD_CONT |
134 | << SRS_JFIELD_OBJ("kbps") | 136 | << SRS_JFIELD_OBJ("kbps") |
@@ -327,6 +329,18 @@ int SrsStatistic::on_audio_info(SrsRequest* req, | @@ -327,6 +329,18 @@ int SrsStatistic::on_audio_info(SrsRequest* req, | ||
327 | return ret; | 329 | return ret; |
328 | } | 330 | } |
329 | 331 | ||
332 | +int SrsStatistic::on_video_frames(SrsRequest* req, int nb_frames) | ||
333 | +{ | ||
334 | + int ret = ERROR_SUCCESS; | ||
335 | + | ||
336 | + SrsStatisticVhost* vhost = create_vhost(req); | ||
337 | + SrsStatisticStream* stream = create_stream(vhost, req); | ||
338 | + | ||
339 | + stream->nb_frames += nb_frames; | ||
340 | + | ||
341 | + return ret; | ||
342 | +} | ||
343 | + | ||
330 | void SrsStatistic::on_stream_publish(SrsRequest* req, int cid) | 344 | void SrsStatistic::on_stream_publish(SrsRequest* req, int cid) |
331 | { | 345 | { |
332 | SrsStatisticVhost* vhost = create_vhost(req); | 346 | SrsStatisticVhost* vhost = create_vhost(req); |
@@ -70,6 +70,7 @@ public: | @@ -70,6 +70,7 @@ public: | ||
70 | bool active; | 70 | bool active; |
71 | int connection_cid; | 71 | int connection_cid; |
72 | int nb_clients; | 72 | int nb_clients; |
73 | + uint64_t nb_frames; | ||
73 | public: | 74 | public: |
74 | /** | 75 | /** |
75 | * stream total kbps. | 76 | * stream total kbps. |
@@ -173,6 +174,11 @@ public: | @@ -173,6 +174,11 @@ public: | ||
173 | SrsAacObjectType aac_object | 174 | SrsAacObjectType aac_object |
174 | ); | 175 | ); |
175 | /** | 176 | /** |
177 | + * When got videos, update the frames. | ||
178 | + * We only stat the total number of video frames. | ||
179 | + */ | ||
180 | + virtual int on_video_frames(SrsRequest* req, int nb_frames); | ||
181 | + /** | ||
176 | * when publish stream. | 182 | * when publish stream. |
177 | * @param req the request object of publish connection. | 183 | * @param req the request object of publish connection. |
178 | * @param cid the cid of publish connection. | 184 | * @param cid the cid of publish connection. |
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
31 | // current release version | 31 | // current release version |
32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
34 | -#define VERSION_REVISION 234 | 34 | +#define VERSION_REVISION 240 |
35 | 35 | ||
36 | // generated by configure, only macros. | 36 | // generated by configure, only macros. |
37 | #include <srs_auto_headers.hpp> | 37 | #include <srs_auto_headers.hpp> |
@@ -152,6 +152,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -152,6 +152,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
152 | #define ERROR_RTSP_AUDIO_CONFIG 2047 | 152 | #define ERROR_RTSP_AUDIO_CONFIG 2047 |
153 | #define ERROR_RTMP_STREAM_NOT_FOUND 2048 | 153 | #define ERROR_RTMP_STREAM_NOT_FOUND 2048 |
154 | #define ERROR_RTMP_CLIENT_NOT_FOUND 2049 | 154 | #define ERROR_RTMP_CLIENT_NOT_FOUND 2049 |
155 | +#define ERROR_RTMP_STREAM_NAME_EMPTY 2050 | ||
155 | // | 156 | // |
156 | // system control message, | 157 | // system control message, |
157 | // not an error, but special control logic. | 158 | // not an error, but special control logic. |
@@ -230,6 +231,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -230,6 +231,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
230 | #define ERROR_RESPONSE_CODE 3064 | 231 | #define ERROR_RESPONSE_CODE 3064 |
231 | #define ERROR_RESPONSE_DATA 3065 | 232 | #define ERROR_RESPONSE_DATA 3065 |
232 | #define ERROR_REQUEST_DATA 3066 | 233 | #define ERROR_REQUEST_DATA 3066 |
234 | +#define ERROR_TS_CONTEXT_NOT_READY 3067 | ||
233 | 235 | ||
234 | /////////////////////////////////////////////////////// | 236 | /////////////////////////////////////////////////////// |
235 | // HTTP/StreamCaster protocol error. | 237 | // HTTP/StreamCaster protocol error. |
@@ -199,6 +199,7 @@ ISrsTsHandler::~ISrsTsHandler() | @@ -199,6 +199,7 @@ ISrsTsHandler::~ISrsTsHandler() | ||
199 | 199 | ||
200 | SrsTsContext::SrsTsContext() | 200 | SrsTsContext::SrsTsContext() |
201 | { | 201 | { |
202 | + ready = false; | ||
202 | pure_audio = false; | 203 | pure_audio = false; |
203 | vcodec = SrsCodecVideoReserved; | 204 | vcodec = SrsCodecVideoReserved; |
204 | acodec = SrsCodecAudioReserved1; | 205 | acodec = SrsCodecAudioReserved1; |
@@ -234,6 +235,7 @@ void SrsTsContext::on_pmt_parsed() | @@ -234,6 +235,7 @@ void SrsTsContext::on_pmt_parsed() | ||
234 | 235 | ||
235 | void SrsTsContext::reset() | 236 | void SrsTsContext::reset() |
236 | { | 237 | { |
238 | + ready = false; | ||
237 | vcodec = SrsCodecVideoReserved; | 239 | vcodec = SrsCodecVideoReserved; |
238 | acodec = SrsCodecAudioReserved1; | 240 | acodec = SrsCodecAudioReserved1; |
239 | } | 241 | } |
@@ -433,6 +435,9 @@ int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStrea | @@ -433,6 +435,9 @@ int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStrea | ||
433 | } | 435 | } |
434 | } | 436 | } |
435 | 437 | ||
438 | + // When PAT and PMT are writen, the context is ready now. | ||
439 | + ready = true; | ||
440 | + | ||
436 | return ret; | 441 | return ret; |
437 | } | 442 | } |
438 | 443 | ||
@@ -440,6 +445,13 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p | @@ -440,6 +445,13 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p | ||
440 | { | 445 | { |
441 | int ret = ERROR_SUCCESS; | 446 | int ret = ERROR_SUCCESS; |
442 | 447 | ||
448 | + // Sometimes, the context is not ready(PAT/PMT write failed), error in this situation. | ||
449 | + if (!ready) { | ||
450 | + ret = ERROR_TS_CONTEXT_NOT_READY; | ||
451 | + srs_error("TS: context not ready, ret=%d", ret); | ||
452 | + return ret; | ||
453 | + } | ||
454 | + | ||
443 | if (msg->payload->length() == 0) { | 455 | if (msg->payload->length() == 0) { |
444 | return ret; | 456 | return ret; |
445 | } | 457 | } |
@@ -3027,6 +3039,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | @@ -3027,6 +3039,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
3027 | video->payload->append((const char*)default_aud_nalu, 2); | 3039 | video->payload->append((const char*)default_aud_nalu, 2); |
3028 | } | 3040 | } |
3029 | 3041 | ||
3042 | + bool is_sps_pps_appended = false; | ||
3030 | // all sample use cont nalu header, except the sps-pps before IDR frame. | 3043 | // all sample use cont nalu header, except the sps-pps before IDR frame. |
3031 | for (int i = 0; i < sample->nb_sample_units; i++) { | 3044 | for (int i = 0; i < sample->nb_sample_units; i++) { |
3032 | SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; | 3045 | SrsCodecSampleUnit* sample_unit = &sample->sample_units[i]; |
@@ -3044,7 +3057,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | @@ -3044,7 +3057,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
3044 | 3057 | ||
3045 | // Insert sps/pps before IDR when there is no sps/pps in samples. | 3058 | // Insert sps/pps before IDR when there is no sps/pps in samples. |
3046 | // The sps/pps is parsed from sequence header(generally the first flv packet). | 3059 | // The sps/pps is parsed from sequence header(generally the first flv packet). |
3047 | - if (nal_unit_type == SrsAvcNaluTypeIDR && !sample->has_sps_pps) { | 3060 | + if (nal_unit_type == SrsAvcNaluTypeIDR && !sample->has_sps_pps && !is_sps_pps_appended) { |
3048 | if (codec->sequenceParameterSetLength > 0) { | 3061 | if (codec->sequenceParameterSetLength > 0) { |
3049 | srs_avc_insert_aud(video->payload, aud_inserted); | 3062 | srs_avc_insert_aud(video->payload, aud_inserted); |
3050 | video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); | 3063 | video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength); |
@@ -3053,6 +3066,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | @@ -3053,6 +3066,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) | ||
3053 | srs_avc_insert_aud(video->payload, aud_inserted); | 3066 | srs_avc_insert_aud(video->payload, aud_inserted); |
3054 | video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); | 3067 | video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength); |
3055 | } | 3068 | } |
3069 | + is_sps_pps_appended = true; | ||
3056 | } | 3070 | } |
3057 | 3071 | ||
3058 | // Insert the NALU to video in annexb. | 3072 | // Insert the NALU to video in annexb. |
@@ -346,6 +346,11 @@ public: | @@ -346,6 +346,11 @@ public: | ||
346 | */ | 346 | */ |
347 | class SrsTsContext | 347 | class SrsTsContext |
348 | { | 348 | { |
349 | +private: | ||
350 | + // Whether context is ready, failed if try to write data when not ready. | ||
351 | + // When PAT and PMT writen, the context is ready. | ||
352 | + // @see https://github.com/ossrs/srs/issues/834 | ||
353 | + bool ready; | ||
349 | // codec | 354 | // codec |
350 | private: | 355 | private: |
351 | std::map<int, SrsTsChannel*> pids; | 356 | std::map<int, SrsTsChannel*> pids; |
@@ -160,9 +160,10 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) | @@ -160,9 +160,10 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) | ||
160 | // reset when buffer is empty. | 160 | // reset when buffer is empty. |
161 | p = end = buffer; | 161 | p = end = buffer; |
162 | srs_verbose("all consumed, reset fast buffer"); | 162 | srs_verbose("all consumed, reset fast buffer"); |
163 | - } else { | 163 | + } else if (nb_exists_bytes < nb_buffer && p > buffer) { |
164 | // move the left bytes to start of buffer. | 164 | // move the left bytes to start of buffer. |
165 | - srs_assert(nb_exists_bytes < nb_buffer); | 165 | + // @remark Only move memory when space is enough, or failed at next check. |
166 | + // @see https://github.com/ossrs/srs/issues/848 | ||
166 | buffer = (char*)memmove(buffer, p, nb_exists_bytes); | 167 | buffer = (char*)memmove(buffer, p, nb_exists_bytes); |
167 | p = buffer; | 168 | p = buffer; |
168 | end = p + nb_exists_bytes; | 169 | end = p + nb_exists_bytes; |
@@ -1812,6 +1812,7 @@ string srs_client_type_string(SrsRtmpConnType type) | @@ -1812,6 +1812,7 @@ string srs_client_type_string(SrsRtmpConnType type) | ||
1812 | case SrsRtmpConnPlay: return "Play"; | 1812 | case SrsRtmpConnPlay: return "Play"; |
1813 | case SrsRtmpConnFlashPublish: return "flash-publish"; | 1813 | case SrsRtmpConnFlashPublish: return "flash-publish"; |
1814 | case SrsRtmpConnFMLEPublish: return "fmle-publish"; | 1814 | case SrsRtmpConnFMLEPublish: return "fmle-publish"; |
1815 | + case SrsRtmpConnHaivisionPublish: return "haivision-publish"; | ||
1815 | default: return "Unknown"; | 1816 | default: return "Unknown"; |
1816 | } | 1817 | } |
1817 | } | 1818 | } |
@@ -2714,6 +2715,14 @@ int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& | @@ -2714,6 +2715,14 @@ int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& | ||
2714 | } | 2715 | } |
2715 | return ret; | 2716 | return ret; |
2716 | } | 2717 | } |
2718 | + | ||
2719 | + // For encoder of Haivision, it always send a _checkbw call message. | ||
2720 | + // @Remark the next message is createStream, so we continue to identify it. | ||
2721 | + // @see https://github.com/ossrs/srs/issues/844 | ||
2722 | + if (call->command_name == "_checkbw") { | ||
2723 | + srs_info("Haivision encoder identified."); | ||
2724 | + continue; | ||
2725 | + } | ||
2717 | continue; | 2726 | continue; |
2718 | } | 2727 | } |
2719 | 2728 | ||
@@ -2989,6 +2998,60 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) | @@ -2989,6 +2998,60 @@ int SrsRtmpServer::start_fmle_publish(int stream_id) | ||
2989 | return ret; | 2998 | return ret; |
2990 | } | 2999 | } |
2991 | 3000 | ||
3001 | +int SrsRtmpServer::start_haivision_publish(int stream_id) | ||
3002 | +{ | ||
3003 | + int ret = ERROR_SUCCESS; | ||
3004 | + | ||
3005 | + // publish | ||
3006 | + if (true) { | ||
3007 | + SrsCommonMessage* msg = NULL; | ||
3008 | + SrsPublishPacket* pkt = NULL; | ||
3009 | + if ((ret = expect_message<SrsPublishPacket>(&msg, &pkt)) != ERROR_SUCCESS) { | ||
3010 | + srs_error("recv publish message failed. ret=%d", ret); | ||
3011 | + return ret; | ||
3012 | + } | ||
3013 | + srs_info("recv publish request message success."); | ||
3014 | + | ||
3015 | + SrsAutoFree(SrsCommonMessage, msg); | ||
3016 | + SrsAutoFree(SrsPublishPacket, pkt); | ||
3017 | + } | ||
3018 | + | ||
3019 | + // publish response onFCPublish(NetStream.Publish.Start) | ||
3020 | + if (true) { | ||
3021 | + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); | ||
3022 | + | ||
3023 | + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; | ||
3024 | + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); | ||
3025 | + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); | ||
3026 | + | ||
3027 | + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { | ||
3028 | + srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); | ||
3029 | + return ret; | ||
3030 | + } | ||
3031 | + srs_info("send onFCPublish(NetStream.Publish.Start) message success."); | ||
3032 | + } | ||
3033 | + | ||
3034 | + // publish response onStatus(NetStream.Publish.Start) | ||
3035 | + if (true) { | ||
3036 | + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); | ||
3037 | + | ||
3038 | + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); | ||
3039 | + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); | ||
3040 | + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); | ||
3041 | + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); | ||
3042 | + | ||
3043 | + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { | ||
3044 | + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); | ||
3045 | + return ret; | ||
3046 | + } | ||
3047 | + srs_info("send onStatus(NetStream.Publish.Start) message success."); | ||
3048 | + } | ||
3049 | + | ||
3050 | + srs_info("Haivision publish success."); | ||
3051 | + | ||
3052 | + return ret; | ||
3053 | +} | ||
3054 | + | ||
2992 | int SrsRtmpServer::fmle_unpublish(int stream_id, double unpublish_tid) | 3055 | int SrsRtmpServer::fmle_unpublish(int stream_id, double unpublish_tid) |
2993 | { | 3056 | { |
2994 | int ret = ERROR_SUCCESS; | 3057 | int ret = ERROR_SUCCESS; |
@@ -3123,6 +3186,10 @@ int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int | @@ -3123,6 +3186,10 @@ int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int | ||
3123 | srs_info("identify client by create stream, play or flash publish."); | 3186 | srs_info("identify client by create stream, play or flash publish."); |
3124 | return identify_create_stream_client(dynamic_cast<SrsCreateStreamPacket*>(pkt), stream_id, type, stream_name, duration); | 3187 | return identify_create_stream_client(dynamic_cast<SrsCreateStreamPacket*>(pkt), stream_id, type, stream_name, duration); |
3125 | } | 3188 | } |
3189 | + if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) { | ||
3190 | + srs_info("identify client by FCPublish, haivision publish."); | ||
3191 | + return identify_haivision_publish_client(dynamic_cast<SrsFMLEStartPacket*>(pkt), type, stream_name); | ||
3192 | + } | ||
3126 | 3193 | ||
3127 | srs_trace("identify_create_stream_client:ignore AMF0/AMF3 command message."); | 3194 | srs_trace("identify_create_stream_client:ignore AMF0/AMF3 command message."); |
3128 | } | 3195 | } |
@@ -3150,6 +3217,26 @@ int SrsRtmpServer::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmp | @@ -3150,6 +3217,26 @@ int SrsRtmpServer::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmp | ||
3150 | return ret; | 3217 | return ret; |
3151 | } | 3218 | } |
3152 | 3219 | ||
3220 | +int SrsRtmpServer::identify_haivision_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, string& stream_name) | ||
3221 | +{ | ||
3222 | + int ret = ERROR_SUCCESS; | ||
3223 | + | ||
3224 | + type = SrsRtmpConnHaivisionPublish; | ||
3225 | + stream_name = req->stream_name; | ||
3226 | + | ||
3227 | + // FCPublish response | ||
3228 | + if (true) { | ||
3229 | + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); | ||
3230 | + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { | ||
3231 | + srs_error("send FCPublish response message failed. ret=%d", ret); | ||
3232 | + return ret; | ||
3233 | + } | ||
3234 | + srs_info("send FCPublish response message success."); | ||
3235 | + } | ||
3236 | + | ||
3237 | + return ret; | ||
3238 | +} | ||
3239 | + | ||
3153 | int SrsRtmpServer::identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, string& stream_name) | 3240 | int SrsRtmpServer::identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, string& stream_name) |
3154 | { | 3241 | { |
3155 | int ret = ERROR_SUCCESS; | 3242 | int ret = ERROR_SUCCESS; |
@@ -69,6 +69,7 @@ class SrsCommonMessage; | @@ -69,6 +69,7 @@ class SrsCommonMessage; | ||
69 | class SrsPacket; | 69 | class SrsPacket; |
70 | class SrsAmf0Object; | 70 | class SrsAmf0Object; |
71 | class IMergeReadHandler; | 71 | class IMergeReadHandler; |
72 | +class SrsCallPacket; | ||
72 | 73 | ||
73 | /**************************************************************************** | 74 | /**************************************************************************** |
74 | ***************************************************************************** | 75 | ***************************************************************************** |
@@ -633,6 +634,7 @@ enum SrsRtmpConnType | @@ -633,6 +634,7 @@ enum SrsRtmpConnType | ||
633 | SrsRtmpConnPlay, | 634 | SrsRtmpConnPlay, |
634 | SrsRtmpConnFMLEPublish, | 635 | SrsRtmpConnFMLEPublish, |
635 | SrsRtmpConnFlashPublish, | 636 | SrsRtmpConnFlashPublish, |
637 | + SrsRtmpConnHaivisionPublish, | ||
636 | }; | 638 | }; |
637 | std::string srs_client_type_string(SrsRtmpConnType type); | 639 | std::string srs_client_type_string(SrsRtmpConnType type); |
638 | bool srs_client_type_is_publish(SrsRtmpConnType type); | 640 | bool srs_client_type_is_publish(SrsRtmpConnType type); |
@@ -992,6 +994,11 @@ public: | @@ -992,6 +994,11 @@ public: | ||
992 | */ | 994 | */ |
993 | virtual int start_fmle_publish(int stream_id); | 995 | virtual int start_fmle_publish(int stream_id); |
994 | /** | 996 | /** |
997 | + * For encoder of Haivision, response the startup request. | ||
998 | + * @see https://github.com/ossrs/srs/issues/844 | ||
999 | + */ | ||
1000 | + virtual int start_haivision_publish(int stream_id); | ||
1001 | + /** | ||
995 | * process the FMLE unpublish event. | 1002 | * process the FMLE unpublish event. |
996 | * @unpublish_tid the unpublish request transaction id. | 1003 | * @unpublish_tid the unpublish request transaction id. |
997 | */ | 1004 | */ |
@@ -1027,6 +1034,7 @@ public: | @@ -1027,6 +1034,7 @@ public: | ||
1027 | private: | 1034 | private: |
1028 | virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); | 1035 | virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); |
1029 | virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); | 1036 | virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); |
1037 | + virtual int identify_haivision_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); | ||
1030 | virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, std::string& stream_name); | 1038 | virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, std::string& stream_name); |
1031 | private: | 1039 | private: |
1032 | virtual int identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, std::string& stream_name, double& duration); | 1040 | virtual int identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, std::string& stream_name, double& duration); |
@@ -1295,7 +1303,7 @@ public: | @@ -1295,7 +1303,7 @@ public: | ||
1295 | }; | 1303 | }; |
1296 | 1304 | ||
1297 | /** | 1305 | /** |
1298 | -* FMLE start publish: ReleaseStream/PublishStream | 1306 | +* FMLE start publish: ReleaseStream/PublishStream/FCPublish/FCUnpublish |
1299 | */ | 1307 | */ |
1300 | class SrsFMLEStartPacket : public SrsPacket | 1308 | class SrsFMLEStartPacket : public SrsPacket |
1301 | { | 1309 | { |
-
请 注册 或 登录 后发表评论