正在显示
13 个修改的文件
包含
1187 行增加
和
916 行删除
| @@ -719,6 +719,7 @@ The play HTTP FLV benchmark by [SB](https://github.com/simple-rtmp-server/srs-be | @@ -719,6 +719,7 @@ The play HTTP FLV benchmark by [SB](https://github.com/simple-rtmp-server/srs-be | ||
| 719 | | 2014-05-24 | 2.0.168 | 2.3k(2300) | players | 92% | 276MB | [code][p17] | | 719 | | 2014-05-24 | 2.0.168 | 2.3k(2300) | players | 92% | 276MB | [code][p17] | |
| 720 | | 2014-05-24 | 2.0.169 | 3.0k(3000) | players | 94% | 188MB | [code][p18] | | 720 | | 2014-05-24 | 2.0.169 | 3.0k(3000) | players | 94% | 188MB | [code][p18] | |
| 721 | | 2014-05-24 | 2.0.170 | 3.0k(3000) | players | 89% | 96MB | [code][p19] | | 721 | | 2014-05-24 | 2.0.170 | 3.0k(3000) | players | 89% | 96MB | [code][p19] | |
| 722 | +| 2014-05-25 | 2.0.171 | 6.0k(6000) | players | 84% | 297MB | [code][p20] | | ||
| 722 | 723 | ||
| 723 | ### Latency benchmark | 724 | ### Latency benchmark |
| 724 | 725 |
| @@ -175,7 +175,7 @@ int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | @@ -175,7 +175,7 @@ int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | ||
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | // send in chunked encoding. | 177 | // send in chunked encoding. |
| 178 | - int nb_iovss = iovcnt * 4; | 178 | + int nb_iovss = 3 + iovcnt; |
| 179 | iovec* iovss = iovss_cache; | 179 | iovec* iovss = iovss_cache; |
| 180 | if (nb_iovss_cache < nb_iovss) { | 180 | if (nb_iovss_cache < nb_iovss) { |
| 181 | srs_freep(iovss_cache); | 181 | srs_freep(iovss_cache); |
| @@ -183,29 +183,44 @@ int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | @@ -183,29 +183,44 @@ int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite) | ||
| 183 | iovss = iovss_cache = new iovec[nb_iovss]; | 183 | iovss = iovss_cache = new iovec[nb_iovss]; |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | - char* pheader_cache = header_cache; | 186 | + // send in chunked encoding. |
| 187 | + | ||
| 188 | + // chunk size. | ||
| 189 | + int size = 0; | ||
| 187 | for (int i = 0; i < iovcnt; i++) { | 190 | for (int i = 0; i < iovcnt; i++) { |
| 188 | - int left = SRS_HTTP_HEADER_CACHE_SIZE - (int)(pheader_cache - header_cache); | ||
| 189 | - srs_assert(left > 0); | ||
| 190 | - | ||
| 191 | iovec* data_iov = iov + i; | 191 | iovec* data_iov = iov + i; |
| 192 | - int nb_size = snprintf(pheader_cache, left, "%x", (int)data_iov->iov_len); | ||
| 193 | - | ||
| 194 | - iovec* iovs = iovss + (i * 4); | ||
| 195 | - iovs[0].iov_base = (char*)pheader_cache; | ||
| 196 | - iovs[0].iov_len = (int)nb_size; | ||
| 197 | - iovs[1].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 198 | - iovs[1].iov_len = 2; | ||
| 199 | - iovs[2].iov_base = (char*)data_iov->iov_base; | ||
| 200 | - iovs[2].iov_len = (int)data_iov->iov_len; | ||
| 201 | - iovs[3].iov_base = (char*)SRS_HTTP_CRLF; | ||
| 202 | - iovs[3].iov_len = 2; | ||
| 203 | - | ||
| 204 | - pheader_cache += nb_size; | 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++; | ||
| 205 | } | 214 | } |
| 206 | 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. | ||
| 207 | ssize_t nwrite; | 222 | ssize_t nwrite; |
| 208 | - if ((ret = skt->writev(iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) { | 223 | + if ((ret = srs_write_large_iovs(skt, iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) { |
| 209 | return ret; | 224 | return ret; |
| 210 | } | 225 | } |
| 211 | 226 | ||
| @@ -1442,6 +1457,21 @@ int SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/) | @@ -1442,6 +1457,21 @@ int SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/) | ||
| 1442 | return ERROR_SUCCESS; | 1457 | return ERROR_SUCCESS; |
| 1443 | } | 1458 | } |
| 1444 | 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 | + | ||
| 1445 | SrsAacStreamEncoder::SrsAacStreamEncoder() | 1475 | SrsAacStreamEncoder::SrsAacStreamEncoder() |
| 1446 | { | 1476 | { |
| 1447 | enc = new SrsAacEncoder(); | 1477 | enc = new SrsAacEncoder(); |
| @@ -1612,7 +1642,11 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1612,7 +1642,11 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1612 | srs_assert(entry); | 1642 | srs_assert(entry); |
| 1613 | if (srs_string_ends_with(entry->pattern, ".flv")) { | 1643 | if (srs_string_ends_with(entry->pattern, ".flv")) { |
| 1614 | 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 | ||
| 1615 | enc = new SrsFlvStreamEncoder(); | 1648 | enc = new SrsFlvStreamEncoder(); |
| 1649 | +#endif | ||
| 1616 | } else if (srs_string_ends_with(entry->pattern, ".aac")) { | 1650 | } else if (srs_string_ends_with(entry->pattern, ".aac")) { |
| 1617 | w->header()->set_content_type("audio/x-aac"); | 1651 | w->header()->set_content_type("audio/x-aac"); |
| 1618 | enc = new SrsAacStreamEncoder(); | 1652 | enc = new SrsAacStreamEncoder(); |
| @@ -1658,6 +1692,10 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1658,6 +1692,10 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1658 | } | 1692 | } |
| 1659 | } | 1693 | } |
| 1660 | 1694 | ||
| 1695 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 1696 | + SrsFastFlvStreamEncoder* ffe = dynamic_cast<SrsFastFlvStreamEncoder*>(enc); | ||
| 1697 | +#endif | ||
| 1698 | + | ||
| 1661 | while (true) { | 1699 | while (true) { |
| 1662 | pprint->elapse(); | 1700 | pprint->elapse(); |
| 1663 | 1701 | ||
| @@ -1684,7 +1722,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | @@ -1684,7 +1722,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) | ||
| 1684 | } | 1722 | } |
| 1685 | 1723 | ||
| 1686 | // 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 | ||
| 1687 | ret = streaming_send_messages(enc, msgs.msgs, count); | 1732 | ret = streaming_send_messages(enc, msgs.msgs, count); |
| 1733 | +#endif | ||
| 1688 | 1734 | ||
| 1689 | // free the messages. | 1735 | // free the messages. |
| 1690 | for (int i = 0; i < count; i++) { | 1736 | for (int i = 0; i < count; i++) { |
| @@ -71,11 +71,9 @@ class SrsHttpMessage; | @@ -71,11 +71,9 @@ class SrsHttpMessage; | ||
| 71 | 71 | ||
| 72 | #ifdef SRS_AUTO_HTTP_PARSER | 72 | #ifdef SRS_AUTO_HTTP_PARSER |
| 73 | 73 | ||
| 74 | -// for HTTP FLV, each video/audio packet is send by 3 iovs, | ||
| 75 | -// while each iov is send by 4 sub iovs, that is needs 3 chunk header, | ||
| 76 | -// suppose each header is 16 length, 3*16=48 is ok. | ||
| 77 | -// that is, 512 can used for 16 iovs to send. | ||
| 78 | -#define SRS_HTTP_HEADER_CACHE_SIZE 512 | 74 | +// the http chunked header size, |
| 75 | +// for writev, there always one chunk to send it. | ||
| 76 | +#define SRS_HTTP_HEADER_CACHE_SIZE 64 | ||
| 79 | 77 | ||
| 80 | /** | 78 | /** |
| 81 | * response writer use st socket | 79 | * response writer use st socket |
| @@ -214,8 +212,8 @@ public: | @@ -214,8 +212,8 @@ public: | ||
| 214 | * set the original messages, then update the message. | 212 | * set the original messages, then update the message. |
| 215 | */ | 213 | */ |
| 216 | virtual int update(std::string url, http_parser* header, | 214 | virtual int update(std::string url, http_parser* header, |
| 217 | - SrsFastBuffer* body, std::vector<SrsHttpHeaderField>& headers | ||
| 218 | - ); | 215 | + SrsFastBuffer* body, std::vector<SrsHttpHeaderField>& headers |
| 216 | + ); | ||
| 219 | private: | 217 | private: |
| 220 | virtual SrsConnection* connection(); | 218 | virtual SrsConnection* connection(); |
| 221 | public: | 219 | public: |
| @@ -454,7 +452,7 @@ public: | @@ -454,7 +452,7 @@ public: | ||
| 454 | */ | 452 | */ |
| 455 | class SrsFlvStreamEncoder : public ISrsStreamEncoder | 453 | class SrsFlvStreamEncoder : public ISrsStreamEncoder |
| 456 | { | 454 | { |
| 457 | -private: | 455 | +protected: |
| 458 | SrsFlvEncoder* enc; | 456 | SrsFlvEncoder* enc; |
| 459 | public: | 457 | public: |
| 460 | SrsFlvStreamEncoder(); | 458 | SrsFlvStreamEncoder(); |
| @@ -469,6 +467,24 @@ public: | @@ -469,6 +467,24 @@ public: | ||
| 469 | virtual int dump_cache(SrsConsumer* consumer); | 467 | virtual int dump_cache(SrsConsumer* consumer); |
| 470 | }; | 468 | }; |
| 471 | 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 | + | ||
| 472 | /** | 488 | /** |
| 473 | * the ts stream encoder, remux rtmp stream to ts stream. | 489 | * the ts stream encoder, remux rtmp stream to ts stream. |
| 474 | */ | 490 | */ |
| @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | // current release version | 31 | // current release version |
| 32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
| 33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
| 34 | -#define VERSION_REVISION 170 | 34 | +#define VERSION_REVISION 171 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
| @@ -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 |
| @@ -37,16 +37,304 @@ using namespace std; | @@ -37,16 +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> | ||
| 41 | + | ||
| 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 | +} | ||
| 40 | 313 | ||
| 41 | SrsFlvEncoder::SrsFlvEncoder() | 314 | SrsFlvEncoder::SrsFlvEncoder() |
| 42 | { | 315 | { |
| 43 | _fs = NULL; | 316 | _fs = NULL; |
| 44 | 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 | ||
| 45 | } | 327 | } |
| 46 | 328 | ||
| 47 | SrsFlvEncoder::~SrsFlvEncoder() | 329 | SrsFlvEncoder::~SrsFlvEncoder() |
| 48 | { | 330 | { |
| 49 | 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 | ||
| 50 | } | 338 | } |
| 51 | 339 | ||
| 52 | int SrsFlvEncoder::initialize(SrsFileWriter* fs) | 340 | int SrsFlvEncoder::initialize(SrsFileWriter* fs) |
| @@ -115,23 +403,9 @@ int SrsFlvEncoder::write_metadata(char type, char* data, int size) | @@ -115,23 +403,9 @@ int SrsFlvEncoder::write_metadata(char type, char* data, int size) | ||
| 115 | 403 | ||
| 116 | srs_assert(data); | 404 | srs_assert(data); |
| 117 | 405 | ||
| 118 | - // 11 bytes tag header | ||
| 119 | - /*char tag_header[] = { | ||
| 120 | - (char)type, // TagType UB [5], 18 = script data | ||
| 121 | - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 122 | - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 123 | - (char)0x00, // TimestampExtended UI8 | ||
| 124 | - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 125 | - };*/ | ||
| 126 | - | ||
| 127 | - // write data size. | ||
| 128 | - if ((ret = tag_stream->initialize(tag_header, 8)) != ERROR_SUCCESS) { | 406 | + if ((ret = write_metadata_to_cache(type, data, size, tag_header)) != ERROR_SUCCESS) { |
| 129 | return ret; | 407 | return ret; |
| 130 | } | 408 | } |
| 131 | - tag_stream->write_1bytes(type); | ||
| 132 | - tag_stream->write_3bytes(size); | ||
| 133 | - tag_stream->write_3bytes(0x00); | ||
| 134 | - tag_stream->write_1bytes(0x00); | ||
| 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,19 +423,174 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | @@ -149,19 +423,174 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | ||
| 149 | 423 | ||
| 150 | srs_assert(data); | 424 | srs_assert(data); |
| 151 | 425 | ||
| 426 | + if ((ret = write_audio_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) { | ||
| 427 | + return ret; | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | ||
| 431 | + if (!srs_is_client_gracefully_close(ret)) { | ||
| 432 | + srs_error("write flv audio tag failed. ret=%d", ret); | ||
| 433 | + } | ||
| 434 | + return ret; | ||
| 435 | + } | ||
| 436 | + | ||
| 437 | + return ret; | ||
| 438 | +} | ||
| 439 | + | ||
| 440 | +int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | ||
| 441 | +{ | ||
| 442 | + int ret = ERROR_SUCCESS; | ||
| 443 | + | ||
| 444 | + srs_assert(data); | ||
| 445 | + | ||
| 446 | + if ((ret = write_video_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) { | ||
| 447 | + return ret; | ||
| 448 | + } | ||
| 449 | + | ||
| 450 | + if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | ||
| 451 | + srs_error("write flv video tag failed. ret=%d", ret); | ||
| 452 | + return ret; | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + return ret; | ||
| 456 | +} | ||
| 457 | + | ||
| 458 | +int SrsFlvEncoder::size_tag(int data_size) | ||
| 459 | +{ | ||
| 460 | + srs_assert(data_size >= 0); | ||
| 461 | + return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; | ||
| 462 | +} | ||
| 463 | + | ||
| 464 | +#ifdef SRS_PERF_FAST_FLV_ENCODER | ||
| 465 | +int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) | ||
| 466 | +{ | ||
| 467 | + int ret = ERROR_SUCCESS; | ||
| 468 | + | ||
| 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 | + } | ||
| 515 | + } | ||
| 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; | ||
| 534 | + } | ||
| 535 | + | ||
| 536 | + if ((ret = _fs->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) { | ||
| 537 | + if (!srs_is_client_gracefully_close(ret)) { | ||
| 538 | + srs_error("write flv tags failed. ret=%d", ret); | ||
| 539 | + } | ||
| 540 | + return ret; | ||
| 541 | + } | ||
| 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 | + | ||
| 152 | timestamp &= 0x7fffffff; | 581 | timestamp &= 0x7fffffff; |
| 153 | 582 | ||
| 154 | // 11bytes tag header | 583 | // 11bytes tag header |
| 155 | /*char tag_header[] = { | 584 | /*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 | - };*/ | 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 | + };*/ | ||
| 162 | 591 | ||
| 163 | // write data size. | 592 | // write data size. |
| 164 | - if ((ret = tag_stream->initialize(tag_header, 8)) != ERROR_SUCCESS) { | 593 | + if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) { |
| 165 | return ret; | 594 | return ret; |
| 166 | } | 595 | } |
| 167 | tag_stream->write_1bytes(SrsCodecFlvTagAudio); | 596 | tag_stream->write_1bytes(SrsCodecFlvTagAudio); |
| @@ -169,18 +598,12 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | @@ -169,18 +598,12 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size) | ||
| 169 | tag_stream->write_3bytes((int32_t)timestamp); | 598 | tag_stream->write_3bytes((int32_t)timestamp); |
| 170 | // default to little-endian | 599 | // default to little-endian |
| 171 | tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | 600 | tag_stream->write_1bytes((timestamp >> 24) & 0xFF); |
| 172 | - | ||
| 173 | - if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | ||
| 174 | - if (!srs_is_client_gracefully_close(ret)) { | ||
| 175 | - srs_error("write flv audio tag failed. ret=%d", ret); | ||
| 176 | - } | ||
| 177 | - return ret; | ||
| 178 | - } | 601 | + tag_stream->write_3bytes(0x00); |
| 179 | 602 | ||
| 180 | return ret; | 603 | return ret; |
| 181 | } | 604 | } |
| 182 | 605 | ||
| 183 | -int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | 606 | +int SrsFlvEncoder::write_video_to_cache(int64_t timestamp, char* data, int size, char* cache) |
| 184 | { | 607 | { |
| 185 | int ret = ERROR_SUCCESS; | 608 | int ret = ERROR_SUCCESS; |
| 186 | 609 | ||
| @@ -190,15 +613,15 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | @@ -190,15 +613,15 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | ||
| 190 | 613 | ||
| 191 | // 11bytes tag header | 614 | // 11bytes tag header |
| 192 | /*char tag_header[] = { | 615 | /*char tag_header[] = { |
| 193 | - (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video | ||
| 194 | - (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message. | ||
| 195 | - (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies. | ||
| 196 | - (char)0x00, // TimestampExtended UI8 | ||
| 197 | - (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0. | ||
| 198 | - };*/ | 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 | + };*/ | ||
| 199 | 622 | ||
| 200 | // write data size. | 623 | // write data size. |
| 201 | - if ((ret = tag_stream->initialize(tag_header, 8)) != ERROR_SUCCESS) { | 624 | + if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) { |
| 202 | return ret; | 625 | return ret; |
| 203 | } | 626 | } |
| 204 | tag_stream->write_1bytes(SrsCodecFlvTagVideo); | 627 | tag_stream->write_1bytes(SrsCodecFlvTagVideo); |
| @@ -206,19 +629,21 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | @@ -206,19 +629,21 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) | ||
| 206 | tag_stream->write_3bytes((int32_t)timestamp); | 629 | tag_stream->write_3bytes((int32_t)timestamp); |
| 207 | // default to little-endian | 630 | // default to little-endian |
| 208 | tag_stream->write_1bytes((timestamp >> 24) & 0xFF); | 631 | tag_stream->write_1bytes((timestamp >> 24) & 0xFF); |
| 209 | - | ||
| 210 | - if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) { | ||
| 211 | - srs_error("write flv video tag failed. ret=%d", ret); | ||
| 212 | - return ret; | ||
| 213 | - } | 632 | + tag_stream->write_3bytes(0x00); |
| 214 | 633 | ||
| 215 | return ret; | 634 | return ret; |
| 216 | } | 635 | } |
| 217 | 636 | ||
| 218 | -int SrsFlvEncoder::size_tag(int data_size) | 637 | +int SrsFlvEncoder::write_pts_to_cache(int size, char* cache) |
| 219 | { | 638 | { |
| 220 | - srs_assert(data_size >= 0); | ||
| 221 | - return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; | 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; | ||
| 222 | } | 647 | } |
| 223 | 648 | ||
| 224 | int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size) | 649 | int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size) |
| @@ -227,10 +652,9 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | @@ -227,10 +652,9 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | ||
| 227 | 652 | ||
| 228 | // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes. | 653 | // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes. |
| 229 | char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE]; | 654 | char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE]; |
| 230 | - 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) { |
| 231 | return ret; | 656 | return ret; |
| 232 | } | 657 | } |
| 233 | - tag_stream->write_4bytes(tag_size + header_size); | ||
| 234 | 658 | ||
| 235 | iovec iovs[3]; | 659 | iovec iovs[3]; |
| 236 | iovs[0].iov_base = header; | 660 | iovs[0].iov_base = header; |
| @@ -238,7 +662,7 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | @@ -238,7 +662,7 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s | ||
| 238 | iovs[1].iov_base = tag; | 662 | iovs[1].iov_base = tag; |
| 239 | iovs[1].iov_len = tag_size; | 663 | iovs[1].iov_len = tag_size; |
| 240 | iovs[2].iov_base = pre_size; | 664 | iovs[2].iov_base = pre_size; |
| 241 | - iovs[2].iov_len = sizeof(SRS_FLV_PREVIOUS_TAG_SIZE); | 665 | + iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE; |
| 242 | 666 | ||
| 243 | if ((ret = _fs->writev(iovs, 3, NULL)) != ERROR_SUCCESS) { | 667 | if ((ret = _fs->writev(iovs, 3, NULL)) != ERROR_SUCCESS) { |
| 244 | if (!srs_is_client_gracefully_close(ret)) { | 668 | if (!srs_is_client_gracefully_close(ret)) { |
| @@ -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 | class SrsStream; | 39 | class SrsStream; |
| 35 | class SrsFileWriter; | 40 | class SrsFileWriter; |
| 36 | class SrsFileReader; | 41 | class SrsFileReader; |
| @@ -38,6 +43,387 @@ class SrsFileReader; | @@ -38,6 +43,387 @@ class SrsFileReader; | ||
| 38 | #define SRS_FLV_TAG_HEADER_SIZE 11 | 43 | #define SRS_FLV_TAG_HEADER_SIZE 11 |
| 39 | #define SRS_FLV_PREVIOUS_TAG_SIZE 4 | 44 | #define SRS_FLV_PREVIOUS_TAG_SIZE 4 |
| 40 | 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 | + | ||
| 41 | /** | 427 | /** |
| 42 | * encode data to flv file. | 428 | * encode data to flv file. |
| 43 | */ | 429 | */ |
| @@ -91,7 +477,28 @@ public: | @@ -91,7 +477,28 @@ public: | ||
| 91 | * @remark assert data_size is not negative. | 477 | * @remark assert data_size is not negative. |
| 92 | */ | 478 | */ |
| 93 | 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 | ||
| 94 | 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); | ||
| 95 | 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); |
| 96 | }; | 503 | }; |
| 97 | 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 |
| @@ -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 |
-
请 注册 或 登录 后发表评论