winlin

fix #405, improve the HTTP FLV performance to 6k. 2.0.171

@@ -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*)&timestamp;
  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*)&timestamp;
  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*)&timestamp;
  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*)&timestamp;  
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*)&timestamp;  
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*)&timestamp;  
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