正在显示
15 个修改的文件
包含
1344 行增加
和
922 行删除
| @@ -326,7 +326,7 @@ Remark: | @@ -326,7 +326,7 @@ Remark: | ||
| 326 | * 2014-04-28, [Release v1.0-mainline2](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline2), support [dvr](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DVR), android, [edge](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Edge). 35255 lines.<br/> | 326 | * 2014-04-28, [Release v1.0-mainline2](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline2), support [dvr](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DVR), android, [edge](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Edge). 35255 lines.<br/> |
| 327 | * 2014-04-07, [Release v1.0-mainline](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline), support [arm](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_LinuxService), http [server](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPServer)/[api](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPApi), [ingest](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleIngest). 30000 lines.<br/> | 327 | * 2014-04-07, [Release v1.0-mainline](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline), support [arm](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_LinuxService), http [server](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPServer)/[api](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPApi), [ingest](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleIngest). 30000 lines.<br/> |
| 328 | * 2013-12-25, [Release v0.9](https://github.com/simple-rtmp-server/srs/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleDemo). 20926 lines.<br/> | 328 | * 2013-12-25, [Release v0.9](https://github.com/simple-rtmp-server/srs/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleDemo). 20926 lines.<br/> |
| 329 | -* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.<br/> | 329 | +* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [SB](https://github.com/simple-rtmp-server/srs-bench). 19186 lines.<br/> |
| 330 | * 2013-12-03, [Release v0.7](https://github.com/simple-rtmp-server/srs/releases/tag/0.7), support [live stream transcoding](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_FFMPEG). 17605 lines.<br/> | 330 | * 2013-12-03, [Release v0.7](https://github.com/simple-rtmp-server/srs/releases/tag/0.7), support [live stream transcoding](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_FFMPEG). 17605 lines.<br/> |
| 331 | * 2013-11-29, [Release v0.6](https://github.com/simple-rtmp-server/srs/releases/tag/0.6), support [forward](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.<br/> | 331 | * 2013-11-29, [Release v0.6](https://github.com/simple-rtmp-server/srs/releases/tag/0.6), support [forward](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.<br/> |
| 332 | * 2013-11-26, [Release v0.5](https://github.com/simple-rtmp-server/srs/releases/tag/0.5), support [HLS(m3u8)](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.<br/> | 332 | * 2013-11-26, [Release v0.5](https://github.com/simple-rtmp-server/srs/releases/tag/0.5), support [HLS(m3u8)](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.<br/> |
| @@ -525,7 +525,7 @@ Remark: | @@ -525,7 +525,7 @@ Remark: | ||
| 525 | * v1.0, 2014-06-27, SRS online 30days with RTMP/HLS. | 525 | * v1.0, 2014-06-27, SRS online 30days with RTMP/HLS. |
| 526 | * v1.0, 2014-06-25, fix [#108](https://github.com/simple-rtmp-server/srs/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133 | 526 | * v1.0, 2014-06-25, fix [#108](https://github.com/simple-rtmp-server/srs/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133 |
| 527 | * v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132 | 527 | * v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132 |
| 528 | -* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130 | 528 | +* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance#performancereport4k) connections(270kbps). 0.9.130 |
| 529 | * v1.0, 2014-06-21, support edge [token traverse](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DRM#tokentraverse), fix [#104](https://github.com/simple-rtmp-server/srs/issues/104). 0.9.129 | 529 | * v1.0, 2014-06-21, support edge [token traverse](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DRM#tokentraverse), fix [#104](https://github.com/simple-rtmp-server/srs/issues/104). 0.9.129 |
| 530 | * v1.0, 2014-06-19, add connections count to api summaries. 0.9.127 | 530 | * v1.0, 2014-06-19, add connections count to api summaries. 0.9.127 |
| 531 | * v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126 | 531 | * v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126 |
| @@ -618,7 +618,7 @@ Remark: | @@ -618,7 +618,7 @@ Remark: | ||
| 618 | * v0.8, 2013-12-08, support multiple http hooks for a event. | 618 | * v0.8, 2013-12-08, support multiple http hooks for a event. |
| 619 | * v0.8, 2013-12-07, support http callback hooks, on_connect. | 619 | * v0.8, 2013-12-07, support http callback hooks, on_connect. |
| 620 | * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. | 620 | * v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4. |
| 621 | -* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use SRS rtmp sdk. | 621 | +* v0.8, 2013-12-07, update http/hls/rtmp load test tool [SB](https://github.com/simple-rtmp-server/srs-bench), use SRS rtmp sdk. |
| 622 | * v0.8, 2013-12-06, support max_connections, drop if exceed. | 622 | * v0.8, 2013-12-06, support max_connections, drop if exceed. |
| 623 | * v0.8, 2013-12-05, support log_dir, write ffmpeg log to file. | 623 | * v0.8, 2013-12-05, support log_dir, write ffmpeg log to file. |
| 624 | * v0.8, 2013-12-05, fix the forward/hls/encoder bug. | 624 | * v0.8, 2013-12-05, fix the forward/hls/encoder bug. |
| @@ -676,9 +676,9 @@ Performance benchmark history, on virtual box. | @@ -676,9 +676,9 @@ Performance benchmark history, on virtual box. | ||
| 676 | * See also: [Performance for x86/x64 Test Guide](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance) | 676 | * See also: [Performance for x86/x64 Test Guide](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance) |
| 677 | * See also: [Performance for RaspberryPi](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_RaspberryPi) | 677 | * See also: [Performance for RaspberryPi](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_RaspberryPi) |
| 678 | 678 | ||
| 679 | -### Play benchmark | 679 | +### Play RTMP benchmark |
| 680 | 680 | ||
| 681 | -The play benchmark by [st-load](https://github.com/winlinvip/st-load): | 681 | +The play RTMP benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench): |
| 682 | 682 | ||
| 683 | 683 | ||
| 684 | | Update | SRS | Clients | Type | CPU | Memory | Commit | | 684 | | Update | SRS | Clients | Type | CPU | Memory | Commit | |
| @@ -696,9 +696,9 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): | @@ -696,9 +696,9 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load): | ||
| 696 | | 2014-12-05 | 2.0.57 | 9.0k(9000) | players | 90% | 468MB | [code][p11] | | 696 | | 2014-12-05 | 2.0.57 | 9.0k(9000) | players | 90% | 468MB | [code][p11] | |
| 697 | | 2014-12-07 | 2.0.67 | 10k(10000) | players | 95% | 656MB | [code][p12] | | 697 | | 2014-12-07 | 2.0.67 | 10k(10000) | players | 95% | 656MB | [code][p12] | |
| 698 | 698 | ||
| 699 | -### Publish benchmark | 699 | +### Publish RTMP benchmark |
| 700 | 700 | ||
| 701 | -The publish benchmark by [st-load](https://github.com/winlinvip/st-load): | 701 | +The publish RTMP benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench): |
| 702 | 702 | ||
| 703 | | Update | SRS | Clients | Type | CPU | Memory | Commit | | 703 | | Update | SRS | Clients | Type | CPU | Memory | Commit | |
| 704 | | ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ | | 704 | | ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ | |
| @@ -712,6 +712,19 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): | @@ -712,6 +712,19 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load): | ||
| 712 | | 2014-12-04 | 2.0.51 | 2.5k(2500) | publishers | 91% | 259MB | [code][p4] | | 712 | | 2014-12-04 | 2.0.51 | 2.5k(2500) | publishers | 91% | 259MB | [code][p4] | |
| 713 | | 2014-12-04 | 2.0.52 | 4.0k(4000) | publishers | 80% | 331MB | [code][p5] | | 713 | | 2014-12-04 | 2.0.52 | 4.0k(4000) | publishers | 80% | 331MB | [code][p5] | |
| 714 | 714 | ||
| 715 | +### Play HTTP FLV benchmark | ||
| 716 | + | ||
| 717 | +The play HTTP FLV benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench): | ||
| 718 | + | ||
| 719 | + | ||
| 720 | +| Update | SRS | Clients | Type | CPU | Memory | Commit | | ||
| 721 | +| ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ | | ||
| 722 | +| 2014-05-24 | 2.0.167 | 1.0k(1000) | players | 82% | 86MB | - | | ||
| 723 | +| 2014-05-24 | 2.0.168 | 2.3k(2300) | players | 92% | 276MB | [code][p17] | | ||
| 724 | +| 2014-05-24 | 2.0.169 | 3.0k(3000) | players | 94% | 188MB | [code][p18] | | ||
| 725 | +| 2014-05-24 | 2.0.170 | 3.0k(3000) | players | 89% | 96MB | [code][p19] | | ||
| 726 | +| 2014-05-25 | 2.0.171 | 6.0k(6000) | players | 84% | 297MB | [code][p20] | | ||
| 727 | + | ||
| 715 | ### Latency benchmark | 728 | ### Latency benchmark |
| 716 | 729 | ||
| 717 | The latency between encoder and player with realtime config( | 730 | The latency between encoder and player with realtime config( |
| @@ -826,6 +839,10 @@ Winlin | @@ -826,6 +839,10 @@ Winlin | ||
| 826 | [p14]: https://github.com/simple-rtmp-server/srs/commit/10297fab519811845b549a8af40a6bcbd23411e8 | 839 | [p14]: https://github.com/simple-rtmp-server/srs/commit/10297fab519811845b549a8af40a6bcbd23411e8 |
| 827 | [p15]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc | 840 | [p15]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc |
| 828 | [p16]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc | 841 | [p16]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc |
| 842 | +[p17]: https://github.com/simple-rtmp-server/srs/commit/fc995473eb02c7cf64b5b212b456e11f34aa7984 | ||
| 843 | +[p18]: https://github.com/simple-rtmp-server/srs/commit/960341b9b2b9646270ccfd113b4dd784d9826c73 | ||
| 844 | +[p19]: https://github.com/simple-rtmp-server/srs/commit/4df19ba99a4e4d80cd89b304f9298d343497bec9 | ||
| 845 | +[p20]: https://github.com/simple-rtmp-server/srs/commit/d12fc7fcc5b2e9e3c8ee5c7da01d0e41c8f8ca4a | ||
| 829 | 846 | ||
| 830 | [authors]: https://github.com/simple-rtmp-server/srs/blob/develop/AUTHORS.txt | 847 | [authors]: https://github.com/simple-rtmp-server/srs/blob/develop/AUTHORS.txt |
| 831 | [bigthanks]: https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Product#bigthanks | 848 | [bigthanks]: https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Product#bigthanks |
| @@ -66,11 +66,14 @@ SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io) | @@ -66,11 +66,14 @@ SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io) | ||
| 66 | content_length = -1; | 66 | content_length = -1; |
| 67 | written = 0; | 67 | written = 0; |
| 68 | header_sent = false; | 68 | header_sent = false; |
| 69 | + nb_iovss_cache = 0; | ||
| 70 | + iovss_cache = NULL; | ||
| 69 | } | 71 | } |
| 70 | 72 | ||
| 71 | SrsHttpResponseWriter::~SrsHttpResponseWriter() | 73 | SrsHttpResponseWriter::~SrsHttpResponseWriter() |
| 72 | { | 74 | { |
| 73 | srs_freep(hdr); | 75 | srs_freep(hdr); |
| 76 | + srs_freep(iovss_cache); | ||
| 74 | } | 77 | } |
| 75 | 78 | ||
| 76 | int SrsHttpResponseWriter::final_request() | 79 | int SrsHttpResponseWriter::final_request() |
| @@ -98,8 +101,14 @@ int SrsHttpResponseWriter::write(char* data, int size) | @@ -98,8 +101,14 @@ int SrsHttpResponseWriter::write(char* data, int size) | ||
| 98 | 101 | ||
| 99 | if (!header_wrote) { | 102 | if (!header_wrote) { |
| 100 | write_header(SRS_CONSTS_HTTP_OK); | 103 | write_header(SRS_CONSTS_HTTP_OK); |
| 104 | + | ||
| 105 | + if ((ret = send_header(data, size)) != ERROR_SUCCESS) { | ||
| 106 | + srs_error("http: send header failed. ret=%d", ret); | ||
| 107 | + return ret; | ||
| 108 | + } | ||
| 101 | } | 109 | } |
| 102 | 110 | ||
| 111 | + // check the bytes send and content length. | ||
| 103 | written += size; | 112 | written += size; |
| 104 | if (content_length != -1 && written > content_length) { | 113 | if (content_length != -1 && written > content_length) { |
| 105 | ret = ERROR_HTTP_CONTENT_LENGTH; | 114 | ret = ERROR_HTTP_CONTENT_LENGTH; |
| @@ -107,11 +116,6 @@ int SrsHttpResponseWriter::write(char* data, int size) | @@ -107,11 +116,6 @@ int SrsHttpResponseWriter::write(char* data, int size) | ||
| 107 | return ret; | 116 | return ret; |
| 108 | } | 117 | } |
| 109 | 118 | ||
| 110 | - if ((ret = send_header(data, size)) != ERROR_SUCCESS) { | ||
| 111 | - srs_error("http: send header failed. ret=%d", ret); | ||
| 112 | - return ret; | ||
| 113 | - } | ||
| 114 | - | ||
| 115 | // ignore NULL content. | 119 | // ignore NULL content. |
| 116 | if (!data) { | 120 | if (!data) { |
| 117 | return ret; | 121 | return ret; |
| @@ -123,19 +127,107 @@ int SrsHttpResponseWriter::write(char* data, int size) | @@ -123,19 +127,107 @@ int SrsHttpResponseWriter::write(char* data, int size) | ||
| 123 | } | 127 | } |
| 124 | 128 | ||
| 125 | // send in chunked encoding. | 129 | // send in chunked encoding. |
| 126 | - std::stringstream ss; | ||
| 127 | - ss << hex << size << SRS_HTTP_CRLF; | ||
| 128 | - std::string ch = ss.str(); | ||
| 129 | - if ((ret = skt->write((void*)ch.data(), (int)ch.length(), NULL)) != ERROR_SUCCESS) { | 130 | + int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); |
| 131 | + | ||
| 132 | + iovec iovs[4]; | ||
| 133 | + iovs[0].iov_base = (char*)header_cache; | ||
| 134 | + iovs[0].iov_len = (int)nb_size; | ||
| 135 | + iovs[1].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 136 | + iovs[1].iov_len = 2; | ||
| 137 | + iovs[2].iov_base = (char*)data; | ||
| 138 | + iovs[2].iov_len = size; | ||
| 139 | + iovs[3].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 140 | + iovs[3].iov_len = 2; | ||
| 141 | + | ||
| 142 | + ssize_t nwrite; | ||
| 143 | + if ((ret = skt->writev(iovs, 4, &nwrite)) != ERROR_SUCCESS) { | ||
| 130 | return ret; | 144 | return ret; |
| 131 | } | 145 | } |
| 132 | - if ((ret = skt->write((void*)data, size, NULL)) != ERROR_SUCCESS) { | 146 | + |
| 147 | + return ret; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | ||
| 151 | +{ | ||
| 152 | + int ret = ERROR_SUCCESS; | ||
| 153 | + | ||
| 154 | + // when header not ready, or not chunked, send one by one. | ||
| 155 | + if (!header_wrote || content_length != -1) { | ||
| 156 | + ssize_t nwrite = 0; | ||
| 157 | + for (int i = 0; i < iovcnt; i++) { | ||
| 158 | + iovec* piovc = iov + i; | ||
| 159 | + nwrite += piovc->iov_len; | ||
| 160 | + if ((ret = write((char*)piovc->iov_base, (int)piovc->iov_len)) != ERROR_SUCCESS) { | ||
| 161 | + return ret; | ||
| 162 | + } | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + if (pnwrite) { | ||
| 166 | + *pnwrite = nwrite; | ||
| 167 | + } | ||
| 168 | + | ||
| 133 | return ret; | 169 | return ret; |
| 134 | } | 170 | } |
| 135 | - if ((ret = skt->write((void*)SRS_HTTP_CRLF, 2, NULL)) != ERROR_SUCCESS) { | 171 | + |
| 172 | + // ignore NULL content. | ||
| 173 | + if (iovcnt <= 0) { | ||
| 136 | return ret; | 174 | return ret; |
| 137 | } | 175 | } |
| 138 | 176 | ||
| 177 | + // send in chunked encoding. | ||
| 178 | + int nb_iovss = 3 + iovcnt; | ||
| 179 | + iovec* iovss = iovss_cache; | ||
| 180 | + if (nb_iovss_cache < nb_iovss) { | ||
| 181 | + srs_freep(iovss_cache); | ||
| 182 | + nb_iovss_cache = nb_iovss; | ||
| 183 | + iovss = iovss_cache = new iovec[nb_iovss]; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + // send in chunked encoding. | ||
| 187 | + | ||
| 188 | + // chunk size. | ||
| 189 | + int size = 0; | ||
| 190 | + for (int i = 0; i < iovcnt; i++) { | ||
| 191 | + iovec* data_iov = iov + i; | ||
| 192 | + size += data_iov->iov_len; | ||
| 193 | + } | ||
| 194 | + written += size; | ||
| 195 | + | ||
| 196 | + // chunk header | ||
| 197 | + int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); | ||
| 198 | + iovec* iovs = iovss; | ||
| 199 | + iovs[0].iov_base = (char*)header_cache; | ||
| 200 | + iovs[0].iov_len = (int)nb_size; | ||
| 201 | + iovs++; | ||
| 202 | + | ||
| 203 | + // chunk header eof. | ||
| 204 | + iovs[0].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 205 | + iovs[0].iov_len = 2; | ||
| 206 | + iovs++; | ||
| 207 | + | ||
| 208 | + // chunk body. | ||
| 209 | + for (int i = 0; i < iovcnt; i++) { | ||
| 210 | + iovec* data_iov = iov + i; | ||
| 211 | + iovs[0].iov_base = (char*)data_iov->iov_base; | ||
| 212 | + iovs[0].iov_len = (int)data_iov->iov_len; | ||
| 213 | + iovs++; | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // chunk body eof. | ||
| 217 | + iovs[0].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 218 | + iovs[0].iov_len = 2; | ||
| 219 | + iovs++; | ||
| 220 | + | ||
| 221 | + // sendout all ioves. | ||
| 222 | + ssize_t nwrite; | ||
| 223 | + if ((ret = srs_write_large_iovs(skt, iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) { | ||
| 224 | + return ret; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + if (pnwrite) { | ||
| 228 | + *pnwrite = nwrite; | ||
| 229 | + } | ||
| 230 | + | ||
| 139 | return ret; | 231 | return ret; |
| 140 | } | 232 | } |
| 141 | 233 | ||
| @@ -1365,6 +1457,21 @@ int SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/) | @@ -1365,6 +1457,21 @@ int SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/) | ||
| 1365 | return ERROR_SUCCESS; | 1457 | return ERROR_SUCCESS; |
| 1366 | } | 1458 | } |
| 1367 | 1459 | ||
| 1460 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 1461 | +SrsFastFlvStreamEncoder::SrsFastFlvStreamEncoder() | ||
| 1462 | +{ | ||
| 1463 | +} | ||
| 1464 | + | ||
| 1465 | +SrsFastFlvStreamEncoder::~SrsFastFlvStreamEncoder() | ||
| 1466 | +{ | ||
| 1467 | +} | ||
| 1468 | + | ||
| 1469 | +int SrsFastFlvStreamEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) | ||
| 1470 | +{ | ||
| 1471 | + return enc->write_tags(msgs, count); | ||
| 1472 | +} | ||
| 1473 | +#endif | ||
| 1474 | + | ||
| 1368 | SrsAacStreamEncoder::SrsAacStreamEncoder() | 1475 | SrsAacStreamEncoder::SrsAacStreamEncoder() |
| 1369 | { | 1476 | { |
| 1370 | enc = new SrsAacEncoder(); | 1477 | enc = new SrsAacEncoder(); |
| @@ -1509,6 +1616,11 @@ int SrsStreamWriter::write(void* buf, size_t count, ssize_t* pnwrite) | @@ -1509,6 +1616,11 @@ int SrsStreamWriter::write(void* buf, size_t count, ssize_t* pnwrite) | ||
| 1509 | return writer->write((char*)buf, (int)count); | 1616 | return writer->write((char*)buf, (int)count); |
| 1510 | } | 1617 | } |
| 1511 | 1618 | ||
| 1619 | +int SrsStreamWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | ||
| 1620 | +{ | ||
| 1621 | + return writer->writev(iov, iovcnt, pnwrite); | ||
| 1622 | +} | ||
| 1623 | + | ||
| 1512 | SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c) | 1624 | SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c) |
| 1513 | { | 1625 | { |
| 1514 | source = s; | 1626 | source = s; |
| @@ -1530,7 +1642,11 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1530,7 +1642,11 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1530 | srs_assert(entry); | 1642 | srs_assert(entry); |
| 1531 | if (srs_string_ends_with(entry->pattern, ".flv")) { | 1643 | if (srs_string_ends_with(entry->pattern, ".flv")) { |
| 1532 | w->header()->set_content_type("video/x-flv"); | 1644 | w->header()->set_content_type("video/x-flv"); |
| 1645 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 1646 | + enc = new SrsFastFlvStreamEncoder(); | ||
| 1647 | +#else | ||
| 1533 | enc = new SrsFlvStreamEncoder(); | 1648 | enc = new SrsFlvStreamEncoder(); |
| 1649 | +#endif | ||
| 1534 | } else if (srs_string_ends_with(entry->pattern, ".aac")) { | 1650 | } else if (srs_string_ends_with(entry->pattern, ".aac")) { |
| 1535 | w->header()->set_content_type("audio/x-aac"); | 1651 | w->header()->set_content_type("audio/x-aac"); |
| 1536 | enc = new SrsAacStreamEncoder(); | 1652 | enc = new SrsAacStreamEncoder(); |
| @@ -1576,6 +1692,10 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1576,6 +1692,10 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1576 | } | 1692 | } |
| 1577 | } | 1693 | } |
| 1578 | 1694 | ||
| 1695 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 1696 | + SrsFastFlvStreamEncoder* ffe = dynamic_cast<SrsFastFlvStreamEncoder*>(enc); | ||
| 1697 | +#endif | ||
| 1698 | + | ||
| 1579 | while (true) { | 1699 | while (true) { |
| 1580 | pprint->elapse(); | 1700 | pprint->elapse(); |
| 1581 | 1701 | ||
| @@ -1602,7 +1722,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1602,7 +1722,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1602 | } | 1722 | } |
| 1603 | 1723 | ||
| 1604 | // sendout all messages. | 1724 | // sendout all messages. |
| 1725 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 1726 | + if (ffe) { | ||
| 1727 | + ret = ffe->write_tags(msgs.msgs, count); | ||
| 1728 | + } else { | ||
| 1729 | + ret = streaming_send_messages(enc, msgs.msgs, count); | ||
| 1730 | + } | ||
| 1731 | +#else | ||
| 1605 | ret = streaming_send_messages(enc, msgs.msgs, count); | 1732 | ret = streaming_send_messages(enc, msgs.msgs, count); |
| 1733 | +#endif | ||
| 1606 | 1734 | ||
| 1607 | // free the messages. | 1735 | // free the messages. |
| 1608 | for (int i = 0; i < count; i++) { | 1736 | for (int i = 0; i < count; i++) { |
| @@ -71,6 +71,10 @@ class SrsHttpMessage; | @@ -71,6 +71,10 @@ class SrsHttpMessage; | ||
| 71 | 71 | ||
| 72 | #ifdef SRS_AUTO_HTTP_PARSER | 72 | #ifdef SRS_AUTO_HTTP_PARSER |
| 73 | 73 | ||
| 74 | +// the http chunked header size, | ||
| 75 | +// for writev, there always one chunk to send it. | ||
| 76 | +#define SRS_HTTP_HEADER_CACHE_SIZE 64 | ||
| 77 | + | ||
| 74 | /** | 78 | /** |
| 75 | * response writer use st socket | 79 | * response writer use st socket |
| 76 | */ | 80 | */ |
| @@ -80,6 +84,10 @@ private: | @@ -80,6 +84,10 @@ private: | ||
| 80 | SrsStSocket* skt; | 84 | SrsStSocket* skt; |
| 81 | SrsHttpHeader* hdr; | 85 | SrsHttpHeader* hdr; |
| 82 | private: | 86 | private: |
| 87 | + char header_cache[SRS_HTTP_HEADER_CACHE_SIZE]; | ||
| 88 | + iovec* iovss_cache; | ||
| 89 | + int nb_iovss_cache; | ||
| 90 | +private: | ||
| 83 | // reply header has been (logically) written | 91 | // reply header has been (logically) written |
| 84 | bool header_wrote; | 92 | bool header_wrote; |
| 85 | // status code passed to WriteHeader | 93 | // status code passed to WriteHeader |
| @@ -102,6 +110,7 @@ public: | @@ -102,6 +110,7 @@ public: | ||
| 102 | virtual int final_request(); | 110 | virtual int final_request(); |
| 103 | virtual SrsHttpHeader* header(); | 111 | virtual SrsHttpHeader* header(); |
| 104 | virtual int write(char* data, int size); | 112 | virtual int write(char* data, int size); |
| 113 | + virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite); | ||
| 105 | virtual void write_header(int code); | 114 | virtual void write_header(int code); |
| 106 | virtual int send_header(char* data, int size); | 115 | virtual int send_header(char* data, int size); |
| 107 | }; | 116 | }; |
| @@ -203,8 +212,8 @@ public: | @@ -203,8 +212,8 @@ public: | ||
| 203 | * set the original messages, then update the message. | 212 | * set the original messages, then update the message. |
| 204 | */ | 213 | */ |
| 205 | virtual int update(std::string url, http_parser* header, | 214 | virtual int update(std::string url, http_parser* header, |
| 206 | - SrsFastBuffer* body, std::vector<SrsHttpHeaderField>& headers | ||
| 207 | - ); | 215 | + SrsFastBuffer* body, std::vector<SrsHttpHeaderField>& headers |
| 216 | + ); | ||
| 208 | private: | 217 | private: |
| 209 | virtual SrsConnection* connection(); | 218 | virtual SrsConnection* connection(); |
| 210 | public: | 219 | public: |
| @@ -443,7 +452,7 @@ public: | @@ -443,7 +452,7 @@ public: | ||
| 443 | */ | 452 | */ |
| 444 | class SrsFlvStreamEncoder : public ISrsStreamEncoder | 453 | class SrsFlvStreamEncoder : public ISrsStreamEncoder |
| 445 | { | 454 | { |
| 446 | -private: | 455 | +protected: |
| 447 | SrsFlvEncoder* enc; | 456 | SrsFlvEncoder* enc; |
| 448 | public: | 457 | public: |
| 449 | SrsFlvStreamEncoder(); | 458 | SrsFlvStreamEncoder(); |
| @@ -458,6 +467,24 @@ public: | @@ -458,6 +467,24 @@ public: | ||
| 458 | virtual int dump_cache(SrsConsumer* consumer); | 467 | virtual int dump_cache(SrsConsumer* consumer); |
| 459 | }; | 468 | }; |
| 460 | 469 | ||
| 470 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 471 | +/** | ||
| 472 | + * the fast flv stream encoder. | ||
| 473 | + * @see https://github.com/simple-rtmp-server/srs/issues/405 | ||
| 474 | + */ | ||
| 475 | +class SrsFastFlvStreamEncoder : public SrsFlvStreamEncoder | ||
| 476 | +{ | ||
| 477 | +public: | ||
| 478 | + SrsFastFlvStreamEncoder(); | ||
| 479 | + virtual ~SrsFastFlvStreamEncoder(); | ||
| 480 | +public: | ||
| 481 | + /** | ||
| 482 | + * write the tags in a time. | ||
| 483 | + */ | ||
| 484 | + virtual int write_tags(SrsSharedPtrMessage** msgs, int count); | ||
| 485 | +}; | ||
| 486 | +#endif | ||
| 487 | + | ||
| 461 | /** | 488 | /** |
| 462 | * the ts stream encoder, remux rtmp stream to ts stream. | 489 | * the ts stream encoder, remux rtmp stream to ts stream. |
| 463 | */ | 490 | */ |
| @@ -538,6 +565,7 @@ public: | @@ -538,6 +565,7 @@ public: | ||
| 538 | virtual int64_t tellg(); | 565 | virtual int64_t tellg(); |
| 539 | public: | 566 | public: |
| 540 | virtual int write(void* buf, size_t count, ssize_t* pnwrite); | 567 | virtual int write(void* buf, size_t count, ssize_t* pnwrite); |
| 568 | + virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite); | ||
| 541 | }; | 569 | }; |
| 542 | 570 | ||
| 543 | /** | 571 | /** |
| @@ -181,5 +181,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -181,5 +181,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 181 | #undef SRS_PERF_SO_SNDBUF_SIZE | 181 | #undef SRS_PERF_SO_SNDBUF_SIZE |
| 182 | #endif | 182 | #endif |
| 183 | 183 | ||
| 184 | +/** | ||
| 185 | + * define the following macro to enable the fast flv encoder. | ||
| 186 | + * @see https://github.com/simple-rtmp-server/srs/issues/405 | ||
| 187 | + */ | ||
| 188 | +#undef SRS_PERF_FAST_FLV_ENCODER | ||
| 189 | +#define SRS_PERF_FAST_FLV_ENCODER | ||
| 190 | + | ||
| 184 | #endif | 191 | #endif |
| 185 | 192 |
| @@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 30 | 30 | ||
| 31 | #include <fcntl.h> | 31 | #include <fcntl.h> |
| 32 | #include <sstream> | 32 | #include <sstream> |
| 33 | +#include <sys/uio.h> | ||
| 33 | using namespace std; | 34 | using namespace std; |
| 34 | 35 | ||
| 35 | #include <srs_kernel_log.hpp> | 36 | #include <srs_kernel_log.hpp> |
| @@ -145,6 +146,27 @@ int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) | @@ -145,6 +146,27 @@ int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) | ||
| 145 | return ret; | 146 | return ret; |
| 146 | } | 147 | } |
| 147 | 148 | ||
| 149 | +int SrsFileWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | ||
| 150 | +{ | ||
| 151 | + int ret = ERROR_SUCCESS; | ||
| 152 | + | ||
| 153 | + ssize_t nwrite = 0; | ||
| 154 | + for (int i = 0; i < iovcnt; i++) { | ||
| 155 | + iovec* piov = iov + i; | ||
| 156 | + ssize_t this_nwrite = 0; | ||
| 157 | + if ((ret = write(piov->iov_base, piov->iov_len, &this_nwrite)) != ERROR_SUCCESS) { | ||
| 158 | + return ret; | ||
| 159 | + } | ||
| 160 | + nwrite += this_nwrite; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + if (pnwrite) { | ||
| 164 | + *pnwrite = nwrite; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + return ret; | ||
| 168 | +} | ||
| 169 | + | ||
| 148 | SrsFileReader::SrsFileReader() | 170 | SrsFileReader::SrsFileReader() |
| 149 | { | 171 | { |
| 150 | fd = -1; | 172 | fd = -1; |
| @@ -31,6 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,6 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | 31 | ||
| 32 | #include <string> | 32 | #include <string> |
| 33 | 33 | ||
| 34 | +// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 35 | +#ifndef _WIN32 | ||
| 36 | +#include <sys/uio.h> | ||
| 37 | +#endif | ||
| 38 | + | ||
| 34 | /** | 39 | /** |
| 35 | * file writer, to write to file. | 40 | * file writer, to write to file. |
| 36 | */ | 41 | */ |
| @@ -62,6 +67,11 @@ public: | @@ -62,6 +67,11 @@ public: | ||
| 62 | * @param pnwrite the output nb_write, NULL to ignore. | 67 | * @param pnwrite the output nb_write, NULL to ignore. |
| 63 | */ | 68 | */ |
| 64 | virtual int write(void* buf, size_t count, ssize_t* pnwrite); | 69 | virtual int write(void* buf, size_t count, ssize_t* pnwrite); |
| 70 | + /** | ||
| 71 | + * for the HTTP FLV, to writev to improve performance. | ||
| 72 | + * @see https://github.com/simple-rtmp-server/srs/issues/405 | ||
| 73 | + */ | ||
| 74 | + virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite); | ||
| 65 | }; | 75 | }; |
| 66 | 76 | ||
| 67 | /** | 77 | /** |
| @@ -37,19 +37,304 @@ using namespace std; | @@ -37,19 +37,304 @@ using namespace std; | ||
| 37 | #include <srs_kernel_stream.hpp> | 37 | #include <srs_kernel_stream.hpp> |
| 38 | #include <srs_kernel_file.hpp> | 38 | #include <srs_kernel_file.hpp> |
| 39 | #include <srs_kernel_codec.hpp> | 39 | #include <srs_kernel_codec.hpp> |
| 40 | +#include <srs_kernel_utility.hpp> | ||
| 40 | 41 | ||
| 41 | -#define SRS_FLV_TAG_HEADER_SIZE 11 | ||
| 42 | -#define SRS_FLV_PREVIOUS_TAG_SIZE 4 | 42 | +SrsMessageHeader::SrsMessageHeader() |
| 43 | +{ | ||
| 44 | + message_type = 0; | ||
| 45 | + payload_length = 0; | ||
| 46 | + timestamp_delta = 0; | ||
| 47 | + stream_id = 0; | ||
| 48 | + | ||
| 49 | + timestamp = 0; | ||
| 50 | + // we always use the connection chunk-id | ||
| 51 | + perfer_cid = RTMP_CID_OverConnection; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +SrsMessageHeader::~SrsMessageHeader() | ||
| 55 | +{ | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +bool SrsMessageHeader::is_audio() | ||
| 59 | +{ | ||
| 60 | + return message_type == RTMP_MSG_AudioMessage; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +bool SrsMessageHeader::is_video() | ||
| 64 | +{ | ||
| 65 | + return message_type == RTMP_MSG_VideoMessage; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +bool SrsMessageHeader::is_amf0_command() | ||
| 69 | +{ | ||
| 70 | + return message_type == RTMP_MSG_AMF0CommandMessage; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +bool SrsMessageHeader::is_amf0_data() | ||
| 74 | +{ | ||
| 75 | + return message_type == RTMP_MSG_AMF0DataMessage; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +bool SrsMessageHeader::is_amf3_command() | ||
| 79 | +{ | ||
| 80 | + return message_type == RTMP_MSG_AMF3CommandMessage; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +bool SrsMessageHeader::is_amf3_data() | ||
| 84 | +{ | ||
| 85 | + return message_type == RTMP_MSG_AMF3DataMessage; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +bool SrsMessageHeader::is_window_ackledgement_size() | ||
| 89 | +{ | ||
| 90 | + return message_type == RTMP_MSG_WindowAcknowledgementSize; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +bool SrsMessageHeader::is_ackledgement() | ||
| 94 | +{ | ||
| 95 | + return message_type == RTMP_MSG_Acknowledgement; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +bool SrsMessageHeader::is_set_chunk_size() | ||
| 99 | +{ | ||
| 100 | + return message_type == RTMP_MSG_SetChunkSize; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +bool SrsMessageHeader::is_user_control_message() | ||
| 104 | +{ | ||
| 105 | + return message_type == RTMP_MSG_UserControlMessage; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +bool SrsMessageHeader::is_set_peer_bandwidth() | ||
| 109 | +{ | ||
| 110 | + return message_type == RTMP_MSG_SetPeerBandwidth; | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +bool SrsMessageHeader::is_aggregate() | ||
| 114 | +{ | ||
| 115 | + return message_type == RTMP_MSG_AggregateMessage; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +void SrsMessageHeader::initialize_amf0_script(int size, int stream) | ||
| 119 | +{ | ||
| 120 | + message_type = RTMP_MSG_AMF0DataMessage; | ||
| 121 | + payload_length = (int32_t)size; | ||
| 122 | + timestamp_delta = (int32_t)0; | ||
| 123 | + timestamp = (int64_t)0; | ||
| 124 | + stream_id = (int32_t)stream; | ||
| 125 | + | ||
| 126 | + // amf0 script use connection2 chunk-id | ||
| 127 | + perfer_cid = RTMP_CID_OverConnection2; | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +void SrsMessageHeader::initialize_audio(int size, u_int32_t time, int stream) | ||
| 131 | +{ | ||
| 132 | + message_type = RTMP_MSG_AudioMessage; | ||
| 133 | + payload_length = (int32_t)size; | ||
| 134 | + timestamp_delta = (int32_t)time; | ||
| 135 | + timestamp = (int64_t)time; | ||
| 136 | + stream_id = (int32_t)stream; | ||
| 137 | + | ||
| 138 | + // audio chunk-id | ||
| 139 | + perfer_cid = RTMP_CID_Audio; | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream) | ||
| 143 | +{ | ||
| 144 | + message_type = RTMP_MSG_VideoMessage; | ||
| 145 | + payload_length = (int32_t)size; | ||
| 146 | + timestamp_delta = (int32_t)time; | ||
| 147 | + timestamp = (int64_t)time; | ||
| 148 | + stream_id = (int32_t)stream; | ||
| 149 | + | ||
| 150 | + // video chunk-id | ||
| 151 | + perfer_cid = RTMP_CID_Video; | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +SrsCommonMessage::SrsCommonMessage() | ||
| 155 | +{ | ||
| 156 | + payload = NULL; | ||
| 157 | + size = 0; | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +SrsCommonMessage::~SrsCommonMessage() | ||
| 161 | +{ | ||
| 162 | + srs_freep(payload); | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() | ||
| 166 | +{ | ||
| 167 | + payload = NULL; | ||
| 168 | + size = 0; | ||
| 169 | + shared_count = 0; | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() | ||
| 173 | +{ | ||
| 174 | + srs_freep(payload); | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +SrsSharedPtrMessage::SrsSharedPtrMessage() | ||
| 178 | +{ | ||
| 179 | + ptr = NULL; | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +SrsSharedPtrMessage::~SrsSharedPtrMessage() | ||
| 183 | +{ | ||
| 184 | + if (ptr) { | ||
| 185 | + if (ptr->shared_count == 0) { | ||
| 186 | + srs_freep(ptr); | ||
| 187 | + } else { | ||
| 188 | + ptr->shared_count--; | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +int SrsSharedPtrMessage::create(SrsCommonMessage* msg) | ||
| 194 | +{ | ||
| 195 | + int ret = ERROR_SUCCESS; | ||
| 196 | + | ||
| 197 | + if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) { | ||
| 198 | + return ret; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + // to prevent double free of payload: | ||
| 202 | + // initialize already attach the payload of msg, | ||
| 203 | + // detach the payload to transfer the owner to shared ptr. | ||
| 204 | + msg->payload = NULL; | ||
| 205 | + msg->size = 0; | ||
| 206 | + | ||
| 207 | + return ret; | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size) | ||
| 211 | +{ | ||
| 212 | + int ret = ERROR_SUCCESS; | ||
| 213 | + | ||
| 214 | + if (ptr) { | ||
| 215 | + ret = ERROR_SYSTEM_ASSERT_FAILED; | ||
| 216 | + srs_error("should not set the payload twice. ret=%d", ret); | ||
| 217 | + srs_assert(false); | ||
| 218 | + | ||
| 219 | + return ret; | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + ptr = new SrsSharedPtrPayload(); | ||
| 223 | + | ||
| 224 | + // direct attach the data. | ||
| 225 | + if (pheader) { | ||
| 226 | + ptr->header.message_type = pheader->message_type; | ||
| 227 | + ptr->header.payload_length = size; | ||
| 228 | + ptr->header.perfer_cid = pheader->perfer_cid; | ||
| 229 | + this->timestamp = pheader->timestamp; | ||
| 230 | + this->stream_id = pheader->stream_id; | ||
| 231 | + } | ||
| 232 | + ptr->payload = payload; | ||
| 233 | + ptr->size = size; | ||
| 234 | + | ||
| 235 | + // message can access it. | ||
| 236 | + this->payload = ptr->payload; | ||
| 237 | + this->size = ptr->size; | ||
| 238 | + | ||
| 239 | + return ret; | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +int SrsSharedPtrMessage::count() | ||
| 243 | +{ | ||
| 244 | + srs_assert(ptr); | ||
| 245 | + return ptr->shared_count; | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +bool SrsSharedPtrMessage::check(int stream_id) | ||
| 249 | +{ | ||
| 250 | + // we donot use the complex basic header, | ||
| 251 | + // ensure the basic header is 1bytes. | ||
| 252 | + if (ptr->header.perfer_cid < 2) { | ||
| 253 | + srs_info("change the chunk_id=%d to default=%d", | ||
| 254 | + ptr->header.perfer_cid, RTMP_CID_ProtocolControl); | ||
| 255 | + ptr->header.perfer_cid = RTMP_CID_ProtocolControl; | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + // we assume that the stream_id in a group must be the same. | ||
| 259 | + if (this->stream_id == stream_id) { | ||
| 260 | + return true; | ||
| 261 | + } | ||
| 262 | + this->stream_id = stream_id; | ||
| 263 | + | ||
| 264 | + return false; | ||
| 265 | +} | ||
| 266 | + | ||
| 267 | +bool SrsSharedPtrMessage::is_av() | ||
| 268 | +{ | ||
| 269 | + return ptr->header.message_type == RTMP_MSG_AudioMessage | ||
| 270 | + || ptr->header.message_type == RTMP_MSG_VideoMessage; | ||
| 271 | +} | ||
| 272 | + | ||
| 273 | +bool SrsSharedPtrMessage::is_audio() | ||
| 274 | +{ | ||
| 275 | + return ptr->header.message_type == RTMP_MSG_AudioMessage; | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +bool SrsSharedPtrMessage::is_video() | ||
| 279 | +{ | ||
| 280 | + return ptr->header.message_type == RTMP_MSG_VideoMessage; | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0) | ||
| 284 | +{ | ||
| 285 | + if (c0) { | ||
| 286 | + return srs_chunk_header_c0( | ||
| 287 | + ptr->header.perfer_cid, timestamp, ptr->header.payload_length, | ||
| 288 | + ptr->header.message_type, stream_id, | ||
| 289 | + cache, nb_cache); | ||
| 290 | + } else { | ||
| 291 | + return srs_chunk_header_c3( | ||
| 292 | + ptr->header.perfer_cid, timestamp, | ||
| 293 | + cache, nb_cache); | ||
| 294 | + } | ||
| 295 | +} | ||
| 296 | + | ||
| 297 | +SrsSharedPtrMessage* SrsSharedPtrMessage::copy() | ||
| 298 | +{ | ||
| 299 | + srs_assert(ptr); | ||
| 300 | + | ||
| 301 | + SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); | ||
| 302 | + | ||
| 303 | + copy->ptr = ptr; | ||
| 304 | + ptr->shared_count++; | ||
| 305 | + | ||
| 306 | + copy->timestamp = timestamp; | ||
| 307 | + copy->stream_id = stream_id; | ||
| 308 | + copy->payload = ptr->payload; | ||
| 309 | + copy->size = ptr->size; | ||
| 310 | + | ||
| 311 | + return copy; | ||
| 312 | +} | ||
| 43 | 313 | ||
| 44 | SrsFlvEncoder::SrsFlvEncoder() | 314 | SrsFlvEncoder::SrsFlvEncoder() |
| 45 | { | 315 | { |
| 46 | _fs = NULL; | 316 | _fs = NULL; |
| 47 | tag_stream = new SrsStream(); | 317 | tag_stream = new SrsStream(); |
| 318 | + | ||
| 319 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 320 | + nb_tag_headers = 0; | ||
| 321 | + tag_headers = NULL; | ||
| 322 | + nb_iovss_cache = 0; | ||
| 323 | + iovss_cache = NULL; | ||
| 324 | + nb_ppts = 0; | ||
| 325 | + ppts = NULL; | ||
| 326 | +#endif | ||
| 48 | } | 327 | } |
| 49 | 328 | ||
| 50 | SrsFlvEncoder::~SrsFlvEncoder() | 329 | SrsFlvEncoder::~SrsFlvEncoder() |
| 51 | { | 330 | { |
| 52 | srs_freep(tag_stream); | 331 | srs_freep(tag_stream); |
| 332 | + | ||
| 333 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 334 | + srs_freep(tag_headers); | ||
| 335 | + srs_freep(iovss_cache); | ||
| 336 | + srs_freep(ppts); | ||
| 337 | +#endif | ||
| 53 | } | 338 | } |
| 54 | 339 | ||
| 55 | int SrsFlvEncoder::initialize(SrsFileWriter* fs) | 340 | int SrsFlvEncoder::initialize(SrsFileWriter* fs) |
| @@ -118,20 +403,9 @@ int SrsFlvEncoder::write_metadata(char type, char* data, int size) | @@ -118,20 +403,9 @@ int SrsFlvEncoder::write_metadata(char type, char* data, int size) | ||
| 118 | 403 | ||
| 119 | srs_assert(data); | 404 | srs_assert(data); |
| 120 | 405 | ||
| 121 | - // 11 bytes tag header | ||
| 122 | - char tag_header[] = { | ||
| 123 | - (char)type, // TagType UB [5], 18 = script data | ||
| 124 | - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 125 | - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 126 | - (char)0x00, // TimestampExtended UI8 | ||
| 127 | - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 128 | - }; | ||
| 129 | - | ||
| 130 | - // write data size. | ||
| 131 | - if ((ret = tag_stream->initialize(tag_header + 1, 3)) != ERROR_SUCCESS) { | 406 | + if ((ret = write_metadata_to_cache(type, data, size, tag_header)) != ERROR_SUCCESS) { |
| 132 | return ret; | 407 | return ret; |
| 133 | } | 408 | } |
| 134 | - tag_stream->write_3bytes(size); | ||
| 135 | 409 | ||
| 136 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | 410 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { |
| 137 | if (!srs_is_client_gracefully_close(ret)) { | 411 | if (!srs_is_client_gracefully_close(ret)) { |
| @@ -149,25 +423,9 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | @@ -149,25 +423,9 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | ||
| 149 | 423 | ||
| 150 | srs_assert(data); | 424 | srs_assert(data); |
| 151 | 425 | ||
| 152 | - timestamp &= 0x7fffffff; | ||
| 153 | - | ||
| 154 | - // 11bytes tag header | ||
| 155 | - char tag_header[] = { | ||
| 156 | - (char)SrsCodecFlvTagAudio, // TagType UB [5], 8 = audio | ||
| 157 | - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 158 | - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 159 | - (char)0x00, // TimestampExtended UI8 | ||
| 160 | - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 161 | - }; | ||
| 162 | - | ||
| 163 | - // write data size. | ||
| 164 | - if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) { | 426 | + if ((ret = write_audio_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) { |
| 165 | return ret; | 427 | return ret; |
| 166 | } | 428 | } |
| 167 | - tag_stream->write_3bytes(size); | ||
| 168 | - tag_stream->write_3bytes((int32_t)timestamp); | ||
| 169 | - // default to little-endian | ||
| 170 | - tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | ||
| 171 | 429 | ||
| 172 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | 430 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { |
| 173 | if (!srs_is_client_gracefully_close(ret)) { | 431 | if (!srs_is_client_gracefully_close(ret)) { |
| @@ -185,25 +443,9 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | @@ -185,25 +443,9 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | ||
| 185 | 443 | ||
| 186 | srs_assert(data); | 444 | srs_assert(data); |
| 187 | 445 | ||
| 188 | - timestamp &= 0x7fffffff; | ||
| 189 | - | ||
| 190 | - // 11bytes tag header | ||
| 191 | - char tag_header[] = { | ||
| 192 | - (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video | ||
| 193 | - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 194 | - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 195 | - (char)0x00, // TimestampExtended UI8 | ||
| 196 | - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 197 | - }; | ||
| 198 | - | ||
| 199 | - // write data size. | ||
| 200 | - if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) { | 446 | + if ((ret = write_video_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) { |
| 201 | return ret; | 447 | return ret; |
| 202 | } | 448 | } |
| 203 | - tag_stream->write_3bytes(size); | ||
| 204 | - tag_stream->write_3bytes((int32_t)timestamp); | ||
| 205 | - // default to little-endian | ||
| 206 | - tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | ||
| 207 | 449 | ||
| 208 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | 450 | if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { |
| 209 | srs_error("write flv video tag failed. ret=%d", ret); | 451 | srs_error("write flv video tag failed. ret=%d", ret); |
| @@ -219,35 +461,212 @@ int SrsFlvEncoder::size_tag(int data_size) | @@ -219,35 +461,212 @@ int SrsFlvEncoder::size_tag(int data_size) | ||
| 219 | return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; | 461 | return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; |
| 220 | } | 462 | } |
| 221 | 463 | ||
| 222 | -int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size) | 464 | +#ifdef SRS_PERF_FAST_FLV_ENCODER |
| 465 | +int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) | ||
| 223 | { | 466 | { |
| 224 | int ret = ERROR_SUCCESS; | 467 | int ret = ERROR_SUCCESS; |
| 225 | 468 | ||
| 226 | - // write tag header. | ||
| 227 | - if ((ret = _fs->write(header, header_size, NULL)) != ERROR_SUCCESS) { | ||
| 228 | - if (!srs_is_client_gracefully_close(ret)) { | ||
| 229 | - srs_error("write flv tag header failed. ret=%d", ret); | 469 | + // realloc the iovss. |
| 470 | + int nb_iovss = 3 * count; | ||
| 471 | + iovec* iovss = iovss_cache; | ||
| 472 | + if (nb_iovss_cache < nb_iovss) { | ||
| 473 | + srs_freep(iovss_cache); | ||
| 474 | + | ||
| 475 | + nb_iovss_cache = nb_iovss; | ||
| 476 | + iovss = iovss_cache = new iovec[nb_iovss]; | ||
| 477 | + } | ||
| 478 | + | ||
| 479 | + // realloc the tag headers. | ||
| 480 | + char* cache = tag_headers; | ||
| 481 | + if (nb_tag_headers < count) { | ||
| 482 | + srs_freep(tag_headers); | ||
| 483 | + | ||
| 484 | + nb_tag_headers = count; | ||
| 485 | + cache = tag_headers = new char[SRS_FLV_TAG_HEADER_SIZE * count]; | ||
| 486 | + } | ||
| 487 | + | ||
| 488 | + // realloc the pts. | ||
| 489 | + char* pts = ppts; | ||
| 490 | + if (nb_ppts < count) { | ||
| 491 | + srs_freep(ppts); | ||
| 492 | + | ||
| 493 | + nb_ppts = count; | ||
| 494 | + pts = ppts = new char[SRS_FLV_PREVIOUS_TAG_SIZE * count]; | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + // the cache is ok, write each messages. | ||
| 498 | + iovec* iovs = iovss; | ||
| 499 | + for (int i = 0; i < count; i++) { | ||
| 500 | + SrsSharedPtrMessage* msg = msgs[i]; | ||
| 501 | + | ||
| 502 | + // cache all flv header. | ||
| 503 | + if (msg->is_audio()) { | ||
| 504 | + if ((ret = write_audio_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) { | ||
| 505 | + return ret; | ||
| 506 | + } | ||
| 507 | + } else if (msg->is_video()) { | ||
| 508 | + if ((ret = write_video_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) { | ||
| 509 | + return ret; | ||
| 510 | + } | ||
| 511 | + } else { | ||
| 512 | + if ((ret = write_metadata_to_cache(SrsCodecFlvTagScript, msg->payload, msg->size, cache)) != ERROR_SUCCESS) { | ||
| 513 | + return ret; | ||
| 514 | + } | ||
| 230 | } | 515 | } |
| 231 | - return ret; | 516 | + |
| 517 | + // cache all pts. | ||
| 518 | + if ((ret = write_pts_to_cache(SRS_FLV_TAG_HEADER_SIZE + msg->size, pts)) != ERROR_SUCCESS) { | ||
| 519 | + return ret; | ||
| 520 | + } | ||
| 521 | + | ||
| 522 | + // all ioves. | ||
| 523 | + iovs[0].iov_base = cache; | ||
| 524 | + iovs[0].iov_len = SRS_FLV_TAG_HEADER_SIZE; | ||
| 525 | + iovs[1].iov_base = msg->payload; | ||
| 526 | + iovs[1].iov_len = msg->size; | ||
| 527 | + iovs[2].iov_base = pts; | ||
| 528 | + iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE; | ||
| 529 | + | ||
| 530 | + // move next. | ||
| 531 | + cache += SRS_FLV_TAG_HEADER_SIZE; | ||
| 532 | + pts += SRS_FLV_PREVIOUS_TAG_SIZE; | ||
| 533 | + iovs += 3; | ||
| 232 | } | 534 | } |
| 233 | 535 | ||
| 234 | - // write tag data. | ||
| 235 | - if ((ret = _fs->write(tag, tag_size, NULL)) != ERROR_SUCCESS) { | 536 | + if ((ret = _fs->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) { |
| 236 | if (!srs_is_client_gracefully_close(ret)) { | 537 | if (!srs_is_client_gracefully_close(ret)) { |
| 237 | - srs_error("write flv tag failed. ret=%d", ret); | 538 | + srs_error("write flv tags failed. ret=%d", ret); |
| 238 | } | 539 | } |
| 239 | return ret; | 540 | return ret; |
| 240 | } | 541 | } |
| 241 | 542 | ||
| 543 | + return ret; | ||
| 544 | +} | ||
| 545 | +#endif | ||
| 546 | + | ||
| 547 | +int SrsFlvEncoder::write_metadata_to_cache(char type, char* data, int size, char* cache) | ||
| 548 | +{ | ||
| 549 | + int ret = ERROR_SUCCESS; | ||
| 550 | + | ||
| 551 | + srs_assert(data); | ||
| 552 | + | ||
| 553 | + // 11 bytes tag header | ||
| 554 | + /*char tag_header[] = { | ||
| 555 | + (char)type, // TagType UB [5], 18 = script data | ||
| 556 | + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 557 | + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 558 | + (char)0x00, // TimestampExtended UI8 | ||
| 559 | + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 560 | + };*/ | ||
| 561 | + | ||
| 562 | + // write data size. | ||
| 563 | + if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) { | ||
| 564 | + return ret; | ||
| 565 | + } | ||
| 566 | + tag_stream->write_1bytes(type); | ||
| 567 | + tag_stream->write_3bytes(size); | ||
| 568 | + tag_stream->write_3bytes(0x00); | ||
| 569 | + tag_stream->write_1bytes(0x00); | ||
| 570 | + tag_stream->write_3bytes(0x00); | ||
| 571 | + | ||
| 572 | + return ret; | ||
| 573 | +} | ||
| 574 | + | ||
| 575 | +int SrsFlvEncoder::write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache) | ||
| 576 | +{ | ||
| 577 | + int ret = ERROR_SUCCESS; | ||
| 578 | + | ||
| 579 | + srs_assert(data); | ||
| 580 | + | ||
| 581 | + timestamp &= 0x7fffffff; | ||
| 582 | + | ||
| 583 | + // 11bytes tag header | ||
| 584 | + /*char tag_header[] = { | ||
| 585 | + (char)SrsCodecFlvTagAudio, // TagType UB [5], 8 = audio | ||
| 586 | + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 587 | + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 588 | + (char)0x00, // TimestampExtended UI8 | ||
| 589 | + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 590 | + };*/ | ||
| 591 | + | ||
| 592 | + // write data size. | ||
| 593 | + if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) { | ||
| 594 | + return ret; | ||
| 595 | + } | ||
| 596 | + tag_stream->write_1bytes(SrsCodecFlvTagAudio); | ||
| 597 | + tag_stream->write_3bytes(size); | ||
| 598 | + tag_stream->write_3bytes((int32_t)timestamp); | ||
| 599 | + // default to little-endian | ||
| 600 | + tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | ||
| 601 | + tag_stream->write_3bytes(0x00); | ||
| 602 | + | ||
| 603 | + return ret; | ||
| 604 | +} | ||
| 605 | + | ||
| 606 | +int SrsFlvEncoder::write_video_to_cache(int64_t timestamp, char* data, int size, char* cache) | ||
| 607 | +{ | ||
| 608 | + int ret = ERROR_SUCCESS; | ||
| 609 | + | ||
| 610 | + srs_assert(data); | ||
| 611 | + | ||
| 612 | + timestamp &= 0x7fffffff; | ||
| 613 | + | ||
| 614 | + // 11bytes tag header | ||
| 615 | + /*char tag_header[] = { | ||
| 616 | + (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video | ||
| 617 | + (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 618 | + (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 619 | + (char)0x00, // TimestampExtended UI8 | ||
| 620 | + (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 621 | + };*/ | ||
| 622 | + | ||
| 623 | + // write data size. | ||
| 624 | + if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) { | ||
| 625 | + return ret; | ||
| 626 | + } | ||
| 627 | + tag_stream->write_1bytes(SrsCodecFlvTagVideo); | ||
| 628 | + tag_stream->write_3bytes(size); | ||
| 629 | + tag_stream->write_3bytes((int32_t)timestamp); | ||
| 630 | + // default to little-endian | ||
| 631 | + tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | ||
| 632 | + tag_stream->write_3bytes(0x00); | ||
| 633 | + | ||
| 634 | + return ret; | ||
| 635 | +} | ||
| 636 | + | ||
| 637 | +int SrsFlvEncoder::write_pts_to_cache(int size, char* cache) | ||
| 638 | +{ | ||
| 639 | + int ret = ERROR_SUCCESS; | ||
| 640 | + | ||
| 641 | + if ((ret = tag_stream->initialize(cache, SRS_FLV_PREVIOUS_TAG_SIZE)) != ERROR_SUCCESS) { | ||
| 642 | + return ret; | ||
| 643 | + } | ||
| 644 | + tag_stream->write_4bytes(size); | ||
| 645 | + | ||
| 646 | + return ret; | ||
| 647 | +} | ||
| 648 | + | ||
| 649 | +int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size) | ||
| 650 | +{ | ||
| 651 | + int ret = ERROR_SUCCESS; | ||
| 652 | + | ||
| 242 | // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes. | 653 | // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes. |
| 243 | char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE]; | 654 | char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE]; |
| 244 | - if ((ret = tag_stream->initialize(pre_size, SRS_FLV_PREVIOUS_TAG_SIZE)) != ERROR_SUCCESS) { | 655 | + if ((ret = write_pts_to_cache(tag_size + header_size, pre_size)) != ERROR_SUCCESS) { |
| 245 | return ret; | 656 | return ret; |
| 246 | } | 657 | } |
| 247 | - tag_stream->write_4bytes(tag_size + header_size); | ||
| 248 | - if ((ret = _fs->write(pre_size, sizeof(pre_size), NULL)) != ERROR_SUCCESS) { | 658 | + |
| 659 | + iovec iovs[3]; | ||
| 660 | + iovs[0].iov_base = header; | ||
| 661 | + iovs[0].iov_len = header_size; | ||
| 662 | + iovs[1].iov_base = tag; | ||
| 663 | + iovs[1].iov_len = tag_size; | ||
| 664 | + iovs[2].iov_base = pre_size; | ||
| 665 | + iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE; | ||
| 666 | + | ||
| 667 | + if ((ret = _fs->writev(iovs, 3, NULL)) != ERROR_SUCCESS) { | ||
| 249 | if (!srs_is_client_gracefully_close(ret)) { | 668 | if (!srs_is_client_gracefully_close(ret)) { |
| 250 | - srs_error("write flv previous tag size failed. ret=%d", ret); | 669 | + srs_error("write flv tag failed. ret=%d", ret); |
| 251 | } | 670 | } |
| 252 | return ret; | 671 | return ret; |
| 253 | } | 672 | } |
| @@ -31,10 +31,399 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,10 +31,399 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | 31 | ||
| 32 | #include <string> | 32 | #include <string> |
| 33 | 33 | ||
| 34 | +// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 35 | +#ifndef _WIN32 | ||
| 36 | +#include <sys/uio.h> | ||
| 37 | +#endif | ||
| 38 | + | ||
| 34 | class SrsStream; | 39 | class SrsStream; |
| 35 | class SrsFileWriter; | 40 | class SrsFileWriter; |
| 36 | class SrsFileReader; | 41 | class SrsFileReader; |
| 37 | 42 | ||
| 43 | +#define SRS_FLV_TAG_HEADER_SIZE 11 | ||
| 44 | +#define SRS_FLV_PREVIOUS_TAG_SIZE 4 | ||
| 45 | + | ||
| 46 | +/**************************************************************************** | ||
| 47 | + ***************************************************************************** | ||
| 48 | + ****************************************************************************/ | ||
| 49 | +/** | ||
| 50 | + 5. Protocol Control Messages | ||
| 51 | + RTMP reserves message type IDs 1-7 for protocol control messages. | ||
| 52 | + These messages contain information needed by the RTM Chunk Stream | ||
| 53 | + protocol or RTMP itself. Protocol messages with IDs 1 & 2 are | ||
| 54 | + reserved for usage with RTM Chunk Stream protocol. Protocol messages | ||
| 55 | + with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID | ||
| 56 | + 7 is used between edge server and origin server. | ||
| 57 | + */ | ||
| 58 | +#define RTMP_MSG_SetChunkSize 0x01 | ||
| 59 | +#define RTMP_MSG_AbortMessage 0x02 | ||
| 60 | +#define RTMP_MSG_Acknowledgement 0x03 | ||
| 61 | +#define RTMP_MSG_UserControlMessage 0x04 | ||
| 62 | +#define RTMP_MSG_WindowAcknowledgementSize 0x05 | ||
| 63 | +#define RTMP_MSG_SetPeerBandwidth 0x06 | ||
| 64 | +#define RTMP_MSG_EdgeAndOriginServerCommand 0x07 | ||
| 65 | +/** | ||
| 66 | + 3. Types of messages | ||
| 67 | + The server and the client send messages over the network to | ||
| 68 | + communicate with each other. The messages can be of any type which | ||
| 69 | + includes audio messages, video messages, command messages, shared | ||
| 70 | + object messages, data messages, and user control messages. | ||
| 71 | + 3.1. Command message | ||
| 72 | + Command messages carry the AMF-encoded commands between the client | ||
| 73 | + and the server. These messages have been assigned message type value | ||
| 74 | + of 20 for AMF0 encoding and message type value of 17 for AMF3 | ||
| 75 | + encoding. These messages are sent to perform some operations like | ||
| 76 | + connect, createStream, publish, play, pause on the peer. Command | ||
| 77 | + messages like onstatus, result etc. are used to inform the sender | ||
| 78 | + about the status of the requested commands. A command message | ||
| 79 | + consists of command name, transaction ID, and command object that | ||
| 80 | + contains related parameters. A client or a server can request Remote | ||
| 81 | + Procedure Calls (RPC) over streams that are communicated using the | ||
| 82 | + command messages to the peer. | ||
| 83 | + */ | ||
| 84 | +#define RTMP_MSG_AMF3CommandMessage 17 // 0x11 | ||
| 85 | +#define RTMP_MSG_AMF0CommandMessage 20 // 0x14 | ||
| 86 | +/** | ||
| 87 | + 3.2. Data message | ||
| 88 | + The client or the server sends this message to send Metadata or any | ||
| 89 | + user data to the peer. Metadata includes details about the | ||
| 90 | + data(audio, video etc.) like creation time, duration, theme and so | ||
| 91 | + on. These messages have been assigned message type value of 18 for | ||
| 92 | + AMF0 and message type value of 15 for AMF3. | ||
| 93 | + */ | ||
| 94 | +#define RTMP_MSG_AMF0DataMessage 18 // 0x12 | ||
| 95 | +#define RTMP_MSG_AMF3DataMessage 15 // 0x0F | ||
| 96 | +/** | ||
| 97 | + 3.3. Shared object message | ||
| 98 | + A shared object is a Flash object (a collection of name value pairs) | ||
| 99 | + that are in synchronization across multiple clients, instances, and | ||
| 100 | + so on. The message types kMsgContainer=19 for AMF0 and | ||
| 101 | + kMsgContainerEx=16 for AMF3 are reserved for shared object events. | ||
| 102 | + Each message can contain multiple events. | ||
| 103 | + */ | ||
| 104 | +#define RTMP_MSG_AMF3SharedObject 16 // 0x10 | ||
| 105 | +#define RTMP_MSG_AMF0SharedObject 19 // 0x13 | ||
| 106 | +/** | ||
| 107 | + 3.4. Audio message | ||
| 108 | + The client or the server sends this message to send audio data to the | ||
| 109 | + peer. The message type value of 8 is reserved for audio messages. | ||
| 110 | + */ | ||
| 111 | +#define RTMP_MSG_AudioMessage 8 // 0x08 | ||
| 112 | +/* * | ||
| 113 | + 3.5. Video message | ||
| 114 | + The client or the server sends this message to send video data to the | ||
| 115 | + peer. The message type value of 9 is reserved for video messages. | ||
| 116 | + These messages are large and can delay the sending of other type of | ||
| 117 | + messages. To avoid such a situation, the video message is assigned | ||
| 118 | + the lowest priority. | ||
| 119 | + */ | ||
| 120 | +#define RTMP_MSG_VideoMessage 9 // 0x09 | ||
| 121 | +/** | ||
| 122 | + 3.6. Aggregate message | ||
| 123 | + An aggregate message is a single message that contains a list of submessages. | ||
| 124 | + The message type value of 22 is reserved for aggregate | ||
| 125 | + messages. | ||
| 126 | + */ | ||
| 127 | +#define RTMP_MSG_AggregateMessage 22 // 0x16 | ||
| 128 | + | ||
| 129 | +/**************************************************************************** | ||
| 130 | + ***************************************************************************** | ||
| 131 | + ****************************************************************************/ | ||
| 132 | +/** | ||
| 133 | + * the chunk stream id used for some under-layer message, | ||
| 134 | + * for example, the PC(protocol control) message. | ||
| 135 | + */ | ||
| 136 | +#define RTMP_CID_ProtocolControl 0x02 | ||
| 137 | +/** | ||
| 138 | + * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection. | ||
| 139 | + * generally use 0x03. | ||
| 140 | + */ | ||
| 141 | +#define RTMP_CID_OverConnection 0x03 | ||
| 142 | +/** | ||
| 143 | + * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection, | ||
| 144 | + * the midst state(we guess). | ||
| 145 | + * rarely used, e.g. onStatus(NetStream.Play.Reset). | ||
| 146 | + */ | ||
| 147 | +#define RTMP_CID_OverConnection2 0x04 | ||
| 148 | +/** | ||
| 149 | + * the stream message(amf0/amf3), over NetStream. | ||
| 150 | + * generally use 0x05. | ||
| 151 | + */ | ||
| 152 | +#define RTMP_CID_OverStream 0x05 | ||
| 153 | +/** | ||
| 154 | + * the stream message(amf0/amf3), over NetStream, the midst state(we guess). | ||
| 155 | + * rarely used, e.g. play("mp4:mystram.f4v") | ||
| 156 | + */ | ||
| 157 | +#define RTMP_CID_OverStream2 0x08 | ||
| 158 | +/** | ||
| 159 | + * the stream message(video), over NetStream | ||
| 160 | + * generally use 0x06. | ||
| 161 | + */ | ||
| 162 | +#define RTMP_CID_Video 0x06 | ||
| 163 | +/** | ||
| 164 | + * the stream message(audio), over NetStream. | ||
| 165 | + * generally use 0x07. | ||
| 166 | + */ | ||
| 167 | +#define RTMP_CID_Audio 0x07 | ||
| 168 | + | ||
| 169 | +/** | ||
| 170 | + * 6.1. Chunk Format | ||
| 171 | + * Extended timestamp: 0 or 4 bytes | ||
| 172 | + * This field MUST be sent when the normal timsestamp is set to | ||
| 173 | + * 0xffffff, it MUST NOT be sent if the normal timestamp is set to | ||
| 174 | + * anything else. So for values less than 0xffffff the normal | ||
| 175 | + * timestamp field SHOULD be used in which case the extended timestamp | ||
| 176 | + * MUST NOT be present. For values greater than or equal to 0xffffff | ||
| 177 | + * the normal timestamp field MUST NOT be used and MUST be set to | ||
| 178 | + * 0xffffff and the extended timestamp MUST be sent. | ||
| 179 | + */ | ||
| 180 | +#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF | ||
| 181 | + | ||
| 182 | +/** | ||
| 183 | + * 4.1. Message Header | ||
| 184 | + */ | ||
| 185 | +class SrsMessageHeader | ||
| 186 | +{ | ||
| 187 | +public: | ||
| 188 | + /** | ||
| 189 | + * 3bytes. | ||
| 190 | + * Three-byte field that contains a timestamp delta of the message. | ||
| 191 | + * @remark, only used for decoding message from chunk stream. | ||
| 192 | + */ | ||
| 193 | + int32_t timestamp_delta; | ||
| 194 | + /** | ||
| 195 | + * 3bytes. | ||
| 196 | + * Three-byte field that represents the size of the payload in bytes. | ||
| 197 | + * It is set in big-endian format. | ||
| 198 | + */ | ||
| 199 | + int32_t payload_length; | ||
| 200 | + /** | ||
| 201 | + * 1byte. | ||
| 202 | + * One byte field to represent the message type. A range of type IDs | ||
| 203 | + * (1-7) are reserved for protocol control messages. | ||
| 204 | + */ | ||
| 205 | + int8_t message_type; | ||
| 206 | + /** | ||
| 207 | + * 4bytes. | ||
| 208 | + * Four-byte field that identifies the stream of the message. These | ||
| 209 | + * bytes are set in little-endian format. | ||
| 210 | + */ | ||
| 211 | + int32_t stream_id; | ||
| 212 | + | ||
| 213 | + /** | ||
| 214 | + * Four-byte field that contains a timestamp of the message. | ||
| 215 | + * The 4 bytes are packed in the big-endian order. | ||
| 216 | + * @remark, used as calc timestamp when decode and encode time. | ||
| 217 | + * @remark, we use 64bits for large time for jitter detect and hls. | ||
| 218 | + */ | ||
| 219 | + int64_t timestamp; | ||
| 220 | +public: | ||
| 221 | + /** | ||
| 222 | + * get the perfered cid(chunk stream id) which sendout over. | ||
| 223 | + * set at decoding, and canbe used for directly send message, | ||
| 224 | + * for example, dispatch to all connections. | ||
| 225 | + */ | ||
| 226 | + int perfer_cid; | ||
| 227 | +public: | ||
| 228 | + SrsMessageHeader(); | ||
| 229 | + virtual ~SrsMessageHeader(); | ||
| 230 | +public: | ||
| 231 | + bool is_audio(); | ||
| 232 | + bool is_video(); | ||
| 233 | + bool is_amf0_command(); | ||
| 234 | + bool is_amf0_data(); | ||
| 235 | + bool is_amf3_command(); | ||
| 236 | + bool is_amf3_data(); | ||
| 237 | + bool is_window_ackledgement_size(); | ||
| 238 | + bool is_ackledgement(); | ||
| 239 | + bool is_set_chunk_size(); | ||
| 240 | + bool is_user_control_message(); | ||
| 241 | + bool is_set_peer_bandwidth(); | ||
| 242 | + bool is_aggregate(); | ||
| 243 | +public: | ||
| 244 | + /** | ||
| 245 | + * create a amf0 script header, set the size and stream_id. | ||
| 246 | + */ | ||
| 247 | + void initialize_amf0_script(int size, int stream); | ||
| 248 | + /** | ||
| 249 | + * create a audio header, set the size, timestamp and stream_id. | ||
| 250 | + */ | ||
| 251 | + void initialize_audio(int size, u_int32_t time, int stream); | ||
| 252 | + /** | ||
| 253 | + * create a video header, set the size, timestamp and stream_id. | ||
| 254 | + */ | ||
| 255 | + void initialize_video(int size, u_int32_t time, int stream); | ||
| 256 | +}; | ||
| 257 | + | ||
| 258 | +/** | ||
| 259 | + * message is raw data RTMP message, bytes oriented, | ||
| 260 | + * protcol always recv RTMP message, and can send RTMP message or RTMP packet. | ||
| 261 | + * the common message is read from underlay protocol sdk. | ||
| 262 | + * while the shared ptr message used to copy and send. | ||
| 263 | + */ | ||
| 264 | +class SrsCommonMessage | ||
| 265 | +{ | ||
| 266 | + // 4.1. Message Header | ||
| 267 | +public: | ||
| 268 | + SrsMessageHeader header; | ||
| 269 | + // 4.2. Message Payload | ||
| 270 | +public: | ||
| 271 | + /** | ||
| 272 | + * current message parsed size, | ||
| 273 | + * size <= header.payload_length | ||
| 274 | + * for the payload maybe sent in multiple chunks. | ||
| 275 | + */ | ||
| 276 | + int size; | ||
| 277 | + /** | ||
| 278 | + * the payload of message, the SrsCommonMessage never know about the detail of payload, | ||
| 279 | + * user must use SrsProtocol.decode_message to get concrete packet. | ||
| 280 | + * @remark, not all message payload can be decoded to packet. for example, | ||
| 281 | + * video/audio packet use raw bytes, no video/audio packet. | ||
| 282 | + */ | ||
| 283 | + char* payload; | ||
| 284 | +public: | ||
| 285 | + SrsCommonMessage(); | ||
| 286 | +public: | ||
| 287 | + virtual ~SrsCommonMessage(); | ||
| 288 | +}; | ||
| 289 | + | ||
| 290 | +/** | ||
| 291 | + * the message header for shared ptr message. | ||
| 292 | + * only the message for all msgs are same. | ||
| 293 | + */ | ||
| 294 | +struct SrsSharedMessageHeader | ||
| 295 | +{ | ||
| 296 | + /** | ||
| 297 | + * 3bytes. | ||
| 298 | + * Three-byte field that represents the size of the payload in bytes. | ||
| 299 | + * It is set in big-endian format. | ||
| 300 | + */ | ||
| 301 | + int32_t payload_length; | ||
| 302 | + /** | ||
| 303 | + * 1byte. | ||
| 304 | + * One byte field to represent the message type. A range of type IDs | ||
| 305 | + * (1-7) are reserved for protocol control messages. | ||
| 306 | + */ | ||
| 307 | + int8_t message_type; | ||
| 308 | + /** | ||
| 309 | + * get the perfered cid(chunk stream id) which sendout over. | ||
| 310 | + * set at decoding, and canbe used for directly send message, | ||
| 311 | + * for example, dispatch to all connections. | ||
| 312 | + */ | ||
| 313 | + int perfer_cid; | ||
| 314 | +}; | ||
| 315 | + | ||
| 316 | +/** | ||
| 317 | + * shared ptr message. | ||
| 318 | + * for audio/video/data message that need less memory copy. | ||
| 319 | + * and only for output. | ||
| 320 | + * | ||
| 321 | + * create first object by constructor and create(), | ||
| 322 | + * use copy if need reference count message. | ||
| 323 | + * | ||
| 324 | + */ | ||
| 325 | +class SrsSharedPtrMessage | ||
| 326 | +{ | ||
| 327 | + // 4.1. Message Header | ||
| 328 | +public: | ||
| 329 | + // the header can shared, only set the timestamp and stream id. | ||
| 330 | + // @see https://github.com/simple-rtmp-server/srs/issues/251 | ||
| 331 | + //SrsSharedMessageHeader header; | ||
| 332 | + /** | ||
| 333 | + * Four-byte field that contains a timestamp of the message. | ||
| 334 | + * The 4 bytes are packed in the big-endian order. | ||
| 335 | + * @remark, used as calc timestamp when decode and encode time. | ||
| 336 | + * @remark, we use 64bits for large time for jitter detect and hls. | ||
| 337 | + */ | ||
| 338 | + int64_t timestamp; | ||
| 339 | + /** | ||
| 340 | + * 4bytes. | ||
| 341 | + * Four-byte field that identifies the stream of the message. These | ||
| 342 | + * bytes are set in big-endian format. | ||
| 343 | + */ | ||
| 344 | + int32_t stream_id; | ||
| 345 | + // 4.2. Message Payload | ||
| 346 | +public: | ||
| 347 | + /** | ||
| 348 | + * current message parsed size, | ||
| 349 | + * size <= header.payload_length | ||
| 350 | + * for the payload maybe sent in multiple chunks. | ||
| 351 | + */ | ||
| 352 | + int size; | ||
| 353 | + /** | ||
| 354 | + * the payload of message, the SrsCommonMessage never know about the detail of payload, | ||
| 355 | + * user must use SrsProtocol.decode_message to get concrete packet. | ||
| 356 | + * @remark, not all message payload can be decoded to packet. for example, | ||
| 357 | + * video/audio packet use raw bytes, no video/audio packet. | ||
| 358 | + */ | ||
| 359 | + char* payload; | ||
| 360 | +private: | ||
| 361 | + class SrsSharedPtrPayload | ||
| 362 | + { | ||
| 363 | + public: | ||
| 364 | + // shared message header. | ||
| 365 | + // @see https://github.com/simple-rtmp-server/srs/issues/251 | ||
| 366 | + SrsSharedMessageHeader header; | ||
| 367 | + // actual shared payload. | ||
| 368 | + char* payload; | ||
| 369 | + // size of payload. | ||
| 370 | + int size; | ||
| 371 | + // the reference count | ||
| 372 | + int shared_count; | ||
| 373 | + public: | ||
| 374 | + SrsSharedPtrPayload(); | ||
| 375 | + virtual ~SrsSharedPtrPayload(); | ||
| 376 | + }; | ||
| 377 | + SrsSharedPtrPayload* ptr; | ||
| 378 | +public: | ||
| 379 | + SrsSharedPtrMessage(); | ||
| 380 | + virtual ~SrsSharedPtrMessage(); | ||
| 381 | +public: | ||
| 382 | + /** | ||
| 383 | + * create shared ptr message, | ||
| 384 | + * copy header, manage the payload of msg, | ||
| 385 | + * set the payload to NULL to prevent double free. | ||
| 386 | + * @remark payload of msg set to NULL if success. | ||
| 387 | + */ | ||
| 388 | + virtual int create(SrsCommonMessage* msg); | ||
| 389 | + /** | ||
| 390 | + * create shared ptr message, | ||
| 391 | + * from the header and payload. | ||
| 392 | + * @remark user should never free the payload. | ||
| 393 | + * @param pheader, the header to copy to the message. NULL to ignore. | ||
| 394 | + */ | ||
| 395 | + virtual int create(SrsMessageHeader* pheader, char* payload, int size); | ||
| 396 | + /** | ||
| 397 | + * get current reference count. | ||
| 398 | + * when this object created, count set to 0. | ||
| 399 | + * if copy() this object, count increase 1. | ||
| 400 | + * if this or copy deleted, free payload when count is 0, or count--. | ||
| 401 | + * @remark, assert object is created. | ||
| 402 | + */ | ||
| 403 | + virtual int count(); | ||
| 404 | + /** | ||
| 405 | + * check perfer cid and stream id. | ||
| 406 | + * @return whether stream id already set. | ||
| 407 | + */ | ||
| 408 | + virtual bool check(int stream_id); | ||
| 409 | +public: | ||
| 410 | + virtual bool is_av(); | ||
| 411 | + virtual bool is_audio(); | ||
| 412 | + virtual bool is_video(); | ||
| 413 | +public: | ||
| 414 | + /** | ||
| 415 | + * generate the chunk header to cache. | ||
| 416 | + * @return the size of header. | ||
| 417 | + */ | ||
| 418 | + virtual int chunk_header(char* cache, int nb_cache, bool c0); | ||
| 419 | +public: | ||
| 420 | + /** | ||
| 421 | + * copy current shared ptr message, use ref-count. | ||
| 422 | + * @remark, assert object is created. | ||
| 423 | + */ | ||
| 424 | + virtual SrsSharedPtrMessage* copy(); | ||
| 425 | +}; | ||
| 426 | + | ||
| 38 | /** | 427 | /** |
| 39 | * encode data to flv file. | 428 | * encode data to flv file. |
| 40 | */ | 429 | */ |
| @@ -44,6 +433,7 @@ private: | @@ -44,6 +433,7 @@ private: | ||
| 44 | SrsFileWriter* _fs; | 433 | SrsFileWriter* _fs; |
| 45 | private: | 434 | private: |
| 46 | SrsStream* tag_stream; | 435 | SrsStream* tag_stream; |
| 436 | + char tag_header[SRS_FLV_TAG_HEADER_SIZE]; | ||
| 47 | public: | 437 | public: |
| 48 | SrsFlvEncoder(); | 438 | SrsFlvEncoder(); |
| 49 | virtual ~SrsFlvEncoder(); | 439 | virtual ~SrsFlvEncoder(); |
| @@ -87,7 +477,28 @@ public: | @@ -87,7 +477,28 @@ public: | ||
| 87 | * @remark assert data_size is not negative. | 477 | * @remark assert data_size is not negative. |
| 88 | */ | 478 | */ |
| 89 | static int size_tag(int data_size); | 479 | static int size_tag(int data_size); |
| 480 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 481 | +private: | ||
| 482 | + // cache tag header. | ||
| 483 | + int nb_tag_headers; | ||
| 484 | + char* tag_headers; | ||
| 485 | + // cache pps(previous tag size) | ||
| 486 | + int nb_ppts; | ||
| 487 | + char* ppts; | ||
| 488 | + // cache iovss. | ||
| 489 | + int nb_iovss_cache; | ||
| 490 | + iovec* iovss_cache; | ||
| 491 | +public: | ||
| 492 | + /** | ||
| 493 | + * write the tags in a time. | ||
| 494 | + */ | ||
| 495 | + virtual int write_tags(SrsSharedPtrMessage** msgs, int count); | ||
| 496 | +#endif | ||
| 90 | private: | 497 | private: |
| 498 | + virtual int write_metadata_to_cache(char type, char* data, int size, char* cache); | ||
| 499 | + virtual int write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache); | ||
| 500 | + virtual int write_video_to_cache(int64_t timestamp, char* data, int size, char* cache); | ||
| 501 | + virtual int write_pts_to_cache(int size, char* cache); | ||
| 91 | virtual int write_tag(char* header, int header_size, char* tag, int tag_size); | 502 | virtual int write_tag(char* header, int header_size, char* tag, int tag_size); |
| 92 | }; | 503 | }; |
| 93 | 504 |
| @@ -40,6 +40,7 @@ using namespace std; | @@ -40,6 +40,7 @@ using namespace std; | ||
| 40 | #include <srs_kernel_log.hpp> | 40 | #include <srs_kernel_log.hpp> |
| 41 | #include <srs_kernel_error.hpp> | 41 | #include <srs_kernel_error.hpp> |
| 42 | #include <srs_kernel_stream.hpp> | 42 | #include <srs_kernel_stream.hpp> |
| 43 | +#include <srs_kernel_flv.hpp> | ||
| 43 | 44 | ||
| 44 | // this value must: | 45 | // this value must: |
| 45 | // equals to (SRS_SYS_CYCLE_INTERVAL*SRS_SYS_TIME_RESOLUTION_MS_TIMES)*1000 | 46 | // equals to (SRS_SYS_CYCLE_INTERVAL*SRS_SYS_TIME_RESOLUTION_MS_TIMES)*1000 |
| @@ -759,3 +760,131 @@ int ff_hex_to_data(u_int8_t* data, const char* p) | @@ -759,3 +760,131 @@ int ff_hex_to_data(u_int8_t* data, const char* p) | ||
| 759 | return len; | 760 | return len; |
| 760 | } | 761 | } |
| 761 | 762 | ||
| 763 | +int srs_chunk_header_c0( | ||
| 764 | + int perfer_cid, u_int32_t timestamp, int32_t payload_length, | ||
| 765 | + int8_t message_type, int32_t stream_id, | ||
| 766 | + char* cache, int nb_cache | ||
| 767 | +) { | ||
| 768 | + // to directly set the field. | ||
| 769 | + char* pp = NULL; | ||
| 770 | + | ||
| 771 | + // generate the header. | ||
| 772 | + char* p = cache; | ||
| 773 | + | ||
| 774 | + // no header. | ||
| 775 | + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { | ||
| 776 | + return 0; | ||
| 777 | + } | ||
| 778 | + | ||
| 779 | + // write new chunk stream header, fmt is 0 | ||
| 780 | + *p++ = 0x00 | (perfer_cid & 0x3F); | ||
| 781 | + | ||
| 782 | + // chunk message header, 11 bytes | ||
| 783 | + // timestamp, 3bytes, big-endian | ||
| 784 | + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { | ||
| 785 | + pp = (char*)×tamp; | ||
| 786 | + *p++ = pp[2]; | ||
| 787 | + *p++ = pp[1]; | ||
| 788 | + *p++ = pp[0]; | ||
| 789 | + } else { | ||
| 790 | + *p++ = 0xFF; | ||
| 791 | + *p++ = 0xFF; | ||
| 792 | + *p++ = 0xFF; | ||
| 793 | + } | ||
| 794 | + | ||
| 795 | + // message_length, 3bytes, big-endian | ||
| 796 | + pp = (char*)&payload_length; | ||
| 797 | + *p++ = pp[2]; | ||
| 798 | + *p++ = pp[1]; | ||
| 799 | + *p++ = pp[0]; | ||
| 800 | + | ||
| 801 | + // message_type, 1bytes | ||
| 802 | + *p++ = message_type; | ||
| 803 | + | ||
| 804 | + // stream_id, 4bytes, little-endian | ||
| 805 | + pp = (char*)&stream_id; | ||
| 806 | + *p++ = pp[0]; | ||
| 807 | + *p++ = pp[1]; | ||
| 808 | + *p++ = pp[2]; | ||
| 809 | + *p++ = pp[3]; | ||
| 810 | + | ||
| 811 | + // for c0 | ||
| 812 | + // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 813 | + // | ||
| 814 | + // for c3: | ||
| 815 | + // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 816 | + // 6.1.3. Extended Timestamp | ||
| 817 | + // This field is transmitted only when the normal time stamp in the | ||
| 818 | + // chunk message header is set to 0x00ffffff. If normal time stamp is | ||
| 819 | + // set to any value less than 0x00ffffff, this field MUST NOT be | ||
| 820 | + // present. This field MUST NOT be present if the timestamp field is not | ||
| 821 | + // present. Type 3 chunks MUST NOT have this field. | ||
| 822 | + // adobe changed for Type3 chunk: | ||
| 823 | + // FMLE always sendout the extended-timestamp, | ||
| 824 | + // must send the extended-timestamp to FMS, | ||
| 825 | + // must send the extended-timestamp to flash-player. | ||
| 826 | + // @see: ngx_rtmp_prepare_message | ||
| 827 | + // @see: http://blog.csdn.net/win_lin/article/details/13363699 | ||
| 828 | + // TODO: FIXME: extract to outer. | ||
| 829 | + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { | ||
| 830 | + pp = (char*)×tamp; | ||
| 831 | + *p++ = pp[3]; | ||
| 832 | + *p++ = pp[2]; | ||
| 833 | + *p++ = pp[1]; | ||
| 834 | + *p++ = pp[0]; | ||
| 835 | + } | ||
| 836 | + | ||
| 837 | + // always has header | ||
| 838 | + return p - cache; | ||
| 839 | +} | ||
| 840 | + | ||
| 841 | +int srs_chunk_header_c3( | ||
| 842 | + int perfer_cid, u_int32_t timestamp, | ||
| 843 | + char* cache, int nb_cache | ||
| 844 | +) { | ||
| 845 | + // to directly set the field. | ||
| 846 | + char* pp = NULL; | ||
| 847 | + | ||
| 848 | + // generate the header. | ||
| 849 | + char* p = cache; | ||
| 850 | + | ||
| 851 | + // no header. | ||
| 852 | + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) { | ||
| 853 | + return 0; | ||
| 854 | + } | ||
| 855 | + | ||
| 856 | + // write no message header chunk stream, fmt is 3 | ||
| 857 | + // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, | ||
| 858 | + // SRS will rollback to 1B chunk header. | ||
| 859 | + *p++ = 0xC0 | (perfer_cid & 0x3F); | ||
| 860 | + | ||
| 861 | + // for c0 | ||
| 862 | + // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 863 | + // | ||
| 864 | + // for c3: | ||
| 865 | + // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 866 | + // 6.1.3. Extended Timestamp | ||
| 867 | + // This field is transmitted only when the normal time stamp in the | ||
| 868 | + // chunk message header is set to 0x00ffffff. If normal time stamp is | ||
| 869 | + // set to any value less than 0x00ffffff, this field MUST NOT be | ||
| 870 | + // present. This field MUST NOT be present if the timestamp field is not | ||
| 871 | + // present. Type 3 chunks MUST NOT have this field. | ||
| 872 | + // adobe changed for Type3 chunk: | ||
| 873 | + // FMLE always sendout the extended-timestamp, | ||
| 874 | + // must send the extended-timestamp to FMS, | ||
| 875 | + // must send the extended-timestamp to flash-player. | ||
| 876 | + // @see: ngx_rtmp_prepare_message | ||
| 877 | + // @see: http://blog.csdn.net/win_lin/article/details/13363699 | ||
| 878 | + // TODO: FIXME: extract to outer. | ||
| 879 | + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { | ||
| 880 | + pp = (char*)×tamp; | ||
| 881 | + *p++ = pp[3]; | ||
| 882 | + *p++ = pp[2]; | ||
| 883 | + *p++ = pp[1]; | ||
| 884 | + *p++ = pp[0]; | ||
| 885 | + } | ||
| 886 | + | ||
| 887 | + // always has header | ||
| 888 | + return p - cache; | ||
| 889 | +} | ||
| 890 | + |
| @@ -138,5 +138,28 @@ extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, i | @@ -138,5 +138,28 @@ extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, i | ||
| 138 | */ | 138 | */ |
| 139 | extern int ff_hex_to_data(u_int8_t* data, const char* p); | 139 | extern int ff_hex_to_data(u_int8_t* data, const char* p); |
| 140 | 140 | ||
| 141 | +/** | ||
| 142 | + * generate the c0 chunk header for msg. | ||
| 143 | + * @param cache, the cache to write header. | ||
| 144 | + * @param nb_cache, the size of cache. | ||
| 145 | + * @return the size of header. 0 if cache not enough. | ||
| 146 | + */ | ||
| 147 | +extern int srs_chunk_header_c0( | ||
| 148 | + int perfer_cid, u_int32_t timestamp, int32_t payload_length, | ||
| 149 | + int8_t message_type, int32_t stream_id, | ||
| 150 | + char* cache, int nb_cache | ||
| 151 | + ); | ||
| 152 | + | ||
| 153 | +/** | ||
| 154 | + * generate the c3 chunk header for msg. | ||
| 155 | + * @param cache, the cache to write header. | ||
| 156 | + * @param nb_cache, the size of cache. | ||
| 157 | + * @return the size of header. 0 if cache not enough. | ||
| 158 | + */ | ||
| 159 | +extern int srs_chunk_header_c3( | ||
| 160 | + int perfer_cid, u_int32_t timestamp, | ||
| 161 | + char* cache, int nb_cache | ||
| 162 | + ); | ||
| 163 | + | ||
| 141 | #endif | 164 | #endif |
| 142 | 165 |
| @@ -33,6 +33,11 @@ | @@ -33,6 +33,11 @@ | ||
| 33 | #include <string> | 33 | #include <string> |
| 34 | #include <vector> | 34 | #include <vector> |
| 35 | 35 | ||
| 36 | +// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 37 | +#ifndef _WIN32 | ||
| 38 | +#include <sys/uio.h> | ||
| 39 | +#endif | ||
| 40 | + | ||
| 36 | class SrsFileReader; | 41 | class SrsFileReader; |
| 37 | class SrsHttpHeader; | 42 | class SrsHttpHeader; |
| 38 | class ISrsHttpMessage; | 43 | class ISrsHttpMessage; |
| @@ -188,6 +193,11 @@ public: | @@ -188,6 +193,11 @@ public: | ||
| 188 | // the initial 512 bytes of written data to DetectContentType. | 193 | // the initial 512 bytes of written data to DetectContentType. |
| 189 | // @param data, the data to send. NULL to flush header only. | 194 | // @param data, the data to send. NULL to flush header only. |
| 190 | virtual int write(char* data, int size) = 0; | 195 | virtual int write(char* data, int size) = 0; |
| 196 | + /** | ||
| 197 | + * for the HTTP FLV, to writev to improve performance. | ||
| 198 | + * @see https://github.com/simple-rtmp-server/srs/issues/405 | ||
| 199 | + */ | ||
| 200 | + virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite) = 0; | ||
| 191 | 201 | ||
| 192 | // WriteHeader sends an HTTP response header with status code. | 202 | // WriteHeader sends an HTTP response header with status code. |
| 193 | // If WriteHeader is not called explicitly, the first call to Write | 203 | // If WriteHeader is not called explicitly, the first call to Write |
| @@ -120,318 +120,6 @@ using namespace std; | @@ -120,318 +120,6 @@ using namespace std; | ||
| 120 | /**************************************************************************** | 120 | /**************************************************************************** |
| 121 | ***************************************************************************** | 121 | ***************************************************************************** |
| 122 | ****************************************************************************/ | 122 | ****************************************************************************/ |
| 123 | -/** | ||
| 124 | -* the chunk stream id used for some under-layer message, | ||
| 125 | -* for example, the PC(protocol control) message. | ||
| 126 | -*/ | ||
| 127 | -#define RTMP_CID_ProtocolControl 0x02 | ||
| 128 | -/** | ||
| 129 | -* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection. | ||
| 130 | -* generally use 0x03. | ||
| 131 | -*/ | ||
| 132 | -#define RTMP_CID_OverConnection 0x03 | ||
| 133 | -/** | ||
| 134 | -* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection, | ||
| 135 | -* the midst state(we guess). | ||
| 136 | -* rarely used, e.g. onStatus(NetStream.Play.Reset). | ||
| 137 | -*/ | ||
| 138 | -#define RTMP_CID_OverConnection2 0x04 | ||
| 139 | -/** | ||
| 140 | -* the stream message(amf0/amf3), over NetStream. | ||
| 141 | -* generally use 0x05. | ||
| 142 | -*/ | ||
| 143 | -#define RTMP_CID_OverStream 0x05 | ||
| 144 | -/** | ||
| 145 | -* the stream message(amf0/amf3), over NetStream, the midst state(we guess). | ||
| 146 | -* rarely used, e.g. play("mp4:mystram.f4v") | ||
| 147 | -*/ | ||
| 148 | -#define RTMP_CID_OverStream2 0x08 | ||
| 149 | -/** | ||
| 150 | -* the stream message(video), over NetStream | ||
| 151 | -* generally use 0x06. | ||
| 152 | -*/ | ||
| 153 | -#define RTMP_CID_Video 0x06 | ||
| 154 | -/** | ||
| 155 | -* the stream message(audio), over NetStream. | ||
| 156 | -* generally use 0x07. | ||
| 157 | -*/ | ||
| 158 | -#define RTMP_CID_Audio 0x07 | ||
| 159 | - | ||
| 160 | -/**************************************************************************** | ||
| 161 | -***************************************************************************** | ||
| 162 | -****************************************************************************/ | ||
| 163 | - | ||
| 164 | -SrsMessageHeader::SrsMessageHeader() | ||
| 165 | -{ | ||
| 166 | - message_type = 0; | ||
| 167 | - payload_length = 0; | ||
| 168 | - timestamp_delta = 0; | ||
| 169 | - stream_id = 0; | ||
| 170 | - | ||
| 171 | - timestamp = 0; | ||
| 172 | - // we always use the connection chunk-id | ||
| 173 | - perfer_cid = RTMP_CID_OverConnection; | ||
| 174 | -} | ||
| 175 | - | ||
| 176 | -SrsMessageHeader::~SrsMessageHeader() | ||
| 177 | -{ | ||
| 178 | -} | ||
| 179 | - | ||
| 180 | -bool SrsMessageHeader::is_audio() | ||
| 181 | -{ | ||
| 182 | - return message_type == RTMP_MSG_AudioMessage; | ||
| 183 | -} | ||
| 184 | - | ||
| 185 | -bool SrsMessageHeader::is_video() | ||
| 186 | -{ | ||
| 187 | - return message_type == RTMP_MSG_VideoMessage; | ||
| 188 | -} | ||
| 189 | - | ||
| 190 | -bool SrsMessageHeader::is_amf0_command() | ||
| 191 | -{ | ||
| 192 | - return message_type == RTMP_MSG_AMF0CommandMessage; | ||
| 193 | -} | ||
| 194 | - | ||
| 195 | -bool SrsMessageHeader::is_amf0_data() | ||
| 196 | -{ | ||
| 197 | - return message_type == RTMP_MSG_AMF0DataMessage; | ||
| 198 | -} | ||
| 199 | - | ||
| 200 | -bool SrsMessageHeader::is_amf3_command() | ||
| 201 | -{ | ||
| 202 | - return message_type == RTMP_MSG_AMF3CommandMessage; | ||
| 203 | -} | ||
| 204 | - | ||
| 205 | -bool SrsMessageHeader::is_amf3_data() | ||
| 206 | -{ | ||
| 207 | - return message_type == RTMP_MSG_AMF3DataMessage; | ||
| 208 | -} | ||
| 209 | - | ||
| 210 | -bool SrsMessageHeader::is_window_ackledgement_size() | ||
| 211 | -{ | ||
| 212 | - return message_type == RTMP_MSG_WindowAcknowledgementSize; | ||
| 213 | -} | ||
| 214 | - | ||
| 215 | -bool SrsMessageHeader::is_ackledgement() | ||
| 216 | -{ | ||
| 217 | - return message_type == RTMP_MSG_Acknowledgement; | ||
| 218 | -} | ||
| 219 | - | ||
| 220 | -bool SrsMessageHeader::is_set_chunk_size() | ||
| 221 | -{ | ||
| 222 | - return message_type == RTMP_MSG_SetChunkSize; | ||
| 223 | -} | ||
| 224 | - | ||
| 225 | -bool SrsMessageHeader::is_user_control_message() | ||
| 226 | -{ | ||
| 227 | - return message_type == RTMP_MSG_UserControlMessage; | ||
| 228 | -} | ||
| 229 | - | ||
| 230 | -bool SrsMessageHeader::is_set_peer_bandwidth() | ||
| 231 | -{ | ||
| 232 | - return message_type == RTMP_MSG_SetPeerBandwidth; | ||
| 233 | -} | ||
| 234 | - | ||
| 235 | -bool SrsMessageHeader::is_aggregate() | ||
| 236 | -{ | ||
| 237 | - return message_type == RTMP_MSG_AggregateMessage; | ||
| 238 | -} | ||
| 239 | - | ||
| 240 | -void SrsMessageHeader::initialize_amf0_script(int size, int stream) | ||
| 241 | -{ | ||
| 242 | - message_type = RTMP_MSG_AMF0DataMessage; | ||
| 243 | - payload_length = (int32_t)size; | ||
| 244 | - timestamp_delta = (int32_t)0; | ||
| 245 | - timestamp = (int64_t)0; | ||
| 246 | - stream_id = (int32_t)stream; | ||
| 247 | - | ||
| 248 | - // amf0 script use connection2 chunk-id | ||
| 249 | - perfer_cid = RTMP_CID_OverConnection2; | ||
| 250 | -} | ||
| 251 | - | ||
| 252 | -void SrsMessageHeader::initialize_audio(int size, u_int32_t time, int stream) | ||
| 253 | -{ | ||
| 254 | - message_type = RTMP_MSG_AudioMessage; | ||
| 255 | - payload_length = (int32_t)size; | ||
| 256 | - timestamp_delta = (int32_t)time; | ||
| 257 | - timestamp = (int64_t)time; | ||
| 258 | - stream_id = (int32_t)stream; | ||
| 259 | - | ||
| 260 | - // audio chunk-id | ||
| 261 | - perfer_cid = RTMP_CID_Audio; | ||
| 262 | -} | ||
| 263 | - | ||
| 264 | -void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream) | ||
| 265 | -{ | ||
| 266 | - message_type = RTMP_MSG_VideoMessage; | ||
| 267 | - payload_length = (int32_t)size; | ||
| 268 | - timestamp_delta = (int32_t)time; | ||
| 269 | - timestamp = (int64_t)time; | ||
| 270 | - stream_id = (int32_t)stream; | ||
| 271 | - | ||
| 272 | - // video chunk-id | ||
| 273 | - perfer_cid = RTMP_CID_Video; | ||
| 274 | -} | ||
| 275 | - | ||
| 276 | -SrsCommonMessage::SrsCommonMessage() | ||
| 277 | -{ | ||
| 278 | - payload = NULL; | ||
| 279 | - size = 0; | ||
| 280 | -} | ||
| 281 | - | ||
| 282 | -SrsCommonMessage::~SrsCommonMessage() | ||
| 283 | -{ | ||
| 284 | - srs_freep(payload); | ||
| 285 | -} | ||
| 286 | - | ||
| 287 | -SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() | ||
| 288 | -{ | ||
| 289 | - payload = NULL; | ||
| 290 | - size = 0; | ||
| 291 | - shared_count = 0; | ||
| 292 | -} | ||
| 293 | - | ||
| 294 | -SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() | ||
| 295 | -{ | ||
| 296 | - srs_freep(payload); | ||
| 297 | -} | ||
| 298 | - | ||
| 299 | -SrsSharedPtrMessage::SrsSharedPtrMessage() | ||
| 300 | -{ | ||
| 301 | - ptr = NULL; | ||
| 302 | -} | ||
| 303 | - | ||
| 304 | -SrsSharedPtrMessage::~SrsSharedPtrMessage() | ||
| 305 | -{ | ||
| 306 | - if (ptr) { | ||
| 307 | - if (ptr->shared_count == 0) { | ||
| 308 | - srs_freep(ptr); | ||
| 309 | - } else { | ||
| 310 | - ptr->shared_count--; | ||
| 311 | - } | ||
| 312 | - } | ||
| 313 | -} | ||
| 314 | - | ||
| 315 | -int SrsSharedPtrMessage::create(SrsCommonMessage* msg) | ||
| 316 | -{ | ||
| 317 | - int ret = ERROR_SUCCESS; | ||
| 318 | - | ||
| 319 | - if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) { | ||
| 320 | - return ret; | ||
| 321 | - } | ||
| 322 | - | ||
| 323 | - // to prevent double free of payload: | ||
| 324 | - // initialize already attach the payload of msg, | ||
| 325 | - // detach the payload to transfer the owner to shared ptr. | ||
| 326 | - msg->payload = NULL; | ||
| 327 | - msg->size = 0; | ||
| 328 | - | ||
| 329 | - return ret; | ||
| 330 | -} | ||
| 331 | - | ||
| 332 | -int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size) | ||
| 333 | -{ | ||
| 334 | - int ret = ERROR_SUCCESS; | ||
| 335 | - | ||
| 336 | - if (ptr) { | ||
| 337 | - ret = ERROR_SYSTEM_ASSERT_FAILED; | ||
| 338 | - srs_error("should not set the payload twice. ret=%d", ret); | ||
| 339 | - srs_assert(false); | ||
| 340 | - | ||
| 341 | - return ret; | ||
| 342 | - } | ||
| 343 | - | ||
| 344 | - ptr = new SrsSharedPtrPayload(); | ||
| 345 | - | ||
| 346 | - // direct attach the data. | ||
| 347 | - if (pheader) { | ||
| 348 | - ptr->header.message_type = pheader->message_type; | ||
| 349 | - ptr->header.payload_length = size; | ||
| 350 | - ptr->header.perfer_cid = pheader->perfer_cid; | ||
| 351 | - this->timestamp = pheader->timestamp; | ||
| 352 | - this->stream_id = pheader->stream_id; | ||
| 353 | - } | ||
| 354 | - ptr->payload = payload; | ||
| 355 | - ptr->size = size; | ||
| 356 | - | ||
| 357 | - // message can access it. | ||
| 358 | - this->payload = ptr->payload; | ||
| 359 | - this->size = ptr->size; | ||
| 360 | - | ||
| 361 | - return ret; | ||
| 362 | -} | ||
| 363 | - | ||
| 364 | -int SrsSharedPtrMessage::count() | ||
| 365 | -{ | ||
| 366 | - srs_assert(ptr); | ||
| 367 | - return ptr->shared_count; | ||
| 368 | -} | ||
| 369 | - | ||
| 370 | -bool SrsSharedPtrMessage::check(int stream_id) | ||
| 371 | -{ | ||
| 372 | - // we donot use the complex basic header, | ||
| 373 | - // ensure the basic header is 1bytes. | ||
| 374 | - if (ptr->header.perfer_cid < 2) { | ||
| 375 | - srs_info("change the chunk_id=%d to default=%d", | ||
| 376 | - ptr->header.perfer_cid, RTMP_CID_ProtocolControl); | ||
| 377 | - ptr->header.perfer_cid = RTMP_CID_ProtocolControl; | ||
| 378 | - } | ||
| 379 | - | ||
| 380 | - // we assume that the stream_id in a group must be the same. | ||
| 381 | - if (this->stream_id == stream_id) { | ||
| 382 | - return true; | ||
| 383 | - } | ||
| 384 | - this->stream_id = stream_id; | ||
| 385 | - | ||
| 386 | - return false; | ||
| 387 | -} | ||
| 388 | - | ||
| 389 | -bool SrsSharedPtrMessage::is_av() | ||
| 390 | -{ | ||
| 391 | - return ptr->header.message_type == RTMP_MSG_AudioMessage | ||
| 392 | - || ptr->header.message_type == RTMP_MSG_VideoMessage; | ||
| 393 | -} | ||
| 394 | - | ||
| 395 | -bool SrsSharedPtrMessage::is_audio() | ||
| 396 | -{ | ||
| 397 | - return ptr->header.message_type == RTMP_MSG_AudioMessage; | ||
| 398 | -} | ||
| 399 | - | ||
| 400 | -bool SrsSharedPtrMessage::is_video() | ||
| 401 | -{ | ||
| 402 | - return ptr->header.message_type == RTMP_MSG_VideoMessage; | ||
| 403 | -} | ||
| 404 | - | ||
| 405 | -int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0) | ||
| 406 | -{ | ||
| 407 | - if (c0) { | ||
| 408 | - return srs_chunk_header_c0( | ||
| 409 | - ptr->header.perfer_cid, timestamp, ptr->header.payload_length, | ||
| 410 | - ptr->header.message_type, stream_id, | ||
| 411 | - cache, nb_cache); | ||
| 412 | - } else { | ||
| 413 | - return srs_chunk_header_c3( | ||
| 414 | - ptr->header.perfer_cid, timestamp, | ||
| 415 | - cache, nb_cache); | ||
| 416 | - } | ||
| 417 | -} | ||
| 418 | - | ||
| 419 | -SrsSharedPtrMessage* SrsSharedPtrMessage::copy() | ||
| 420 | -{ | ||
| 421 | - srs_assert(ptr); | ||
| 422 | - | ||
| 423 | - SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); | ||
| 424 | - | ||
| 425 | - copy->ptr = ptr; | ||
| 426 | - ptr->shared_count++; | ||
| 427 | - | ||
| 428 | - copy->timestamp = timestamp; | ||
| 429 | - copy->stream_id = stream_id; | ||
| 430 | - copy->payload = ptr->payload; | ||
| 431 | - copy->size = ptr->size; | ||
| 432 | - | ||
| 433 | - return copy; | ||
| 434 | -} | ||
| 435 | 123 | ||
| 436 | SrsPacket::SrsPacket() | 124 | SrsPacket::SrsPacket() |
| 437 | { | 125 | { |
| @@ -904,41 +592,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) | @@ -904,41 +592,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) | ||
| 904 | 592 | ||
| 905 | int SrsProtocol::do_iovs_send(iovec* iovs, int size) | 593 | int SrsProtocol::do_iovs_send(iovec* iovs, int size) |
| 906 | { | 594 | { |
| 907 | - int ret = ERROR_SUCCESS; | ||
| 908 | - | ||
| 909 | - // the limits of writev iovs. | ||
| 910 | - // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 911 | -#ifndef _WIN32 | ||
| 912 | - static int limits = sysconf(_SC_IOV_MAX); | ||
| 913 | -#else | ||
| 914 | - static int limits = 1024; | ||
| 915 | -#endif | ||
| 916 | - | ||
| 917 | - // send in a time. | ||
| 918 | - if (size < limits) { | ||
| 919 | - if ((ret = skt->writev(iovs, size, NULL)) != ERROR_SUCCESS) { | ||
| 920 | - if (!srs_is_client_gracefully_close(ret)) { | ||
| 921 | - srs_error("send with writev failed. ret=%d", ret); | ||
| 922 | - } | ||
| 923 | - return ret; | ||
| 924 | - } | ||
| 925 | - return ret; | ||
| 926 | - } | ||
| 927 | - | ||
| 928 | - // send in multiple times. | ||
| 929 | - int cur_iov = 0; | ||
| 930 | - while (cur_iov < size) { | ||
| 931 | - int cur_count = srs_min(limits, size - cur_iov); | ||
| 932 | - if ((ret = skt->writev(iovs + cur_iov, cur_count, NULL)) != ERROR_SUCCESS) { | ||
| 933 | - if (!srs_is_client_gracefully_close(ret)) { | ||
| 934 | - srs_error("send with writev failed. ret=%d", ret); | ||
| 935 | - } | ||
| 936 | - return ret; | ||
| 937 | - } | ||
| 938 | - cur_iov += cur_count; | ||
| 939 | - } | ||
| 940 | - | ||
| 941 | - return ret; | 595 | + return srs_write_large_iovs(skt, iovs, size); |
| 942 | } | 596 | } |
| 943 | 597 | ||
| 944 | int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) | 598 | int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) |
| @@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 43 | #include <srs_kernel_error.hpp> | 43 | #include <srs_kernel_error.hpp> |
| 44 | #include <srs_kernel_consts.hpp> | 44 | #include <srs_kernel_consts.hpp> |
| 45 | #include <srs_core_performance.hpp> | 45 | #include <srs_core_performance.hpp> |
| 46 | +#include <srs_kernel_flv.hpp> | ||
| 46 | 47 | ||
| 47 | class ISrsProtocolReaderWriter; | 48 | class ISrsProtocolReaderWriter; |
| 48 | class SrsFastBuffer; | 49 | class SrsFastBuffer; |
| @@ -60,89 +61,6 @@ class IMergeReadHandler; | @@ -60,89 +61,6 @@ class IMergeReadHandler; | ||
| 60 | ***************************************************************************** | 61 | ***************************************************************************** |
| 61 | ****************************************************************************/ | 62 | ****************************************************************************/ |
| 62 | /** | 63 | /** |
| 63 | - 5. Protocol Control Messages | ||
| 64 | - RTMP reserves message type IDs 1-7 for protocol control messages. | ||
| 65 | - These messages contain information needed by the RTM Chunk Stream | ||
| 66 | - protocol or RTMP itself. Protocol messages with IDs 1 & 2 are | ||
| 67 | - reserved for usage with RTM Chunk Stream protocol. Protocol messages | ||
| 68 | - with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID | ||
| 69 | - 7 is used between edge server and origin server. | ||
| 70 | - */ | ||
| 71 | -#define RTMP_MSG_SetChunkSize 0x01 | ||
| 72 | -#define RTMP_MSG_AbortMessage 0x02 | ||
| 73 | -#define RTMP_MSG_Acknowledgement 0x03 | ||
| 74 | -#define RTMP_MSG_UserControlMessage 0x04 | ||
| 75 | -#define RTMP_MSG_WindowAcknowledgementSize 0x05 | ||
| 76 | -#define RTMP_MSG_SetPeerBandwidth 0x06 | ||
| 77 | -#define RTMP_MSG_EdgeAndOriginServerCommand 0x07 | ||
| 78 | -/** | ||
| 79 | - 3. Types of messages | ||
| 80 | - The server and the client send messages over the network to | ||
| 81 | - communicate with each other. The messages can be of any type which | ||
| 82 | - includes audio messages, video messages, command messages, shared | ||
| 83 | - object messages, data messages, and user control messages. | ||
| 84 | - 3.1. Command message | ||
| 85 | - Command messages carry the AMF-encoded commands between the client | ||
| 86 | - and the server. These messages have been assigned message type value | ||
| 87 | - of 20 for AMF0 encoding and message type value of 17 for AMF3 | ||
| 88 | - encoding. These messages are sent to perform some operations like | ||
| 89 | - connect, createStream, publish, play, pause on the peer. Command | ||
| 90 | - messages like onstatus, result etc. are used to inform the sender | ||
| 91 | - about the status of the requested commands. A command message | ||
| 92 | - consists of command name, transaction ID, and command object that | ||
| 93 | - contains related parameters. A client or a server can request Remote | ||
| 94 | - Procedure Calls (RPC) over streams that are communicated using the | ||
| 95 | - command messages to the peer. | ||
| 96 | - */ | ||
| 97 | -#define RTMP_MSG_AMF3CommandMessage 17 // 0x11 | ||
| 98 | -#define RTMP_MSG_AMF0CommandMessage 20 // 0x14 | ||
| 99 | -/** | ||
| 100 | - 3.2. Data message | ||
| 101 | - The client or the server sends this message to send Metadata or any | ||
| 102 | - user data to the peer. Metadata includes details about the | ||
| 103 | - data(audio, video etc.) like creation time, duration, theme and so | ||
| 104 | - on. These messages have been assigned message type value of 18 for | ||
| 105 | - AMF0 and message type value of 15 for AMF3. | ||
| 106 | - */ | ||
| 107 | -#define RTMP_MSG_AMF0DataMessage 18 // 0x12 | ||
| 108 | -#define RTMP_MSG_AMF3DataMessage 15 // 0x0F | ||
| 109 | -/** | ||
| 110 | - 3.3. Shared object message | ||
| 111 | - A shared object is a Flash object (a collection of name value pairs) | ||
| 112 | - that are in synchronization across multiple clients, instances, and | ||
| 113 | - so on. The message types kMsgContainer=19 for AMF0 and | ||
| 114 | - kMsgContainerEx=16 for AMF3 are reserved for shared object events. | ||
| 115 | - Each message can contain multiple events. | ||
| 116 | - */ | ||
| 117 | -#define RTMP_MSG_AMF3SharedObject 16 // 0x10 | ||
| 118 | -#define RTMP_MSG_AMF0SharedObject 19 // 0x13 | ||
| 119 | -/** | ||
| 120 | - 3.4. Audio message | ||
| 121 | - The client or the server sends this message to send audio data to the | ||
| 122 | - peer. The message type value of 8 is reserved for audio messages. | ||
| 123 | - */ | ||
| 124 | -#define RTMP_MSG_AudioMessage 8 // 0x08 | ||
| 125 | -/* * | ||
| 126 | - 3.5. Video message | ||
| 127 | - The client or the server sends this message to send video data to the | ||
| 128 | - peer. The message type value of 9 is reserved for video messages. | ||
| 129 | - These messages are large and can delay the sending of other type of | ||
| 130 | - messages. To avoid such a situation, the video message is assigned | ||
| 131 | - the lowest priority. | ||
| 132 | - */ | ||
| 133 | -#define RTMP_MSG_VideoMessage 9 // 0x09 | ||
| 134 | -/** | ||
| 135 | - 3.6. Aggregate message | ||
| 136 | - An aggregate message is a single message that contains a list of submessages. | ||
| 137 | - The message type value of 22 is reserved for aggregate | ||
| 138 | - messages. | ||
| 139 | - */ | ||
| 140 | -#define RTMP_MSG_AggregateMessage 22 // 0x16 | ||
| 141 | - | ||
| 142 | -/**************************************************************************** | ||
| 143 | - ***************************************************************************** | ||
| 144 | - ****************************************************************************/ | ||
| 145 | -/** | ||
| 146 | * amf0 command message, command name macros | 64 | * amf0 command message, command name macros |
| 147 | */ | 65 | */ |
| 148 | #define RTMP_AMF0_COMMAND_CONNECT "connect" | 66 | #define RTMP_AMF0_COMMAND_CONNECT "connect" |
| @@ -163,263 +81,6 @@ class IMergeReadHandler; | @@ -163,263 +81,6 @@ class IMergeReadHandler; | ||
| 163 | /**************************************************************************** | 81 | /**************************************************************************** |
| 164 | ***************************************************************************** | 82 | ***************************************************************************** |
| 165 | ****************************************************************************/ | 83 | ****************************************************************************/ |
| 166 | -/** | ||
| 167 | -* 6.1. Chunk Format | ||
| 168 | -* Extended timestamp: 0 or 4 bytes | ||
| 169 | -* This field MUST be sent when the normal timsestamp is set to | ||
| 170 | -* 0xffffff, it MUST NOT be sent if the normal timestamp is set to | ||
| 171 | -* anything else. So for values less than 0xffffff the normal | ||
| 172 | -* timestamp field SHOULD be used in which case the extended timestamp | ||
| 173 | -* MUST NOT be present. For values greater than or equal to 0xffffff | ||
| 174 | -* the normal timestamp field MUST NOT be used and MUST be set to | ||
| 175 | -* 0xffffff and the extended timestamp MUST be sent. | ||
| 176 | -*/ | ||
| 177 | -#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF | ||
| 178 | - | ||
| 179 | -/** | ||
| 180 | -* 4.1. Message Header | ||
| 181 | -*/ | ||
| 182 | -class SrsMessageHeader | ||
| 183 | -{ | ||
| 184 | -public: | ||
| 185 | - /** | ||
| 186 | - * 3bytes. | ||
| 187 | - * Three-byte field that contains a timestamp delta of the message. | ||
| 188 | - * @remark, only used for decoding message from chunk stream. | ||
| 189 | - */ | ||
| 190 | - int32_t timestamp_delta; | ||
| 191 | - /** | ||
| 192 | - * 3bytes. | ||
| 193 | - * Three-byte field that represents the size of the payload in bytes. | ||
| 194 | - * It is set in big-endian format. | ||
| 195 | - */ | ||
| 196 | - int32_t payload_length; | ||
| 197 | - /** | ||
| 198 | - * 1byte. | ||
| 199 | - * One byte field to represent the message type. A range of type IDs | ||
| 200 | - * (1-7) are reserved for protocol control messages. | ||
| 201 | - */ | ||
| 202 | - int8_t message_type; | ||
| 203 | - /** | ||
| 204 | - * 4bytes. | ||
| 205 | - * Four-byte field that identifies the stream of the message. These | ||
| 206 | - * bytes are set in little-endian format. | ||
| 207 | - */ | ||
| 208 | - int32_t stream_id; | ||
| 209 | - | ||
| 210 | - /** | ||
| 211 | - * Four-byte field that contains a timestamp of the message. | ||
| 212 | - * The 4 bytes are packed in the big-endian order. | ||
| 213 | - * @remark, used as calc timestamp when decode and encode time. | ||
| 214 | - * @remark, we use 64bits for large time for jitter detect and hls. | ||
| 215 | - */ | ||
| 216 | - int64_t timestamp; | ||
| 217 | -public: | ||
| 218 | - /** | ||
| 219 | - * get the perfered cid(chunk stream id) which sendout over. | ||
| 220 | - * set at decoding, and canbe used for directly send message, | ||
| 221 | - * for example, dispatch to all connections. | ||
| 222 | - */ | ||
| 223 | - int perfer_cid; | ||
| 224 | -public: | ||
| 225 | - SrsMessageHeader(); | ||
| 226 | - virtual ~SrsMessageHeader(); | ||
| 227 | -public: | ||
| 228 | - bool is_audio(); | ||
| 229 | - bool is_video(); | ||
| 230 | - bool is_amf0_command(); | ||
| 231 | - bool is_amf0_data(); | ||
| 232 | - bool is_amf3_command(); | ||
| 233 | - bool is_amf3_data(); | ||
| 234 | - bool is_window_ackledgement_size(); | ||
| 235 | - bool is_ackledgement(); | ||
| 236 | - bool is_set_chunk_size(); | ||
| 237 | - bool is_user_control_message(); | ||
| 238 | - bool is_set_peer_bandwidth(); | ||
| 239 | - bool is_aggregate(); | ||
| 240 | -public: | ||
| 241 | - /** | ||
| 242 | - * create a amf0 script header, set the size and stream_id. | ||
| 243 | - */ | ||
| 244 | - void initialize_amf0_script(int size, int stream); | ||
| 245 | - /** | ||
| 246 | - * create a audio header, set the size, timestamp and stream_id. | ||
| 247 | - */ | ||
| 248 | - void initialize_audio(int size, u_int32_t time, int stream); | ||
| 249 | - /** | ||
| 250 | - * create a video header, set the size, timestamp and stream_id. | ||
| 251 | - */ | ||
| 252 | - void initialize_video(int size, u_int32_t time, int stream); | ||
| 253 | -}; | ||
| 254 | - | ||
| 255 | -/** | ||
| 256 | -* message is raw data RTMP message, bytes oriented, | ||
| 257 | -* protcol always recv RTMP message, and can send RTMP message or RTMP packet. | ||
| 258 | -* the common message is read from underlay protocol sdk. | ||
| 259 | -* while the shared ptr message used to copy and send. | ||
| 260 | -*/ | ||
| 261 | -class SrsCommonMessage | ||
| 262 | -{ | ||
| 263 | -// 4.1. Message Header | ||
| 264 | -public: | ||
| 265 | - SrsMessageHeader header; | ||
| 266 | -// 4.2. Message Payload | ||
| 267 | -public: | ||
| 268 | - /** | ||
| 269 | - * current message parsed size, | ||
| 270 | - * size <= header.payload_length | ||
| 271 | - * for the payload maybe sent in multiple chunks. | ||
| 272 | - */ | ||
| 273 | - int size; | ||
| 274 | - /** | ||
| 275 | - * the payload of message, the SrsCommonMessage never know about the detail of payload, | ||
| 276 | - * user must use SrsProtocol.decode_message to get concrete packet. | ||
| 277 | - * @remark, not all message payload can be decoded to packet. for example, | ||
| 278 | - * video/audio packet use raw bytes, no video/audio packet. | ||
| 279 | - */ | ||
| 280 | - char* payload; | ||
| 281 | -public: | ||
| 282 | - SrsCommonMessage(); | ||
| 283 | -public: | ||
| 284 | - virtual ~SrsCommonMessage(); | ||
| 285 | -}; | ||
| 286 | - | ||
| 287 | -/** | ||
| 288 | -* the message header for shared ptr message. | ||
| 289 | -* only the message for all msgs are same. | ||
| 290 | -*/ | ||
| 291 | -struct SrsSharedMessageHeader | ||
| 292 | -{ | ||
| 293 | - /** | ||
| 294 | - * 3bytes. | ||
| 295 | - * Three-byte field that represents the size of the payload in bytes. | ||
| 296 | - * It is set in big-endian format. | ||
| 297 | - */ | ||
| 298 | - int32_t payload_length; | ||
| 299 | - /** | ||
| 300 | - * 1byte. | ||
| 301 | - * One byte field to represent the message type. A range of type IDs | ||
| 302 | - * (1-7) are reserved for protocol control messages. | ||
| 303 | - */ | ||
| 304 | - int8_t message_type; | ||
| 305 | - /** | ||
| 306 | - * get the perfered cid(chunk stream id) which sendout over. | ||
| 307 | - * set at decoding, and canbe used for directly send message, | ||
| 308 | - * for example, dispatch to all connections. | ||
| 309 | - */ | ||
| 310 | - int perfer_cid; | ||
| 311 | -}; | ||
| 312 | - | ||
| 313 | -/** | ||
| 314 | -* shared ptr message. | ||
| 315 | -* for audio/video/data message that need less memory copy. | ||
| 316 | -* and only for output. | ||
| 317 | -* | ||
| 318 | -* create first object by constructor and create(), | ||
| 319 | -* use copy if need reference count message. | ||
| 320 | -* | ||
| 321 | -*/ | ||
| 322 | -class SrsSharedPtrMessage | ||
| 323 | -{ | ||
| 324 | -// 4.1. Message Header | ||
| 325 | -public: | ||
| 326 | - // the header can shared, only set the timestamp and stream id. | ||
| 327 | - // @see https://github.com/simple-rtmp-server/srs/issues/251 | ||
| 328 | - //SrsSharedMessageHeader header; | ||
| 329 | - /** | ||
| 330 | - * Four-byte field that contains a timestamp of the message. | ||
| 331 | - * The 4 bytes are packed in the big-endian order. | ||
| 332 | - * @remark, used as calc timestamp when decode and encode time. | ||
| 333 | - * @remark, we use 64bits for large time for jitter detect and hls. | ||
| 334 | - */ | ||
| 335 | - int64_t timestamp; | ||
| 336 | - /** | ||
| 337 | - * 4bytes. | ||
| 338 | - * Four-byte field that identifies the stream of the message. These | ||
| 339 | - * bytes are set in big-endian format. | ||
| 340 | - */ | ||
| 341 | - int32_t stream_id; | ||
| 342 | -// 4.2. Message Payload | ||
| 343 | -public: | ||
| 344 | - /** | ||
| 345 | - * current message parsed size, | ||
| 346 | - * size <= header.payload_length | ||
| 347 | - * for the payload maybe sent in multiple chunks. | ||
| 348 | - */ | ||
| 349 | - int size; | ||
| 350 | - /** | ||
| 351 | - * the payload of message, the SrsCommonMessage never know about the detail of payload, | ||
| 352 | - * user must use SrsProtocol.decode_message to get concrete packet. | ||
| 353 | - * @remark, not all message payload can be decoded to packet. for example, | ||
| 354 | - * video/audio packet use raw bytes, no video/audio packet. | ||
| 355 | - */ | ||
| 356 | - char* payload; | ||
| 357 | -private: | ||
| 358 | - class SrsSharedPtrPayload | ||
| 359 | - { | ||
| 360 | - public: | ||
| 361 | - // shared message header. | ||
| 362 | - // @see https://github.com/simple-rtmp-server/srs/issues/251 | ||
| 363 | - SrsSharedMessageHeader header; | ||
| 364 | - // actual shared payload. | ||
| 365 | - char* payload; | ||
| 366 | - // size of payload. | ||
| 367 | - int size; | ||
| 368 | - // the reference count | ||
| 369 | - int shared_count; | ||
| 370 | - public: | ||
| 371 | - SrsSharedPtrPayload(); | ||
| 372 | - virtual ~SrsSharedPtrPayload(); | ||
| 373 | - }; | ||
| 374 | - SrsSharedPtrPayload* ptr; | ||
| 375 | -public: | ||
| 376 | - SrsSharedPtrMessage(); | ||
| 377 | - virtual ~SrsSharedPtrMessage(); | ||
| 378 | -public: | ||
| 379 | - /** | ||
| 380 | - * create shared ptr message, | ||
| 381 | - * copy header, manage the payload of msg, | ||
| 382 | - * set the payload to NULL to prevent double free. | ||
| 383 | - * @remark payload of msg set to NULL if success. | ||
| 384 | - */ | ||
| 385 | - virtual int create(SrsCommonMessage* msg); | ||
| 386 | - /** | ||
| 387 | - * create shared ptr message, | ||
| 388 | - * from the header and payload. | ||
| 389 | - * @remark user should never free the payload. | ||
| 390 | - * @param pheader, the header to copy to the message. NULL to ignore. | ||
| 391 | - */ | ||
| 392 | - virtual int create(SrsMessageHeader* pheader, char* payload, int size); | ||
| 393 | - /** | ||
| 394 | - * get current reference count. | ||
| 395 | - * when this object created, count set to 0. | ||
| 396 | - * if copy() this object, count increase 1. | ||
| 397 | - * if this or copy deleted, free payload when count is 0, or count--. | ||
| 398 | - * @remark, assert object is created. | ||
| 399 | - */ | ||
| 400 | - virtual int count(); | ||
| 401 | - /** | ||
| 402 | - * check perfer cid and stream id. | ||
| 403 | - * @return whether stream id already set. | ||
| 404 | - */ | ||
| 405 | - virtual bool check(int stream_id); | ||
| 406 | -public: | ||
| 407 | - virtual bool is_av(); | ||
| 408 | - virtual bool is_audio(); | ||
| 409 | - virtual bool is_video(); | ||
| 410 | -public: | ||
| 411 | - /** | ||
| 412 | - * generate the chunk header to cache. | ||
| 413 | - * @return the size of header. | ||
| 414 | - */ | ||
| 415 | - virtual int chunk_header(char* cache, int nb_cache, bool c0); | ||
| 416 | -public: | ||
| 417 | - /** | ||
| 418 | - * copy current shared ptr message, use ref-count. | ||
| 419 | - * @remark, assert object is created. | ||
| 420 | - */ | ||
| 421 | - virtual SrsSharedPtrMessage* copy(); | ||
| 422 | -}; | ||
| 423 | 84 | ||
| 424 | /** | 85 | /** |
| 425 | * the decoded message payload. | 86 | * the decoded message payload. |
| @@ -23,6 +23,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -23,6 +23,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 23 | 23 | ||
| 24 | #include <srs_rtmp_utility.hpp> | 24 | #include <srs_rtmp_utility.hpp> |
| 25 | 25 | ||
| 26 | +// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 27 | +#ifndef _WIN32 | ||
| 28 | +#include <unistd.h> | ||
| 29 | +#endif | ||
| 30 | + | ||
| 26 | #include <stdlib.h> | 31 | #include <stdlib.h> |
| 27 | using namespace std; | 32 | using namespace std; |
| 28 | 33 | ||
| @@ -32,6 +37,8 @@ using namespace std; | @@ -32,6 +37,8 @@ using namespace std; | ||
| 32 | #include <srs_rtmp_stack.hpp> | 37 | #include <srs_rtmp_stack.hpp> |
| 33 | #include <srs_kernel_codec.hpp> | 38 | #include <srs_kernel_codec.hpp> |
| 34 | #include <srs_kernel_consts.hpp> | 39 | #include <srs_kernel_consts.hpp> |
| 40 | +#include <srs_rtmp_stack.hpp> | ||
| 41 | +#include <srs_rtmp_io.hpp> | ||
| 35 | 42 | ||
| 36 | void srs_discovery_tc_url( | 43 | void srs_discovery_tc_url( |
| 37 | string tcUrl, | 44 | string tcUrl, |
| @@ -159,136 +166,6 @@ bool srs_bytes_equals(void* pa, void* pb, int size) | @@ -159,136 +166,6 @@ bool srs_bytes_equals(void* pa, void* pb, int size) | ||
| 159 | return true; | 166 | return true; |
| 160 | } | 167 | } |
| 161 | 168 | ||
| 162 | -int srs_chunk_header_c0( | ||
| 163 | - int perfer_cid, u_int32_t timestamp, int32_t payload_length, | ||
| 164 | - int8_t message_type, int32_t stream_id, | ||
| 165 | - char* cache, int nb_cache | ||
| 166 | -) | ||
| 167 | -{ | ||
| 168 | - // to directly set the field. | ||
| 169 | - char* pp = NULL; | ||
| 170 | - | ||
| 171 | - // generate the header. | ||
| 172 | - char* p = cache; | ||
| 173 | - | ||
| 174 | - // no header. | ||
| 175 | - if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { | ||
| 176 | - return 0; | ||
| 177 | - } | ||
| 178 | - | ||
| 179 | - // write new chunk stream header, fmt is 0 | ||
| 180 | - *p++ = 0x00 | (perfer_cid & 0x3F); | ||
| 181 | - | ||
| 182 | - // chunk message header, 11 bytes | ||
| 183 | - // timestamp, 3bytes, big-endian | ||
| 184 | - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { | ||
| 185 | - pp = (char*)×tamp; | ||
| 186 | - *p++ = pp[2]; | ||
| 187 | - *p++ = pp[1]; | ||
| 188 | - *p++ = pp[0]; | ||
| 189 | - } else { | ||
| 190 | - *p++ = 0xFF; | ||
| 191 | - *p++ = 0xFF; | ||
| 192 | - *p++ = 0xFF; | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - // message_length, 3bytes, big-endian | ||
| 196 | - pp = (char*)&payload_length; | ||
| 197 | - *p++ = pp[2]; | ||
| 198 | - *p++ = pp[1]; | ||
| 199 | - *p++ = pp[0]; | ||
| 200 | - | ||
| 201 | - // message_type, 1bytes | ||
| 202 | - *p++ = message_type; | ||
| 203 | - | ||
| 204 | - // stream_id, 4bytes, little-endian | ||
| 205 | - pp = (char*)&stream_id; | ||
| 206 | - *p++ = pp[0]; | ||
| 207 | - *p++ = pp[1]; | ||
| 208 | - *p++ = pp[2]; | ||
| 209 | - *p++ = pp[3]; | ||
| 210 | - | ||
| 211 | - // for c0 | ||
| 212 | - // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 213 | - // | ||
| 214 | - // for c3: | ||
| 215 | - // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 216 | - // 6.1.3. Extended Timestamp | ||
| 217 | - // This field is transmitted only when the normal time stamp in the | ||
| 218 | - // chunk message header is set to 0x00ffffff. If normal time stamp is | ||
| 219 | - // set to any value less than 0x00ffffff, this field MUST NOT be | ||
| 220 | - // present. This field MUST NOT be present if the timestamp field is not | ||
| 221 | - // present. Type 3 chunks MUST NOT have this field. | ||
| 222 | - // adobe changed for Type3 chunk: | ||
| 223 | - // FMLE always sendout the extended-timestamp, | ||
| 224 | - // must send the extended-timestamp to FMS, | ||
| 225 | - // must send the extended-timestamp to flash-player. | ||
| 226 | - // @see: ngx_rtmp_prepare_message | ||
| 227 | - // @see: http://blog.csdn.net/win_lin/article/details/13363699 | ||
| 228 | - // TODO: FIXME: extract to outer. | ||
| 229 | - if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { | ||
| 230 | - pp = (char*)×tamp; | ||
| 231 | - *p++ = pp[3]; | ||
| 232 | - *p++ = pp[2]; | ||
| 233 | - *p++ = pp[1]; | ||
| 234 | - *p++ = pp[0]; | ||
| 235 | - } | ||
| 236 | - | ||
| 237 | - // always has header | ||
| 238 | - return p - cache; | ||
| 239 | -} | ||
| 240 | - | ||
| 241 | -int srs_chunk_header_c3( | ||
| 242 | - int perfer_cid, u_int32_t timestamp, | ||
| 243 | - char* cache, int nb_cache | ||
| 244 | -) | ||
| 245 | -{ | ||
| 246 | - // to directly set the field. | ||
| 247 | - char* pp = NULL; | ||
| 248 | - | ||
| 249 | - // generate the header. | ||
| 250 | - char* p = cache; | ||
| 251 | - | ||
| 252 | - // no header. | ||
| 253 | - if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) { | ||
| 254 | - return 0; | ||
| 255 | - } | ||
| 256 | - | ||
| 257 | - // write no message header chunk stream, fmt is 3 | ||
| 258 | - // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, | ||
| 259 | - // SRS will rollback to 1B chunk header. | ||
| 260 | - *p++ = 0xC0 | (perfer_cid & 0x3F); | ||
| 261 | - | ||
| 262 | - // for c0 | ||
| 263 | - // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 264 | - // | ||
| 265 | - // for c3: | ||
| 266 | - // chunk extended timestamp header, 0 or 4 bytes, big-endian | ||
| 267 | - // 6.1.3. Extended Timestamp | ||
| 268 | - // This field is transmitted only when the normal time stamp in the | ||
| 269 | - // chunk message header is set to 0x00ffffff. If normal time stamp is | ||
| 270 | - // set to any value less than 0x00ffffff, this field MUST NOT be | ||
| 271 | - // present. This field MUST NOT be present if the timestamp field is not | ||
| 272 | - // present. Type 3 chunks MUST NOT have this field. | ||
| 273 | - // adobe changed for Type3 chunk: | ||
| 274 | - // FMLE always sendout the extended-timestamp, | ||
| 275 | - // must send the extended-timestamp to FMS, | ||
| 276 | - // must send the extended-timestamp to flash-player. | ||
| 277 | - // @see: ngx_rtmp_prepare_message | ||
| 278 | - // @see: http://blog.csdn.net/win_lin/article/details/13363699 | ||
| 279 | - // TODO: FIXME: extract to outer. | ||
| 280 | - if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { | ||
| 281 | - pp = (char*)×tamp; | ||
| 282 | - *p++ = pp[3]; | ||
| 283 | - *p++ = pp[2]; | ||
| 284 | - *p++ = pp[1]; | ||
| 285 | - *p++ = pp[0]; | ||
| 286 | - } | ||
| 287 | - | ||
| 288 | - // always has header | ||
| 289 | - return p - cache; | ||
| 290 | -} | ||
| 291 | - | ||
| 292 | int srs_do_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) | 169 | int srs_do_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) |
| 293 | { | 170 | { |
| 294 | int ret = ERROR_SUCCESS; | 171 | int ret = ERROR_SUCCESS; |
| @@ -362,3 +239,43 @@ std::string srs_generate_stream_url(std::string vhost, std::string app, std::str | @@ -362,3 +239,43 @@ std::string srs_generate_stream_url(std::string vhost, std::string app, std::str | ||
| 362 | return url; | 239 | return url; |
| 363 | } | 240 | } |
| 364 | 241 | ||
| 242 | +int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite) | ||
| 243 | +{ | ||
| 244 | + int ret = ERROR_SUCCESS; | ||
| 245 | + | ||
| 246 | + // the limits of writev iovs. | ||
| 247 | + // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 248 | +#ifndef _WIN32 | ||
| 249 | + // for linux, generally it's 1024. | ||
| 250 | + static int limits = sysconf(_SC_IOV_MAX); | ||
| 251 | +#else | ||
| 252 | + static int limits = 1024; | ||
| 253 | +#endif | ||
| 254 | + | ||
| 255 | + // send in a time. | ||
| 256 | + if (size < limits) { | ||
| 257 | + if ((ret = skt->writev(iovs, size, pnwrite)) != ERROR_SUCCESS) { | ||
| 258 | + if (!srs_is_client_gracefully_close(ret)) { | ||
| 259 | + srs_error("send with writev failed. ret=%d", ret); | ||
| 260 | + } | ||
| 261 | + return ret; | ||
| 262 | + } | ||
| 263 | + return ret; | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + // send in multiple times. | ||
| 267 | + int cur_iov = 0; | ||
| 268 | + while (cur_iov < size) { | ||
| 269 | + int cur_count = srs_min(limits, size - cur_iov); | ||
| 270 | + if ((ret = skt->writev(iovs + cur_iov, cur_count, pnwrite)) != ERROR_SUCCESS) { | ||
| 271 | + if (!srs_is_client_gracefully_close(ret)) { | ||
| 272 | + srs_error("send with writev failed. ret=%d", ret); | ||
| 273 | + } | ||
| 274 | + return ret; | ||
| 275 | + } | ||
| 276 | + cur_iov += cur_count; | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + return ret; | ||
| 280 | +} | ||
| 281 | + |
| @@ -29,12 +29,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -29,12 +29,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 29 | */ | 29 | */ |
| 30 | #include <srs_core.hpp> | 30 | #include <srs_core.hpp> |
| 31 | 31 | ||
| 32 | +// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 | ||
| 33 | +#ifndef _WIN32 | ||
| 34 | +#include <sys/uio.h> | ||
| 35 | +#endif | ||
| 36 | + | ||
| 32 | #include <string> | 37 | #include <string> |
| 33 | 38 | ||
| 34 | #include <srs_kernel_consts.hpp> | 39 | #include <srs_kernel_consts.hpp> |
| 35 | 40 | ||
| 36 | class SrsMessageHeader; | 41 | class SrsMessageHeader; |
| 37 | class SrsSharedPtrMessage; | 42 | class SrsSharedPtrMessage; |
| 43 | +class ISrsProtocolReaderWriter; | ||
| 38 | 44 | ||
| 39 | /** | 45 | /** |
| 40 | * parse the tcUrl, output the schema, host, vhost, app and port. | 46 | * parse the tcUrl, output the schema, host, vhost, app and port. |
| @@ -91,29 +97,6 @@ extern std::string srs_generate_tc_url( | @@ -91,29 +97,6 @@ extern std::string srs_generate_tc_url( | ||
| 91 | extern bool srs_bytes_equals(void* pa, void* pb, int size); | 97 | extern bool srs_bytes_equals(void* pa, void* pb, int size); |
| 92 | 98 | ||
| 93 | /** | 99 | /** |
| 94 | -* generate the c0 chunk header for msg. | ||
| 95 | -* @param cache, the cache to write header. | ||
| 96 | -* @param nb_cache, the size of cache. | ||
| 97 | -* @return the size of header. 0 if cache not enough. | ||
| 98 | -*/ | ||
| 99 | -extern int srs_chunk_header_c0( | ||
| 100 | - int perfer_cid, u_int32_t timestamp, int32_t payload_length, | ||
| 101 | - int8_t message_type, int32_t stream_id, | ||
| 102 | - char* cache, int nb_cache | ||
| 103 | -); | ||
| 104 | - | ||
| 105 | -/** | ||
| 106 | -* generate the c3 chunk header for msg. | ||
| 107 | -* @param cache, the cache to write header. | ||
| 108 | -* @param nb_cache, the size of cache. | ||
| 109 | -* @return the size of header. 0 if cache not enough. | ||
| 110 | -*/ | ||
| 111 | -extern int srs_chunk_header_c3( | ||
| 112 | - int perfer_cid, u_int32_t timestamp, | ||
| 113 | - char* cache, int nb_cache | ||
| 114 | -); | ||
| 115 | - | ||
| 116 | -/** | ||
| 117 | * create shared ptr message from bytes. | 100 | * create shared ptr message from bytes. |
| 118 | * @param data the packet bytes. user should never free it. | 101 | * @param data the packet bytes. user should never free it. |
| 119 | * @param ppmsg output the shared ptr message. user should free it. | 102 | * @param ppmsg output the shared ptr message. user should free it. |
| @@ -123,5 +106,8 @@ extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int s | @@ -123,5 +106,8 @@ extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int s | ||
| 123 | // get the stream identify, vhost/app/stream. | 106 | // get the stream identify, vhost/app/stream. |
| 124 | extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream); | 107 | extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream); |
| 125 | 108 | ||
| 109 | +// write large numbers of iovs. | ||
| 110 | +extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL); | ||
| 111 | + | ||
| 126 | #endif | 112 | #endif |
| 127 | 113 |
-
请 注册 或 登录 后发表评论