diff --git a/README.md b/README.md
index fcbac90fc..be92b76a3 100755
--- a/README.md
+++ b/README.md
@@ -326,7 +326,7 @@ Remark:
* 2014-04-28, [Release v1.0-mainline2](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline2), support [dvr](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DVR), android, [edge](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Edge). 35255 lines.
* 2014-04-07, [Release v1.0-mainline](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline), support [arm](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_LinuxService), http [server](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPServer)/[api](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPApi), [ingest](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleIngest). 30000 lines.
* 2013-12-25, [Release v0.9](https://github.com/simple-rtmp-server/srs/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleDemo). 20926 lines.
-* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.
+* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [SB](https://github.com/simple-rtmp-server/srs-bench). 19186 lines.
* 2013-12-03, [Release v0.7](https://github.com/simple-rtmp-server/srs/releases/tag/0.7), support [live stream transcoding](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_FFMPEG). 17605 lines.
* 2013-11-29, [Release v0.6](https://github.com/simple-rtmp-server/srs/releases/tag/0.6), support [forward](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.
* 2013-11-26, [Release v0.5](https://github.com/simple-rtmp-server/srs/releases/tag/0.5), support [HLS(m3u8)](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.
@@ -525,7 +525,7 @@ Remark:
* v1.0, 2014-06-27, SRS online 30days with RTMP/HLS.
* v1.0, 2014-06-25, fix [#108](https://github.com/simple-rtmp-server/srs/issues/108), support config time jitter for encoder non-monotonical stream. 0.9.133
* v1.0, 2014-06-23, support report summaries in heartbeat. 0.9.132
-* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance#%E6%80%A7%E8%83%BD%E4%BE%8B%E8%A1%8C%E6%8A%A5%E5%91%8A4k) connections(270kbps). 0.9.130
+* v1.0, 2014-06-22, performance refine, support [3k+](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance#performancereport4k) connections(270kbps). 0.9.130
* v1.0, 2014-06-21, support edge [token traverse](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DRM#tokentraverse), fix [#104](https://github.com/simple-rtmp-server/srs/issues/104). 0.9.129
* v1.0, 2014-06-19, add connections count to api summaries. 0.9.127
* v1.0, 2014-06-19, add srs bytes and kbps to api summaries. 0.9.126
@@ -618,7 +618,7 @@ Remark:
* v0.8, 2013-12-08, support multiple http hooks for a event.
* v0.8, 2013-12-07, support http callback hooks, on_connect.
* v0.8, 2013-12-07, support network based cli and json result, add CherryPy 3.2.4.
-* v0.8, 2013-12-07, update http/hls/rtmp load test tool [st_load](https://github.com/winlinvip/st-load), use SRS rtmp sdk.
+* v0.8, 2013-12-07, update http/hls/rtmp load test tool [SB](https://github.com/simple-rtmp-server/srs-bench), use SRS rtmp sdk.
* v0.8, 2013-12-06, support max_connections, drop if exceed.
* v0.8, 2013-12-05, support log_dir, write ffmpeg log to file.
* v0.8, 2013-12-05, fix the forward/hls/encoder bug.
@@ -676,9 +676,9 @@ Performance benchmark history, on virtual box.
* See also: [Performance for x86/x64 Test Guide](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Performance)
* See also: [Performance for RaspberryPi](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_RaspberryPi)
-### Play benchmark
+### Play RTMP benchmark
-The play benchmark by [st-load](https://github.com/winlinvip/st-load):
+The play RTMP benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench):
| Update | SRS | Clients | Type | CPU | Memory | Commit |
@@ -696,9 +696,9 @@ The play benchmark by [st-load](https://github.com/winlinvip/st-load):
| 2014-12-05 | 2.0.57 | 9.0k(9000) | players | 90% | 468MB | [code][p11] |
| 2014-12-07 | 2.0.67 | 10k(10000) | players | 95% | 656MB | [code][p12] |
-### Publish benchmark
+### Publish RTMP benchmark
-The publish benchmark by [st-load](https://github.com/winlinvip/st-load):
+The publish RTMP benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench):
| Update | SRS | Clients | Type | CPU | Memory | Commit |
| ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ |
@@ -712,6 +712,19 @@ The publish benchmark by [st-load](https://github.com/winlinvip/st-load):
| 2014-12-04 | 2.0.51 | 2.5k(2500) | publishers | 91% | 259MB | [code][p4] |
| 2014-12-04 | 2.0.52 | 4.0k(4000) | publishers | 80% | 331MB | [code][p5] |
+### Play HTTP FLV benchmark
+
+The play HTTP FLV benchmark by [SB](https://github.com/simple-rtmp-server/srs-bench):
+
+
+| Update | SRS | Clients | Type | CPU | Memory | Commit |
+| ------------- | --------- | ------------- | ------------- | --------- | -------- | ------------ |
+| 2014-05-24 | 2.0.167 | 1.0k(1000) | players | 82% | 86MB | - |
+| 2014-05-24 | 2.0.168 | 2.3k(2300) | players | 92% | 276MB | [code][p17] |
+| 2014-05-24 | 2.0.169 | 3.0k(3000) | players | 94% | 188MB | [code][p18] |
+| 2014-05-24 | 2.0.170 | 3.0k(3000) | players | 89% | 96MB | [code][p19] |
+| 2014-05-25 | 2.0.171 | 6.0k(6000) | players | 84% | 297MB | [code][p20] |
+
### Latency benchmark
The latency between encoder and player with realtime config(
@@ -826,6 +839,10 @@ Winlin
[p14]: https://github.com/simple-rtmp-server/srs/commit/10297fab519811845b549a8af40a6bcbd23411e8
[p15]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc
[p16]: https://github.com/simple-rtmp-server/srs/commit/0d6b91039d408328caab31a1077d56a809b6bebc
+[p17]: https://github.com/simple-rtmp-server/srs/commit/fc995473eb02c7cf64b5b212b456e11f34aa7984
+[p18]: https://github.com/simple-rtmp-server/srs/commit/960341b9b2b9646270ccfd113b4dd784d9826c73
+[p19]: https://github.com/simple-rtmp-server/srs/commit/4df19ba99a4e4d80cd89b304f9298d343497bec9
+[p20]: https://github.com/simple-rtmp-server/srs/commit/d12fc7fcc5b2e9e3c8ee5c7da01d0e41c8f8ca4a
[authors]: https://github.com/simple-rtmp-server/srs/blob/develop/AUTHORS.txt
[bigthanks]: https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Product#bigthanks
diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp
index 0654ac362..e7e259456 100644
--- a/trunk/src/app/srs_app_http_conn.cpp
+++ b/trunk/src/app/srs_app_http_conn.cpp
@@ -66,11 +66,14 @@ SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io)
content_length = -1;
written = 0;
header_sent = false;
+ nb_iovss_cache = 0;
+ iovss_cache = NULL;
}
SrsHttpResponseWriter::~SrsHttpResponseWriter()
{
srs_freep(hdr);
+ srs_freep(iovss_cache);
}
int SrsHttpResponseWriter::final_request()
@@ -98,8 +101,14 @@ int SrsHttpResponseWriter::write(char* data, int size)
if (!header_wrote) {
write_header(SRS_CONSTS_HTTP_OK);
+
+ if ((ret = send_header(data, size)) != ERROR_SUCCESS) {
+ srs_error("http: send header failed. ret=%d", ret);
+ return ret;
+ }
}
+ // check the bytes send and content length.
written += size;
if (content_length != -1 && written > content_length) {
ret = ERROR_HTTP_CONTENT_LENGTH;
@@ -107,11 +116,6 @@ int SrsHttpResponseWriter::write(char* data, int size)
return ret;
}
- if ((ret = send_header(data, size)) != ERROR_SUCCESS) {
- srs_error("http: send header failed. ret=%d", ret);
- return ret;
- }
-
// ignore NULL content.
if (!data) {
return ret;
@@ -123,19 +127,107 @@ int SrsHttpResponseWriter::write(char* data, int size)
}
// send in chunked encoding.
- std::stringstream ss;
- ss << hex << size << SRS_HTTP_CRLF;
- std::string ch = ss.str();
- if ((ret = skt->write((void*)ch.data(), (int)ch.length(), NULL)) != ERROR_SUCCESS) {
+ int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size);
+
+ iovec iovs[4];
+ iovs[0].iov_base = (char*)header_cache;
+ iovs[0].iov_len = (int)nb_size;
+ iovs[1].iov_base = (char*)SRS_HTTP_CRLF;
+ iovs[1].iov_len = 2;
+ iovs[2].iov_base = (char*)data;
+ iovs[2].iov_len = size;
+ iovs[3].iov_base = (char*)SRS_HTTP_CRLF;
+ iovs[3].iov_len = 2;
+
+ ssize_t nwrite;
+ if ((ret = skt->writev(iovs, 4, &nwrite)) != ERROR_SUCCESS) {
return ret;
}
- if ((ret = skt->write((void*)data, size, NULL)) != ERROR_SUCCESS) {
+
+ return ret;
+}
+
+int SrsHttpResponseWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite)
+{
+ int ret = ERROR_SUCCESS;
+
+ // when header not ready, or not chunked, send one by one.
+ if (!header_wrote || content_length != -1) {
+ ssize_t nwrite = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ iovec* piovc = iov + i;
+ nwrite += piovc->iov_len;
+ if ((ret = write((char*)piovc->iov_base, (int)piovc->iov_len)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (pnwrite) {
+ *pnwrite = nwrite;
+ }
+
return ret;
}
- if ((ret = skt->write((void*)SRS_HTTP_CRLF, 2, NULL)) != ERROR_SUCCESS) {
+
+ // ignore NULL content.
+ if (iovcnt <= 0) {
return ret;
}
+ // send in chunked encoding.
+ int nb_iovss = 3 + iovcnt;
+ iovec* iovss = iovss_cache;
+ if (nb_iovss_cache < nb_iovss) {
+ srs_freep(iovss_cache);
+ nb_iovss_cache = nb_iovss;
+ iovss = iovss_cache = new iovec[nb_iovss];
+ }
+
+ // send in chunked encoding.
+
+ // chunk size.
+ int size = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ iovec* data_iov = iov + i;
+ size += data_iov->iov_len;
+ }
+ written += size;
+
+ // chunk header
+ int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size);
+ iovec* iovs = iovss;
+ iovs[0].iov_base = (char*)header_cache;
+ iovs[0].iov_len = (int)nb_size;
+ iovs++;
+
+ // chunk header eof.
+ iovs[0].iov_base = (char*)SRS_HTTP_CRLF;
+ iovs[0].iov_len = 2;
+ iovs++;
+
+ // chunk body.
+ for (int i = 0; i < iovcnt; i++) {
+ iovec* data_iov = iov + i;
+ iovs[0].iov_base = (char*)data_iov->iov_base;
+ iovs[0].iov_len = (int)data_iov->iov_len;
+ iovs++;
+ }
+
+ // chunk body eof.
+ iovs[0].iov_base = (char*)SRS_HTTP_CRLF;
+ iovs[0].iov_len = 2;
+ iovs++;
+
+ // sendout all ioves.
+ ssize_t nwrite;
+ if ((ret = srs_write_large_iovs(skt, iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ if (pnwrite) {
+ *pnwrite = nwrite;
+ }
+
return ret;
}
@@ -1365,6 +1457,21 @@ int SrsFlvStreamEncoder::dump_cache(SrsConsumer* /*consumer*/)
return ERROR_SUCCESS;
}
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+SrsFastFlvStreamEncoder::SrsFastFlvStreamEncoder()
+{
+}
+
+SrsFastFlvStreamEncoder::~SrsFastFlvStreamEncoder()
+{
+}
+
+int SrsFastFlvStreamEncoder::write_tags(SrsSharedPtrMessage** msgs, int count)
+{
+ return enc->write_tags(msgs, count);
+}
+#endif
+
SrsAacStreamEncoder::SrsAacStreamEncoder()
{
enc = new SrsAacEncoder();
@@ -1509,6 +1616,11 @@ int SrsStreamWriter::write(void* buf, size_t count, ssize_t* pnwrite)
return writer->write((char*)buf, (int)count);
}
+int SrsStreamWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite)
+{
+ return writer->writev(iov, iovcnt, pnwrite);
+}
+
SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c)
{
source = s;
@@ -1530,7 +1642,11 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
srs_assert(entry);
if (srs_string_ends_with(entry->pattern, ".flv")) {
w->header()->set_content_type("video/x-flv");
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+ enc = new SrsFastFlvStreamEncoder();
+#else
enc = new SrsFlvStreamEncoder();
+#endif
} else if (srs_string_ends_with(entry->pattern, ".aac")) {
w->header()->set_content_type("audio/x-aac");
enc = new SrsAacStreamEncoder();
@@ -1576,6 +1692,10 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
}
}
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+ SrsFastFlvStreamEncoder* ffe = dynamic_cast(enc);
+#endif
+
while (true) {
pprint->elapse();
@@ -1602,7 +1722,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
}
// sendout all messages.
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+ if (ffe) {
+ ret = ffe->write_tags(msgs.msgs, count);
+ } else {
+ ret = streaming_send_messages(enc, msgs.msgs, count);
+ }
+#else
ret = streaming_send_messages(enc, msgs.msgs, count);
+#endif
// free the messages.
for (int i = 0; i < count; i++) {
diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp
index 891c744b3..f1d55d06d 100644
--- a/trunk/src/app/srs_app_http_conn.hpp
+++ b/trunk/src/app/srs_app_http_conn.hpp
@@ -71,6 +71,10 @@ class SrsHttpMessage;
#ifdef SRS_AUTO_HTTP_PARSER
+// the http chunked header size,
+// for writev, there always one chunk to send it.
+#define SRS_HTTP_HEADER_CACHE_SIZE 64
+
/**
* response writer use st socket
*/
@@ -79,6 +83,10 @@ class SrsHttpResponseWriter : public ISrsHttpResponseWriter
private:
SrsStSocket* skt;
SrsHttpHeader* hdr;
+private:
+ char header_cache[SRS_HTTP_HEADER_CACHE_SIZE];
+ iovec* iovss_cache;
+ int nb_iovss_cache;
private:
// reply header has been (logically) written
bool header_wrote;
@@ -102,6 +110,7 @@ public:
virtual int final_request();
virtual SrsHttpHeader* header();
virtual int write(char* data, int size);
+ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite);
virtual void write_header(int code);
virtual int send_header(char* data, int size);
};
@@ -203,8 +212,8 @@ public:
* set the original messages, then update the message.
*/
virtual int update(std::string url, http_parser* header,
- SrsFastBuffer* body, std::vector& headers
- );
+ SrsFastBuffer* body, std::vector& headers
+ );
private:
virtual SrsConnection* connection();
public:
@@ -443,7 +452,7 @@ public:
*/
class SrsFlvStreamEncoder : public ISrsStreamEncoder
{
-private:
+protected:
SrsFlvEncoder* enc;
public:
SrsFlvStreamEncoder();
@@ -458,6 +467,24 @@ public:
virtual int dump_cache(SrsConsumer* consumer);
};
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+/**
+ * the fast flv stream encoder.
+ * @see https://github.com/simple-rtmp-server/srs/issues/405
+ */
+class SrsFastFlvStreamEncoder : public SrsFlvStreamEncoder
+{
+public:
+ SrsFastFlvStreamEncoder();
+ virtual ~SrsFastFlvStreamEncoder();
+public:
+ /**
+ * write the tags in a time.
+ */
+ virtual int write_tags(SrsSharedPtrMessage** msgs, int count);
+};
+#endif
+
/**
* the ts stream encoder, remux rtmp stream to ts stream.
*/
@@ -538,6 +565,7 @@ public:
virtual int64_t tellg();
public:
virtual int write(void* buf, size_t count, ssize_t* pnwrite);
+ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite);
};
/**
diff --git a/trunk/src/core/srs_core_performance.hpp b/trunk/src/core/srs_core_performance.hpp
index b6aff9f0a..407ca0fef 100644
--- a/trunk/src/core/srs_core_performance.hpp
+++ b/trunk/src/core/srs_core_performance.hpp
@@ -181,5 +181,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#undef SRS_PERF_SO_SNDBUF_SIZE
#endif
+/**
+ * define the following macro to enable the fast flv encoder.
+ * @see https://github.com/simple-rtmp-server/srs/issues/405
+ */
+#undef SRS_PERF_FAST_FLV_ENCODER
+#define SRS_PERF_FAST_FLV_ENCODER
+
#endif
diff --git a/trunk/src/kernel/srs_kernel_file.cpp b/trunk/src/kernel/srs_kernel_file.cpp
index 40ef01305..75410b568 100644
--- a/trunk/src/kernel/srs_kernel_file.cpp
+++ b/trunk/src/kernel/srs_kernel_file.cpp
@@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include
+#include
using namespace std;
#include
@@ -145,6 +146,27 @@ int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite)
return ret;
}
+int SrsFileWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite)
+{
+ int ret = ERROR_SUCCESS;
+
+ ssize_t nwrite = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ iovec* piov = iov + i;
+ ssize_t this_nwrite = 0;
+ if ((ret = write(piov->iov_base, piov->iov_len, &this_nwrite)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ nwrite += this_nwrite;
+ }
+
+ if (pnwrite) {
+ *pnwrite = nwrite;
+ }
+
+ return ret;
+}
+
SrsFileReader::SrsFileReader()
{
fd = -1;
diff --git a/trunk/src/kernel/srs_kernel_file.hpp b/trunk/src/kernel/srs_kernel_file.hpp
index 4943c1ed7..167e8559d 100644
--- a/trunk/src/kernel/srs_kernel_file.hpp
+++ b/trunk/src/kernel/srs_kernel_file.hpp
@@ -31,6 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
+// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+#include
+#endif
+
/**
* file writer, to write to file.
*/
@@ -62,6 +67,11 @@ public:
* @param pnwrite the output nb_write, NULL to ignore.
*/
virtual int write(void* buf, size_t count, ssize_t* pnwrite);
+ /**
+ * for the HTTP FLV, to writev to improve performance.
+ * @see https://github.com/simple-rtmp-server/srs/issues/405
+ */
+ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite);
};
/**
diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp
index 725a4401d..745b5b083 100644
--- a/trunk/src/kernel/srs_kernel_flv.cpp
+++ b/trunk/src/kernel/srs_kernel_flv.cpp
@@ -37,19 +37,304 @@ using namespace std;
#include
#include
#include
+#include
-#define SRS_FLV_TAG_HEADER_SIZE 11
-#define SRS_FLV_PREVIOUS_TAG_SIZE 4
+SrsMessageHeader::SrsMessageHeader()
+{
+ message_type = 0;
+ payload_length = 0;
+ timestamp_delta = 0;
+ stream_id = 0;
+
+ timestamp = 0;
+ // we always use the connection chunk-id
+ perfer_cid = RTMP_CID_OverConnection;
+}
+
+SrsMessageHeader::~SrsMessageHeader()
+{
+}
+
+bool SrsMessageHeader::is_audio()
+{
+ return message_type == RTMP_MSG_AudioMessage;
+}
+
+bool SrsMessageHeader::is_video()
+{
+ return message_type == RTMP_MSG_VideoMessage;
+}
+
+bool SrsMessageHeader::is_amf0_command()
+{
+ return message_type == RTMP_MSG_AMF0CommandMessage;
+}
+
+bool SrsMessageHeader::is_amf0_data()
+{
+ return message_type == RTMP_MSG_AMF0DataMessage;
+}
+
+bool SrsMessageHeader::is_amf3_command()
+{
+ return message_type == RTMP_MSG_AMF3CommandMessage;
+}
+
+bool SrsMessageHeader::is_amf3_data()
+{
+ return message_type == RTMP_MSG_AMF3DataMessage;
+}
+
+bool SrsMessageHeader::is_window_ackledgement_size()
+{
+ return message_type == RTMP_MSG_WindowAcknowledgementSize;
+}
+
+bool SrsMessageHeader::is_ackledgement()
+{
+ return message_type == RTMP_MSG_Acknowledgement;
+}
+
+bool SrsMessageHeader::is_set_chunk_size()
+{
+ return message_type == RTMP_MSG_SetChunkSize;
+}
+
+bool SrsMessageHeader::is_user_control_message()
+{
+ return message_type == RTMP_MSG_UserControlMessage;
+}
+
+bool SrsMessageHeader::is_set_peer_bandwidth()
+{
+ return message_type == RTMP_MSG_SetPeerBandwidth;
+}
+
+bool SrsMessageHeader::is_aggregate()
+{
+ return message_type == RTMP_MSG_AggregateMessage;
+}
+
+void SrsMessageHeader::initialize_amf0_script(int size, int stream)
+{
+ message_type = RTMP_MSG_AMF0DataMessage;
+ payload_length = (int32_t)size;
+ timestamp_delta = (int32_t)0;
+ timestamp = (int64_t)0;
+ stream_id = (int32_t)stream;
+
+ // amf0 script use connection2 chunk-id
+ perfer_cid = RTMP_CID_OverConnection2;
+}
+
+void SrsMessageHeader::initialize_audio(int size, u_int32_t time, int stream)
+{
+ message_type = RTMP_MSG_AudioMessage;
+ payload_length = (int32_t)size;
+ timestamp_delta = (int32_t)time;
+ timestamp = (int64_t)time;
+ stream_id = (int32_t)stream;
+
+ // audio chunk-id
+ perfer_cid = RTMP_CID_Audio;
+}
+
+void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream)
+{
+ message_type = RTMP_MSG_VideoMessage;
+ payload_length = (int32_t)size;
+ timestamp_delta = (int32_t)time;
+ timestamp = (int64_t)time;
+ stream_id = (int32_t)stream;
+
+ // video chunk-id
+ perfer_cid = RTMP_CID_Video;
+}
+
+SrsCommonMessage::SrsCommonMessage()
+{
+ payload = NULL;
+ size = 0;
+}
+
+SrsCommonMessage::~SrsCommonMessage()
+{
+ srs_freep(payload);
+}
+
+SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload()
+{
+ payload = NULL;
+ size = 0;
+ shared_count = 0;
+}
+
+SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload()
+{
+ srs_freep(payload);
+}
+
+SrsSharedPtrMessage::SrsSharedPtrMessage()
+{
+ ptr = NULL;
+}
+
+SrsSharedPtrMessage::~SrsSharedPtrMessage()
+{
+ if (ptr) {
+ if (ptr->shared_count == 0) {
+ srs_freep(ptr);
+ } else {
+ ptr->shared_count--;
+ }
+ }
+}
+
+int SrsSharedPtrMessage::create(SrsCommonMessage* msg)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ // to prevent double free of payload:
+ // initialize already attach the payload of msg,
+ // detach the payload to transfer the owner to shared ptr.
+ msg->payload = NULL;
+ msg->size = 0;
+
+ return ret;
+}
+
+int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size)
+{
+ int ret = ERROR_SUCCESS;
+
+ if (ptr) {
+ ret = ERROR_SYSTEM_ASSERT_FAILED;
+ srs_error("should not set the payload twice. ret=%d", ret);
+ srs_assert(false);
+
+ return ret;
+ }
+
+ ptr = new SrsSharedPtrPayload();
+
+ // direct attach the data.
+ if (pheader) {
+ ptr->header.message_type = pheader->message_type;
+ ptr->header.payload_length = size;
+ ptr->header.perfer_cid = pheader->perfer_cid;
+ this->timestamp = pheader->timestamp;
+ this->stream_id = pheader->stream_id;
+ }
+ ptr->payload = payload;
+ ptr->size = size;
+
+ // message can access it.
+ this->payload = ptr->payload;
+ this->size = ptr->size;
+
+ return ret;
+}
+
+int SrsSharedPtrMessage::count()
+{
+ srs_assert(ptr);
+ return ptr->shared_count;
+}
+
+bool SrsSharedPtrMessage::check(int stream_id)
+{
+ // we donot use the complex basic header,
+ // ensure the basic header is 1bytes.
+ if (ptr->header.perfer_cid < 2) {
+ srs_info("change the chunk_id=%d to default=%d",
+ ptr->header.perfer_cid, RTMP_CID_ProtocolControl);
+ ptr->header.perfer_cid = RTMP_CID_ProtocolControl;
+ }
+
+ // we assume that the stream_id in a group must be the same.
+ if (this->stream_id == stream_id) {
+ return true;
+ }
+ this->stream_id = stream_id;
+
+ return false;
+}
+
+bool SrsSharedPtrMessage::is_av()
+{
+ return ptr->header.message_type == RTMP_MSG_AudioMessage
+ || ptr->header.message_type == RTMP_MSG_VideoMessage;
+}
+
+bool SrsSharedPtrMessage::is_audio()
+{
+ return ptr->header.message_type == RTMP_MSG_AudioMessage;
+}
+
+bool SrsSharedPtrMessage::is_video()
+{
+ return ptr->header.message_type == RTMP_MSG_VideoMessage;
+}
+
+int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0)
+{
+ if (c0) {
+ return srs_chunk_header_c0(
+ ptr->header.perfer_cid, timestamp, ptr->header.payload_length,
+ ptr->header.message_type, stream_id,
+ cache, nb_cache);
+ } else {
+ return srs_chunk_header_c3(
+ ptr->header.perfer_cid, timestamp,
+ cache, nb_cache);
+ }
+}
+
+SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
+{
+ srs_assert(ptr);
+
+ SrsSharedPtrMessage* copy = new SrsSharedPtrMessage();
+
+ copy->ptr = ptr;
+ ptr->shared_count++;
+
+ copy->timestamp = timestamp;
+ copy->stream_id = stream_id;
+ copy->payload = ptr->payload;
+ copy->size = ptr->size;
+
+ return copy;
+}
SrsFlvEncoder::SrsFlvEncoder()
{
_fs = NULL;
tag_stream = new SrsStream();
+
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+ nb_tag_headers = 0;
+ tag_headers = NULL;
+ nb_iovss_cache = 0;
+ iovss_cache = NULL;
+ nb_ppts = 0;
+ ppts = NULL;
+#endif
}
SrsFlvEncoder::~SrsFlvEncoder()
{
srs_freep(tag_stream);
+
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+ srs_freep(tag_headers);
+ srs_freep(iovss_cache);
+ srs_freep(ppts);
+#endif
}
int SrsFlvEncoder::initialize(SrsFileWriter* fs)
@@ -118,20 +403,9 @@ int SrsFlvEncoder::write_metadata(char type, char* data, int size)
srs_assert(data);
- // 11 bytes tag header
- char tag_header[] = {
- (char)type, // TagType UB [5], 18 = script data
- (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
- (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
- (char)0x00, // TimestampExtended UI8
- (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
- };
-
- // write data size.
- if ((ret = tag_stream->initialize(tag_header + 1, 3)) != ERROR_SUCCESS) {
+ if ((ret = write_metadata_to_cache(type, data, size, tag_header)) != ERROR_SUCCESS) {
return ret;
}
- tag_stream->write_3bytes(size);
if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
@@ -149,25 +423,9 @@ int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size)
srs_assert(data);
- timestamp &= 0x7fffffff;
-
- // 11bytes tag header
- char tag_header[] = {
- (char)SrsCodecFlvTagAudio, // TagType UB [5], 8 = audio
- (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
- (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
- (char)0x00, // TimestampExtended UI8
- (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
- };
-
- // write data size.
- if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) {
+ if ((ret = write_audio_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {
return ret;
}
- tag_stream->write_3bytes(size);
- tag_stream->write_3bytes((int32_t)timestamp);
- // default to little-endian
- tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
@@ -185,25 +443,9 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size)
srs_assert(data);
- timestamp &= 0x7fffffff;
-
- // 11bytes tag header
- char tag_header[] = {
- (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video
- (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
- (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
- (char)0x00, // TimestampExtended UI8
- (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
- };
-
- // write data size.
- if ((ret = tag_stream->initialize(tag_header + 1, 7)) != ERROR_SUCCESS) {
+ if ((ret = write_video_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {
return ret;
}
- tag_stream->write_3bytes(size);
- tag_stream->write_3bytes((int32_t)timestamp);
- // default to little-endian
- tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
srs_error("write flv video tag failed. ret=%d", ret);
@@ -219,35 +461,212 @@ int SrsFlvEncoder::size_tag(int data_size)
return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE;
}
+#ifdef SRS_PERF_FAST_FLV_ENCODER
+int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count)
+{
+ int ret = ERROR_SUCCESS;
+
+ // realloc the iovss.
+ int nb_iovss = 3 * count;
+ iovec* iovss = iovss_cache;
+ if (nb_iovss_cache < nb_iovss) {
+ srs_freep(iovss_cache);
+
+ nb_iovss_cache = nb_iovss;
+ iovss = iovss_cache = new iovec[nb_iovss];
+ }
+
+ // realloc the tag headers.
+ char* cache = tag_headers;
+ if (nb_tag_headers < count) {
+ srs_freep(tag_headers);
+
+ nb_tag_headers = count;
+ cache = tag_headers = new char[SRS_FLV_TAG_HEADER_SIZE * count];
+ }
+
+ // realloc the pts.
+ char* pts = ppts;
+ if (nb_ppts < count) {
+ srs_freep(ppts);
+
+ nb_ppts = count;
+ pts = ppts = new char[SRS_FLV_PREVIOUS_TAG_SIZE * count];
+ }
+
+ // the cache is ok, write each messages.
+ iovec* iovs = iovss;
+ for (int i = 0; i < count; i++) {
+ SrsSharedPtrMessage* msg = msgs[i];
+
+ // cache all flv header.
+ if (msg->is_audio()) {
+ if ((ret = write_audio_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ } else if (msg->is_video()) {
+ if ((ret = write_video_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ } else {
+ if ((ret = write_metadata_to_cache(SrsCodecFlvTagScript, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ }
+
+ // cache all pts.
+ if ((ret = write_pts_to_cache(SRS_FLV_TAG_HEADER_SIZE + msg->size, pts)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ // all ioves.
+ iovs[0].iov_base = cache;
+ iovs[0].iov_len = SRS_FLV_TAG_HEADER_SIZE;
+ iovs[1].iov_base = msg->payload;
+ iovs[1].iov_len = msg->size;
+ iovs[2].iov_base = pts;
+ iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE;
+
+ // move next.
+ cache += SRS_FLV_TAG_HEADER_SIZE;
+ pts += SRS_FLV_PREVIOUS_TAG_SIZE;
+ iovs += 3;
+ }
+
+ if ((ret = _fs->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) {
+ if (!srs_is_client_gracefully_close(ret)) {
+ srs_error("write flv tags failed. ret=%d", ret);
+ }
+ return ret;
+ }
+
+ return ret;
+}
+#endif
+
+int SrsFlvEncoder::write_metadata_to_cache(char type, char* data, int size, char* cache)
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_assert(data);
+
+ // 11 bytes tag header
+ /*char tag_header[] = {
+ (char)type, // TagType UB [5], 18 = script data
+ (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
+ (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
+ (char)0x00, // TimestampExtended UI8
+ (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
+ };*/
+
+ // write data size.
+ if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ tag_stream->write_1bytes(type);
+ tag_stream->write_3bytes(size);
+ tag_stream->write_3bytes(0x00);
+ tag_stream->write_1bytes(0x00);
+ tag_stream->write_3bytes(0x00);
+
+ return ret;
+}
+
+int SrsFlvEncoder::write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache)
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_assert(data);
+
+ timestamp &= 0x7fffffff;
+
+ // 11bytes tag header
+ /*char tag_header[] = {
+ (char)SrsCodecFlvTagAudio, // TagType UB [5], 8 = audio
+ (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
+ (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
+ (char)0x00, // TimestampExtended UI8
+ (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
+ };*/
+
+ // write data size.
+ if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ tag_stream->write_1bytes(SrsCodecFlvTagAudio);
+ tag_stream->write_3bytes(size);
+ tag_stream->write_3bytes((int32_t)timestamp);
+ // default to little-endian
+ tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
+ tag_stream->write_3bytes(0x00);
+
+ return ret;
+}
+
+int SrsFlvEncoder::write_video_to_cache(int64_t timestamp, char* data, int size, char* cache)
+{
+ int ret = ERROR_SUCCESS;
+
+ srs_assert(data);
+
+ timestamp &= 0x7fffffff;
+
+ // 11bytes tag header
+ /*char tag_header[] = {
+ (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video
+ (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
+ (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
+ (char)0x00, // TimestampExtended UI8
+ (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
+ };*/
+
+ // write data size.
+ if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ tag_stream->write_1bytes(SrsCodecFlvTagVideo);
+ tag_stream->write_3bytes(size);
+ tag_stream->write_3bytes((int32_t)timestamp);
+ // default to little-endian
+ tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
+ tag_stream->write_3bytes(0x00);
+
+ return ret;
+}
+
+int SrsFlvEncoder::write_pts_to_cache(int size, char* cache)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = tag_stream->initialize(cache, SRS_FLV_PREVIOUS_TAG_SIZE)) != ERROR_SUCCESS) {
+ return ret;
+ }
+ tag_stream->write_4bytes(size);
+
+ return ret;
+}
+
int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size)
{
int ret = ERROR_SUCCESS;
- // write tag header.
- if ((ret = _fs->write(header, header_size, NULL)) != ERROR_SUCCESS) {
- if (!srs_is_client_gracefully_close(ret)) {
- srs_error("write flv tag header failed. ret=%d", ret);
- }
- return ret;
- }
-
- // write tag data.
- if ((ret = _fs->write(tag, tag_size, NULL)) != ERROR_SUCCESS) {
- if (!srs_is_client_gracefully_close(ret)) {
- srs_error("write flv tag failed. ret=%d", ret);
- }
- return ret;
- }
-
// PreviousTagSizeN UI32 Size of last tag, including its header, in bytes.
char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE];
- if ((ret = tag_stream->initialize(pre_size, SRS_FLV_PREVIOUS_TAG_SIZE)) != ERROR_SUCCESS) {
+ if ((ret = write_pts_to_cache(tag_size + header_size, pre_size)) != ERROR_SUCCESS) {
return ret;
}
- tag_stream->write_4bytes(tag_size + header_size);
- if ((ret = _fs->write(pre_size, sizeof(pre_size), NULL)) != ERROR_SUCCESS) {
+
+ iovec iovs[3];
+ iovs[0].iov_base = header;
+ iovs[0].iov_len = header_size;
+ iovs[1].iov_base = tag;
+ iovs[1].iov_len = tag_size;
+ iovs[2].iov_base = pre_size;
+ iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE;
+
+ if ((ret = _fs->writev(iovs, 3, NULL)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
- srs_error("write flv previous tag size failed. ret=%d", ret);
+ srs_error("write flv tag failed. ret=%d", ret);
}
return ret;
}
diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp
index e3564f7db..a33e1a320 100644
--- a/trunk/src/kernel/srs_kernel_flv.hpp
+++ b/trunk/src/kernel/srs_kernel_flv.hpp
@@ -31,10 +31,399 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
+// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+#include
+#endif
+
class SrsStream;
class SrsFileWriter;
class SrsFileReader;
+#define SRS_FLV_TAG_HEADER_SIZE 11
+#define SRS_FLV_PREVIOUS_TAG_SIZE 4
+
+/****************************************************************************
+ *****************************************************************************
+ ****************************************************************************/
+/**
+ 5. Protocol Control Messages
+ RTMP reserves message type IDs 1-7 for protocol control messages.
+ These messages contain information needed by the RTM Chunk Stream
+ protocol or RTMP itself. Protocol messages with IDs 1 & 2 are
+ reserved for usage with RTM Chunk Stream protocol. Protocol messages
+ with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID
+ 7 is used between edge server and origin server.
+ */
+#define RTMP_MSG_SetChunkSize 0x01
+#define RTMP_MSG_AbortMessage 0x02
+#define RTMP_MSG_Acknowledgement 0x03
+#define RTMP_MSG_UserControlMessage 0x04
+#define RTMP_MSG_WindowAcknowledgementSize 0x05
+#define RTMP_MSG_SetPeerBandwidth 0x06
+#define RTMP_MSG_EdgeAndOriginServerCommand 0x07
+/**
+ 3. Types of messages
+ The server and the client send messages over the network to
+ communicate with each other. The messages can be of any type which
+ includes audio messages, video messages, command messages, shared
+ object messages, data messages, and user control messages.
+ 3.1. Command message
+ Command messages carry the AMF-encoded commands between the client
+ and the server. These messages have been assigned message type value
+ of 20 for AMF0 encoding and message type value of 17 for AMF3
+ encoding. These messages are sent to perform some operations like
+ connect, createStream, publish, play, pause on the peer. Command
+ messages like onstatus, result etc. are used to inform the sender
+ about the status of the requested commands. A command message
+ consists of command name, transaction ID, and command object that
+ contains related parameters. A client or a server can request Remote
+ Procedure Calls (RPC) over streams that are communicated using the
+ command messages to the peer.
+ */
+#define RTMP_MSG_AMF3CommandMessage 17 // 0x11
+#define RTMP_MSG_AMF0CommandMessage 20 // 0x14
+/**
+ 3.2. Data message
+ The client or the server sends this message to send Metadata or any
+ user data to the peer. Metadata includes details about the
+ data(audio, video etc.) like creation time, duration, theme and so
+ on. These messages have been assigned message type value of 18 for
+ AMF0 and message type value of 15 for AMF3.
+ */
+#define RTMP_MSG_AMF0DataMessage 18 // 0x12
+#define RTMP_MSG_AMF3DataMessage 15 // 0x0F
+/**
+ 3.3. Shared object message
+ A shared object is a Flash object (a collection of name value pairs)
+ that are in synchronization across multiple clients, instances, and
+ so on. The message types kMsgContainer=19 for AMF0 and
+ kMsgContainerEx=16 for AMF3 are reserved for shared object events.
+ Each message can contain multiple events.
+ */
+#define RTMP_MSG_AMF3SharedObject 16 // 0x10
+#define RTMP_MSG_AMF0SharedObject 19 // 0x13
+/**
+ 3.4. Audio message
+ The client or the server sends this message to send audio data to the
+ peer. The message type value of 8 is reserved for audio messages.
+ */
+#define RTMP_MSG_AudioMessage 8 // 0x08
+/* *
+ 3.5. Video message
+ The client or the server sends this message to send video data to the
+ peer. The message type value of 9 is reserved for video messages.
+ These messages are large and can delay the sending of other type of
+ messages. To avoid such a situation, the video message is assigned
+ the lowest priority.
+ */
+#define RTMP_MSG_VideoMessage 9 // 0x09
+/**
+ 3.6. Aggregate message
+ An aggregate message is a single message that contains a list of submessages.
+ The message type value of 22 is reserved for aggregate
+ messages.
+ */
+#define RTMP_MSG_AggregateMessage 22 // 0x16
+
+/****************************************************************************
+ *****************************************************************************
+ ****************************************************************************/
+/**
+ * the chunk stream id used for some under-layer message,
+ * for example, the PC(protocol control) message.
+ */
+#define RTMP_CID_ProtocolControl 0x02
+/**
+ * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection.
+ * generally use 0x03.
+ */
+#define RTMP_CID_OverConnection 0x03
+/**
+ * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection,
+ * the midst state(we guess).
+ * rarely used, e.g. onStatus(NetStream.Play.Reset).
+ */
+#define RTMP_CID_OverConnection2 0x04
+/**
+ * the stream message(amf0/amf3), over NetStream.
+ * generally use 0x05.
+ */
+#define RTMP_CID_OverStream 0x05
+/**
+ * the stream message(amf0/amf3), over NetStream, the midst state(we guess).
+ * rarely used, e.g. play("mp4:mystram.f4v")
+ */
+#define RTMP_CID_OverStream2 0x08
+/**
+ * the stream message(video), over NetStream
+ * generally use 0x06.
+ */
+#define RTMP_CID_Video 0x06
+/**
+ * the stream message(audio), over NetStream.
+ * generally use 0x07.
+ */
+#define RTMP_CID_Audio 0x07
+
+/**
+ * 6.1. Chunk Format
+ * Extended timestamp: 0 or 4 bytes
+ * This field MUST be sent when the normal timsestamp is set to
+ * 0xffffff, it MUST NOT be sent if the normal timestamp is set to
+ * anything else. So for values less than 0xffffff the normal
+ * timestamp field SHOULD be used in which case the extended timestamp
+ * MUST NOT be present. For values greater than or equal to 0xffffff
+ * the normal timestamp field MUST NOT be used and MUST be set to
+ * 0xffffff and the extended timestamp MUST be sent.
+ */
+#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF
+
+/**
+ * 4.1. Message Header
+ */
+class SrsMessageHeader
+{
+public:
+ /**
+ * 3bytes.
+ * Three-byte field that contains a timestamp delta of the message.
+ * @remark, only used for decoding message from chunk stream.
+ */
+ int32_t timestamp_delta;
+ /**
+ * 3bytes.
+ * Three-byte field that represents the size of the payload in bytes.
+ * It is set in big-endian format.
+ */
+ int32_t payload_length;
+ /**
+ * 1byte.
+ * One byte field to represent the message type. A range of type IDs
+ * (1-7) are reserved for protocol control messages.
+ */
+ int8_t message_type;
+ /**
+ * 4bytes.
+ * Four-byte field that identifies the stream of the message. These
+ * bytes are set in little-endian format.
+ */
+ int32_t stream_id;
+
+ /**
+ * Four-byte field that contains a timestamp of the message.
+ * The 4 bytes are packed in the big-endian order.
+ * @remark, used as calc timestamp when decode and encode time.
+ * @remark, we use 64bits for large time for jitter detect and hls.
+ */
+ int64_t timestamp;
+public:
+ /**
+ * get the perfered cid(chunk stream id) which sendout over.
+ * set at decoding, and canbe used for directly send message,
+ * for example, dispatch to all connections.
+ */
+ int perfer_cid;
+public:
+ SrsMessageHeader();
+ virtual ~SrsMessageHeader();
+public:
+ bool is_audio();
+ bool is_video();
+ bool is_amf0_command();
+ bool is_amf0_data();
+ bool is_amf3_command();
+ bool is_amf3_data();
+ bool is_window_ackledgement_size();
+ bool is_ackledgement();
+ bool is_set_chunk_size();
+ bool is_user_control_message();
+ bool is_set_peer_bandwidth();
+ bool is_aggregate();
+public:
+ /**
+ * create a amf0 script header, set the size and stream_id.
+ */
+ void initialize_amf0_script(int size, int stream);
+ /**
+ * create a audio header, set the size, timestamp and stream_id.
+ */
+ void initialize_audio(int size, u_int32_t time, int stream);
+ /**
+ * create a video header, set the size, timestamp and stream_id.
+ */
+ void initialize_video(int size, u_int32_t time, int stream);
+};
+
+/**
+ * message is raw data RTMP message, bytes oriented,
+ * protcol always recv RTMP message, and can send RTMP message or RTMP packet.
+ * the common message is read from underlay protocol sdk.
+ * while the shared ptr message used to copy and send.
+ */
+class SrsCommonMessage
+{
+ // 4.1. Message Header
+public:
+ SrsMessageHeader header;
+ // 4.2. Message Payload
+public:
+ /**
+ * current message parsed size,
+ * size <= header.payload_length
+ * for the payload maybe sent in multiple chunks.
+ */
+ int size;
+ /**
+ * the payload of message, the SrsCommonMessage never know about the detail of payload,
+ * user must use SrsProtocol.decode_message to get concrete packet.
+ * @remark, not all message payload can be decoded to packet. for example,
+ * video/audio packet use raw bytes, no video/audio packet.
+ */
+ char* payload;
+public:
+ SrsCommonMessage();
+public:
+ virtual ~SrsCommonMessage();
+};
+
+/**
+ * the message header for shared ptr message.
+ * only the message for all msgs are same.
+ */
+struct SrsSharedMessageHeader
+{
+ /**
+ * 3bytes.
+ * Three-byte field that represents the size of the payload in bytes.
+ * It is set in big-endian format.
+ */
+ int32_t payload_length;
+ /**
+ * 1byte.
+ * One byte field to represent the message type. A range of type IDs
+ * (1-7) are reserved for protocol control messages.
+ */
+ int8_t message_type;
+ /**
+ * get the perfered cid(chunk stream id) which sendout over.
+ * set at decoding, and canbe used for directly send message,
+ * for example, dispatch to all connections.
+ */
+ int perfer_cid;
+};
+
+/**
+ * shared ptr message.
+ * for audio/video/data message that need less memory copy.
+ * and only for output.
+ *
+ * create first object by constructor and create(),
+ * use copy if need reference count message.
+ *
+ */
+class SrsSharedPtrMessage
+{
+ // 4.1. Message Header
+public:
+ // the header can shared, only set the timestamp and stream id.
+ // @see https://github.com/simple-rtmp-server/srs/issues/251
+ //SrsSharedMessageHeader header;
+ /**
+ * Four-byte field that contains a timestamp of the message.
+ * The 4 bytes are packed in the big-endian order.
+ * @remark, used as calc timestamp when decode and encode time.
+ * @remark, we use 64bits for large time for jitter detect and hls.
+ */
+ int64_t timestamp;
+ /**
+ * 4bytes.
+ * Four-byte field that identifies the stream of the message. These
+ * bytes are set in big-endian format.
+ */
+ int32_t stream_id;
+ // 4.2. Message Payload
+public:
+ /**
+ * current message parsed size,
+ * size <= header.payload_length
+ * for the payload maybe sent in multiple chunks.
+ */
+ int size;
+ /**
+ * the payload of message, the SrsCommonMessage never know about the detail of payload,
+ * user must use SrsProtocol.decode_message to get concrete packet.
+ * @remark, not all message payload can be decoded to packet. for example,
+ * video/audio packet use raw bytes, no video/audio packet.
+ */
+ char* payload;
+private:
+ class SrsSharedPtrPayload
+ {
+ public:
+ // shared message header.
+ // @see https://github.com/simple-rtmp-server/srs/issues/251
+ SrsSharedMessageHeader header;
+ // actual shared payload.
+ char* payload;
+ // size of payload.
+ int size;
+ // the reference count
+ int shared_count;
+ public:
+ SrsSharedPtrPayload();
+ virtual ~SrsSharedPtrPayload();
+ };
+ SrsSharedPtrPayload* ptr;
+public:
+ SrsSharedPtrMessage();
+ virtual ~SrsSharedPtrMessage();
+public:
+ /**
+ * create shared ptr message,
+ * copy header, manage the payload of msg,
+ * set the payload to NULL to prevent double free.
+ * @remark payload of msg set to NULL if success.
+ */
+ virtual int create(SrsCommonMessage* msg);
+ /**
+ * create shared ptr message,
+ * from the header and payload.
+ * @remark user should never free the payload.
+ * @param pheader, the header to copy to the message. NULL to ignore.
+ */
+ virtual int create(SrsMessageHeader* pheader, char* payload, int size);
+ /**
+ * get current reference count.
+ * when this object created, count set to 0.
+ * if copy() this object, count increase 1.
+ * if this or copy deleted, free payload when count is 0, or count--.
+ * @remark, assert object is created.
+ */
+ virtual int count();
+ /**
+ * check perfer cid and stream id.
+ * @return whether stream id already set.
+ */
+ virtual bool check(int stream_id);
+public:
+ virtual bool is_av();
+ virtual bool is_audio();
+ virtual bool is_video();
+public:
+ /**
+ * generate the chunk header to cache.
+ * @return the size of header.
+ */
+ virtual int chunk_header(char* cache, int nb_cache, bool c0);
+public:
+ /**
+ * copy current shared ptr message, use ref-count.
+ * @remark, assert object is created.
+ */
+ virtual SrsSharedPtrMessage* copy();
+};
+
/**
* encode data to flv file.
*/
@@ -44,6 +433,7 @@ private:
SrsFileWriter* _fs;
private:
SrsStream* tag_stream;
+ char tag_header[SRS_FLV_TAG_HEADER_SIZE];
public:
SrsFlvEncoder();
virtual ~SrsFlvEncoder();
@@ -87,7 +477,28 @@ public:
* @remark assert data_size is not negative.
*/
static int size_tag(int data_size);
+#ifdef SRS_PERF_FAST_FLV_ENCODER
private:
+ // cache tag header.
+ int nb_tag_headers;
+ char* tag_headers;
+ // cache pps(previous tag size)
+ int nb_ppts;
+ char* ppts;
+ // cache iovss.
+ int nb_iovss_cache;
+ iovec* iovss_cache;
+public:
+ /**
+ * write the tags in a time.
+ */
+ virtual int write_tags(SrsSharedPtrMessage** msgs, int count);
+#endif
+private:
+ virtual int write_metadata_to_cache(char type, char* data, int size, char* cache);
+ virtual int write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache);
+ virtual int write_video_to_cache(int64_t timestamp, char* data, int size, char* cache);
+ virtual int write_pts_to_cache(int size, char* cache);
virtual int write_tag(char* header, int header_size, char* tag, int tag_size);
};
diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp
index 58fb3fa6a..f469e694e 100644
--- a/trunk/src/kernel/srs_kernel_utility.cpp
+++ b/trunk/src/kernel/srs_kernel_utility.cpp
@@ -40,6 +40,7 @@ using namespace std;
#include
#include
#include
+#include
// this value must:
// 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)
return len;
}
+int srs_chunk_header_c0(
+ int perfer_cid, u_int32_t timestamp, int32_t payload_length,
+ int8_t message_type, int32_t stream_id,
+ char* cache, int nb_cache
+) {
+ // to directly set the field.
+ char* pp = NULL;
+
+ // generate the header.
+ char* p = cache;
+
+ // no header.
+ if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) {
+ return 0;
+ }
+
+ // write new chunk stream header, fmt is 0
+ *p++ = 0x00 | (perfer_cid & 0x3F);
+
+ // chunk message header, 11 bytes
+ // timestamp, 3bytes, big-endian
+ if (timestamp < RTMP_EXTENDED_TIMESTAMP) {
+ pp = (char*)×tamp;
+ *p++ = pp[2];
+ *p++ = pp[1];
+ *p++ = pp[0];
+ } else {
+ *p++ = 0xFF;
+ *p++ = 0xFF;
+ *p++ = 0xFF;
+ }
+
+ // message_length, 3bytes, big-endian
+ pp = (char*)&payload_length;
+ *p++ = pp[2];
+ *p++ = pp[1];
+ *p++ = pp[0];
+
+ // message_type, 1bytes
+ *p++ = message_type;
+
+ // stream_id, 4bytes, little-endian
+ pp = (char*)&stream_id;
+ *p++ = pp[0];
+ *p++ = pp[1];
+ *p++ = pp[2];
+ *p++ = pp[3];
+
+ // for c0
+ // chunk extended timestamp header, 0 or 4 bytes, big-endian
+ //
+ // for c3:
+ // chunk extended timestamp header, 0 or 4 bytes, big-endian
+ // 6.1.3. Extended Timestamp
+ // This field is transmitted only when the normal time stamp in the
+ // chunk message header is set to 0x00ffffff. If normal time stamp is
+ // set to any value less than 0x00ffffff, this field MUST NOT be
+ // present. This field MUST NOT be present if the timestamp field is not
+ // present. Type 3 chunks MUST NOT have this field.
+ // adobe changed for Type3 chunk:
+ // FMLE always sendout the extended-timestamp,
+ // must send the extended-timestamp to FMS,
+ // must send the extended-timestamp to flash-player.
+ // @see: ngx_rtmp_prepare_message
+ // @see: http://blog.csdn.net/win_lin/article/details/13363699
+ // TODO: FIXME: extract to outer.
+ if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
+ pp = (char*)×tamp;
+ *p++ = pp[3];
+ *p++ = pp[2];
+ *p++ = pp[1];
+ *p++ = pp[0];
+ }
+
+ // always has header
+ return p - cache;
+}
+
+int srs_chunk_header_c3(
+ int perfer_cid, u_int32_t timestamp,
+ char* cache, int nb_cache
+) {
+ // to directly set the field.
+ char* pp = NULL;
+
+ // generate the header.
+ char* p = cache;
+
+ // no header.
+ if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) {
+ return 0;
+ }
+
+ // write no message header chunk stream, fmt is 3
+ // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header,
+ // SRS will rollback to 1B chunk header.
+ *p++ = 0xC0 | (perfer_cid & 0x3F);
+
+ // for c0
+ // chunk extended timestamp header, 0 or 4 bytes, big-endian
+ //
+ // for c3:
+ // chunk extended timestamp header, 0 or 4 bytes, big-endian
+ // 6.1.3. Extended Timestamp
+ // This field is transmitted only when the normal time stamp in the
+ // chunk message header is set to 0x00ffffff. If normal time stamp is
+ // set to any value less than 0x00ffffff, this field MUST NOT be
+ // present. This field MUST NOT be present if the timestamp field is not
+ // present. Type 3 chunks MUST NOT have this field.
+ // adobe changed for Type3 chunk:
+ // FMLE always sendout the extended-timestamp,
+ // must send the extended-timestamp to FMS,
+ // must send the extended-timestamp to flash-player.
+ // @see: ngx_rtmp_prepare_message
+ // @see: http://blog.csdn.net/win_lin/article/details/13363699
+ // TODO: FIXME: extract to outer.
+ if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
+ pp = (char*)×tamp;
+ *p++ = pp[3];
+ *p++ = pp[2];
+ *p++ = pp[1];
+ *p++ = pp[0];
+ }
+
+ // always has header
+ return p - cache;
+}
+
diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp
index 678cd6d94..d6406e33d 100644
--- a/trunk/src/kernel/srs_kernel_utility.hpp
+++ b/trunk/src/kernel/srs_kernel_utility.hpp
@@ -138,5 +138,28 @@ extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, i
*/
extern int ff_hex_to_data(u_int8_t* data, const char* p);
+/**
+ * generate the c0 chunk header for msg.
+ * @param cache, the cache to write header.
+ * @param nb_cache, the size of cache.
+ * @return the size of header. 0 if cache not enough.
+ */
+extern int srs_chunk_header_c0(
+ int perfer_cid, u_int32_t timestamp, int32_t payload_length,
+ int8_t message_type, int32_t stream_id,
+ char* cache, int nb_cache
+ );
+
+/**
+ * generate the c3 chunk header for msg.
+ * @param cache, the cache to write header.
+ * @param nb_cache, the size of cache.
+ * @return the size of header. 0 if cache not enough.
+ */
+extern int srs_chunk_header_c3(
+ int perfer_cid, u_int32_t timestamp,
+ char* cache, int nb_cache
+ );
+
#endif
diff --git a/trunk/src/protocol/srs_http_stack.hpp b/trunk/src/protocol/srs_http_stack.hpp
index 8b14aad0e..a127156fa 100644
--- a/trunk/src/protocol/srs_http_stack.hpp
+++ b/trunk/src/protocol/srs_http_stack.hpp
@@ -33,6 +33,11 @@
#include
#include
+// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+#include
+#endif
+
class SrsFileReader;
class SrsHttpHeader;
class ISrsHttpMessage;
@@ -188,6 +193,11 @@ public:
// the initial 512 bytes of written data to DetectContentType.
// @param data, the data to send. NULL to flush header only.
virtual int write(char* data, int size) = 0;
+ /**
+ * for the HTTP FLV, to writev to improve performance.
+ * @see https://github.com/simple-rtmp-server/srs/issues/405
+ */
+ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite) = 0;
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp
index d107f59b8..c0ca5e9b5 100644
--- a/trunk/src/protocol/srs_rtmp_stack.cpp
+++ b/trunk/src/protocol/srs_rtmp_stack.cpp
@@ -120,318 +120,6 @@ using namespace std;
/****************************************************************************
*****************************************************************************
****************************************************************************/
-/**
-* the chunk stream id used for some under-layer message,
-* for example, the PC(protocol control) message.
-*/
-#define RTMP_CID_ProtocolControl 0x02
-/**
-* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection.
-* generally use 0x03.
-*/
-#define RTMP_CID_OverConnection 0x03
-/**
-* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection,
-* the midst state(we guess).
-* rarely used, e.g. onStatus(NetStream.Play.Reset).
-*/
-#define RTMP_CID_OverConnection2 0x04
-/**
-* the stream message(amf0/amf3), over NetStream.
-* generally use 0x05.
-*/
-#define RTMP_CID_OverStream 0x05
-/**
-* the stream message(amf0/amf3), over NetStream, the midst state(we guess).
-* rarely used, e.g. play("mp4:mystram.f4v")
-*/
-#define RTMP_CID_OverStream2 0x08
-/**
-* the stream message(video), over NetStream
-* generally use 0x06.
-*/
-#define RTMP_CID_Video 0x06
-/**
-* the stream message(audio), over NetStream.
-* generally use 0x07.
-*/
-#define RTMP_CID_Audio 0x07
-
-/****************************************************************************
-*****************************************************************************
-****************************************************************************/
-
-SrsMessageHeader::SrsMessageHeader()
-{
- message_type = 0;
- payload_length = 0;
- timestamp_delta = 0;
- stream_id = 0;
-
- timestamp = 0;
- // we always use the connection chunk-id
- perfer_cid = RTMP_CID_OverConnection;
-}
-
-SrsMessageHeader::~SrsMessageHeader()
-{
-}
-
-bool SrsMessageHeader::is_audio()
-{
- return message_type == RTMP_MSG_AudioMessage;
-}
-
-bool SrsMessageHeader::is_video()
-{
- return message_type == RTMP_MSG_VideoMessage;
-}
-
-bool SrsMessageHeader::is_amf0_command()
-{
- return message_type == RTMP_MSG_AMF0CommandMessage;
-}
-
-bool SrsMessageHeader::is_amf0_data()
-{
- return message_type == RTMP_MSG_AMF0DataMessage;
-}
-
-bool SrsMessageHeader::is_amf3_command()
-{
- return message_type == RTMP_MSG_AMF3CommandMessage;
-}
-
-bool SrsMessageHeader::is_amf3_data()
-{
- return message_type == RTMP_MSG_AMF3DataMessage;
-}
-
-bool SrsMessageHeader::is_window_ackledgement_size()
-{
- return message_type == RTMP_MSG_WindowAcknowledgementSize;
-}
-
-bool SrsMessageHeader::is_ackledgement()
-{
- return message_type == RTMP_MSG_Acknowledgement;
-}
-
-bool SrsMessageHeader::is_set_chunk_size()
-{
- return message_type == RTMP_MSG_SetChunkSize;
-}
-
-bool SrsMessageHeader::is_user_control_message()
-{
- return message_type == RTMP_MSG_UserControlMessage;
-}
-
-bool SrsMessageHeader::is_set_peer_bandwidth()
-{
- return message_type == RTMP_MSG_SetPeerBandwidth;
-}
-
-bool SrsMessageHeader::is_aggregate()
-{
- return message_type == RTMP_MSG_AggregateMessage;
-}
-
-void SrsMessageHeader::initialize_amf0_script(int size, int stream)
-{
- message_type = RTMP_MSG_AMF0DataMessage;
- payload_length = (int32_t)size;
- timestamp_delta = (int32_t)0;
- timestamp = (int64_t)0;
- stream_id = (int32_t)stream;
-
- // amf0 script use connection2 chunk-id
- perfer_cid = RTMP_CID_OverConnection2;
-}
-
-void SrsMessageHeader::initialize_audio(int size, u_int32_t time, int stream)
-{
- message_type = RTMP_MSG_AudioMessage;
- payload_length = (int32_t)size;
- timestamp_delta = (int32_t)time;
- timestamp = (int64_t)time;
- stream_id = (int32_t)stream;
-
- // audio chunk-id
- perfer_cid = RTMP_CID_Audio;
-}
-
-void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream)
-{
- message_type = RTMP_MSG_VideoMessage;
- payload_length = (int32_t)size;
- timestamp_delta = (int32_t)time;
- timestamp = (int64_t)time;
- stream_id = (int32_t)stream;
-
- // video chunk-id
- perfer_cid = RTMP_CID_Video;
-}
-
-SrsCommonMessage::SrsCommonMessage()
-{
- payload = NULL;
- size = 0;
-}
-
-SrsCommonMessage::~SrsCommonMessage()
-{
- srs_freep(payload);
-}
-
-SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload()
-{
- payload = NULL;
- size = 0;
- shared_count = 0;
-}
-
-SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload()
-{
- srs_freep(payload);
-}
-
-SrsSharedPtrMessage::SrsSharedPtrMessage()
-{
- ptr = NULL;
-}
-
-SrsSharedPtrMessage::~SrsSharedPtrMessage()
-{
- if (ptr) {
- if (ptr->shared_count == 0) {
- srs_freep(ptr);
- } else {
- ptr->shared_count--;
- }
- }
-}
-
-int SrsSharedPtrMessage::create(SrsCommonMessage* msg)
-{
- int ret = ERROR_SUCCESS;
-
- if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) {
- return ret;
- }
-
- // to prevent double free of payload:
- // initialize already attach the payload of msg,
- // detach the payload to transfer the owner to shared ptr.
- msg->payload = NULL;
- msg->size = 0;
-
- return ret;
-}
-
-int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size)
-{
- int ret = ERROR_SUCCESS;
-
- if (ptr) {
- ret = ERROR_SYSTEM_ASSERT_FAILED;
- srs_error("should not set the payload twice. ret=%d", ret);
- srs_assert(false);
-
- return ret;
- }
-
- ptr = new SrsSharedPtrPayload();
-
- // direct attach the data.
- if (pheader) {
- ptr->header.message_type = pheader->message_type;
- ptr->header.payload_length = size;
- ptr->header.perfer_cid = pheader->perfer_cid;
- this->timestamp = pheader->timestamp;
- this->stream_id = pheader->stream_id;
- }
- ptr->payload = payload;
- ptr->size = size;
-
- // message can access it.
- this->payload = ptr->payload;
- this->size = ptr->size;
-
- return ret;
-}
-
-int SrsSharedPtrMessage::count()
-{
- srs_assert(ptr);
- return ptr->shared_count;
-}
-
-bool SrsSharedPtrMessage::check(int stream_id)
-{
- // we donot use the complex basic header,
- // ensure the basic header is 1bytes.
- if (ptr->header.perfer_cid < 2) {
- srs_info("change the chunk_id=%d to default=%d",
- ptr->header.perfer_cid, RTMP_CID_ProtocolControl);
- ptr->header.perfer_cid = RTMP_CID_ProtocolControl;
- }
-
- // we assume that the stream_id in a group must be the same.
- if (this->stream_id == stream_id) {
- return true;
- }
- this->stream_id = stream_id;
-
- return false;
-}
-
-bool SrsSharedPtrMessage::is_av()
-{
- return ptr->header.message_type == RTMP_MSG_AudioMessage
- || ptr->header.message_type == RTMP_MSG_VideoMessage;
-}
-
-bool SrsSharedPtrMessage::is_audio()
-{
- return ptr->header.message_type == RTMP_MSG_AudioMessage;
-}
-
-bool SrsSharedPtrMessage::is_video()
-{
- return ptr->header.message_type == RTMP_MSG_VideoMessage;
-}
-
-int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0)
-{
- if (c0) {
- return srs_chunk_header_c0(
- ptr->header.perfer_cid, timestamp, ptr->header.payload_length,
- ptr->header.message_type, stream_id,
- cache, nb_cache);
- } else {
- return srs_chunk_header_c3(
- ptr->header.perfer_cid, timestamp,
- cache, nb_cache);
- }
-}
-
-SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
-{
- srs_assert(ptr);
-
- SrsSharedPtrMessage* copy = new SrsSharedPtrMessage();
-
- copy->ptr = ptr;
- ptr->shared_count++;
-
- copy->timestamp = timestamp;
- copy->stream_id = stream_id;
- copy->payload = ptr->payload;
- copy->size = ptr->size;
-
- return copy;
-}
SrsPacket::SrsPacket()
{
@@ -904,41 +592,7 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs)
int SrsProtocol::do_iovs_send(iovec* iovs, int size)
{
- int ret = ERROR_SUCCESS;
-
- // the limits of writev iovs.
- // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
-#ifndef _WIN32
- static int limits = sysconf(_SC_IOV_MAX);
-#else
- static int limits = 1024;
-#endif
-
- // send in a time.
- if (size < limits) {
- if ((ret = skt->writev(iovs, size, NULL)) != ERROR_SUCCESS) {
- if (!srs_is_client_gracefully_close(ret)) {
- srs_error("send with writev failed. ret=%d", ret);
- }
- return ret;
- }
- return ret;
- }
-
- // send in multiple times.
- int cur_iov = 0;
- while (cur_iov < size) {
- int cur_count = srs_min(limits, size - cur_iov);
- if ((ret = skt->writev(iovs + cur_iov, cur_count, NULL)) != ERROR_SUCCESS) {
- if (!srs_is_client_gracefully_close(ret)) {
- srs_error("send with writev failed. ret=%d", ret);
- }
- return ret;
- }
- cur_iov += cur_count;
- }
-
- return ret;
+ return srs_write_large_iovs(skt, iovs, size);
}
int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id)
diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp
index c0eb1dfec..31b45ce95 100644
--- a/trunk/src/protocol/srs_rtmp_stack.hpp
+++ b/trunk/src/protocol/srs_rtmp_stack.hpp
@@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
#include
#include
+#include
class ISrsProtocolReaderWriter;
class SrsFastBuffer;
@@ -56,89 +57,6 @@ class SrsChunkStream;
class SrsSharedPtrMessage;
class IMergeReadHandler;
-/****************************************************************************
- *****************************************************************************
- ****************************************************************************/
-/**
- 5. Protocol Control Messages
- RTMP reserves message type IDs 1-7 for protocol control messages.
- These messages contain information needed by the RTM Chunk Stream
- protocol or RTMP itself. Protocol messages with IDs 1 & 2 are
- reserved for usage with RTM Chunk Stream protocol. Protocol messages
- with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID
- 7 is used between edge server and origin server.
- */
-#define RTMP_MSG_SetChunkSize 0x01
-#define RTMP_MSG_AbortMessage 0x02
-#define RTMP_MSG_Acknowledgement 0x03
-#define RTMP_MSG_UserControlMessage 0x04
-#define RTMP_MSG_WindowAcknowledgementSize 0x05
-#define RTMP_MSG_SetPeerBandwidth 0x06
-#define RTMP_MSG_EdgeAndOriginServerCommand 0x07
-/**
- 3. Types of messages
- The server and the client send messages over the network to
- communicate with each other. The messages can be of any type which
- includes audio messages, video messages, command messages, shared
- object messages, data messages, and user control messages.
- 3.1. Command message
- Command messages carry the AMF-encoded commands between the client
- and the server. These messages have been assigned message type value
- of 20 for AMF0 encoding and message type value of 17 for AMF3
- encoding. These messages are sent to perform some operations like
- connect, createStream, publish, play, pause on the peer. Command
- messages like onstatus, result etc. are used to inform the sender
- about the status of the requested commands. A command message
- consists of command name, transaction ID, and command object that
- contains related parameters. A client or a server can request Remote
- Procedure Calls (RPC) over streams that are communicated using the
- command messages to the peer.
- */
-#define RTMP_MSG_AMF3CommandMessage 17 // 0x11
-#define RTMP_MSG_AMF0CommandMessage 20 // 0x14
-/**
- 3.2. Data message
- The client or the server sends this message to send Metadata or any
- user data to the peer. Metadata includes details about the
- data(audio, video etc.) like creation time, duration, theme and so
- on. These messages have been assigned message type value of 18 for
- AMF0 and message type value of 15 for AMF3.
- */
-#define RTMP_MSG_AMF0DataMessage 18 // 0x12
-#define RTMP_MSG_AMF3DataMessage 15 // 0x0F
-/**
- 3.3. Shared object message
- A shared object is a Flash object (a collection of name value pairs)
- that are in synchronization across multiple clients, instances, and
- so on. The message types kMsgContainer=19 for AMF0 and
- kMsgContainerEx=16 for AMF3 are reserved for shared object events.
- Each message can contain multiple events.
- */
-#define RTMP_MSG_AMF3SharedObject 16 // 0x10
-#define RTMP_MSG_AMF0SharedObject 19 // 0x13
-/**
- 3.4. Audio message
- The client or the server sends this message to send audio data to the
- peer. The message type value of 8 is reserved for audio messages.
- */
-#define RTMP_MSG_AudioMessage 8 // 0x08
-/* *
- 3.5. Video message
- The client or the server sends this message to send video data to the
- peer. The message type value of 9 is reserved for video messages.
- These messages are large and can delay the sending of other type of
- messages. To avoid such a situation, the video message is assigned
- the lowest priority.
- */
-#define RTMP_MSG_VideoMessage 9 // 0x09
-/**
- 3.6. Aggregate message
- An aggregate message is a single message that contains a list of submessages.
- The message type value of 22 is reserved for aggregate
- messages.
- */
-#define RTMP_MSG_AggregateMessage 22 // 0x16
-
/****************************************************************************
*****************************************************************************
****************************************************************************/
@@ -163,263 +81,6 @@ class IMergeReadHandler;
/****************************************************************************
*****************************************************************************
****************************************************************************/
-/**
-* 6.1. Chunk Format
-* Extended timestamp: 0 or 4 bytes
-* This field MUST be sent when the normal timsestamp is set to
-* 0xffffff, it MUST NOT be sent if the normal timestamp is set to
-* anything else. So for values less than 0xffffff the normal
-* timestamp field SHOULD be used in which case the extended timestamp
-* MUST NOT be present. For values greater than or equal to 0xffffff
-* the normal timestamp field MUST NOT be used and MUST be set to
-* 0xffffff and the extended timestamp MUST be sent.
-*/
-#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF
-
-/**
-* 4.1. Message Header
-*/
-class SrsMessageHeader
-{
-public:
- /**
- * 3bytes.
- * Three-byte field that contains a timestamp delta of the message.
- * @remark, only used for decoding message from chunk stream.
- */
- int32_t timestamp_delta;
- /**
- * 3bytes.
- * Three-byte field that represents the size of the payload in bytes.
- * It is set in big-endian format.
- */
- int32_t payload_length;
- /**
- * 1byte.
- * One byte field to represent the message type. A range of type IDs
- * (1-7) are reserved for protocol control messages.
- */
- int8_t message_type;
- /**
- * 4bytes.
- * Four-byte field that identifies the stream of the message. These
- * bytes are set in little-endian format.
- */
- int32_t stream_id;
-
- /**
- * Four-byte field that contains a timestamp of the message.
- * The 4 bytes are packed in the big-endian order.
- * @remark, used as calc timestamp when decode and encode time.
- * @remark, we use 64bits for large time for jitter detect and hls.
- */
- int64_t timestamp;
-public:
- /**
- * get the perfered cid(chunk stream id) which sendout over.
- * set at decoding, and canbe used for directly send message,
- * for example, dispatch to all connections.
- */
- int perfer_cid;
-public:
- SrsMessageHeader();
- virtual ~SrsMessageHeader();
-public:
- bool is_audio();
- bool is_video();
- bool is_amf0_command();
- bool is_amf0_data();
- bool is_amf3_command();
- bool is_amf3_data();
- bool is_window_ackledgement_size();
- bool is_ackledgement();
- bool is_set_chunk_size();
- bool is_user_control_message();
- bool is_set_peer_bandwidth();
- bool is_aggregate();
-public:
- /**
- * create a amf0 script header, set the size and stream_id.
- */
- void initialize_amf0_script(int size, int stream);
- /**
- * create a audio header, set the size, timestamp and stream_id.
- */
- void initialize_audio(int size, u_int32_t time, int stream);
- /**
- * create a video header, set the size, timestamp and stream_id.
- */
- void initialize_video(int size, u_int32_t time, int stream);
-};
-
-/**
-* message is raw data RTMP message, bytes oriented,
-* protcol always recv RTMP message, and can send RTMP message or RTMP packet.
-* the common message is read from underlay protocol sdk.
-* while the shared ptr message used to copy and send.
-*/
-class SrsCommonMessage
-{
-// 4.1. Message Header
-public:
- SrsMessageHeader header;
-// 4.2. Message Payload
-public:
- /**
- * current message parsed size,
- * size <= header.payload_length
- * for the payload maybe sent in multiple chunks.
- */
- int size;
- /**
- * the payload of message, the SrsCommonMessage never know about the detail of payload,
- * user must use SrsProtocol.decode_message to get concrete packet.
- * @remark, not all message payload can be decoded to packet. for example,
- * video/audio packet use raw bytes, no video/audio packet.
- */
- char* payload;
-public:
- SrsCommonMessage();
-public:
- virtual ~SrsCommonMessage();
-};
-
-/**
-* the message header for shared ptr message.
-* only the message for all msgs are same.
-*/
-struct SrsSharedMessageHeader
-{
- /**
- * 3bytes.
- * Three-byte field that represents the size of the payload in bytes.
- * It is set in big-endian format.
- */
- int32_t payload_length;
- /**
- * 1byte.
- * One byte field to represent the message type. A range of type IDs
- * (1-7) are reserved for protocol control messages.
- */
- int8_t message_type;
- /**
- * get the perfered cid(chunk stream id) which sendout over.
- * set at decoding, and canbe used for directly send message,
- * for example, dispatch to all connections.
- */
- int perfer_cid;
-};
-
-/**
-* shared ptr message.
-* for audio/video/data message that need less memory copy.
-* and only for output.
-*
-* create first object by constructor and create(),
-* use copy if need reference count message.
-*
-*/
-class SrsSharedPtrMessage
-{
-// 4.1. Message Header
-public:
- // the header can shared, only set the timestamp and stream id.
- // @see https://github.com/simple-rtmp-server/srs/issues/251
- //SrsSharedMessageHeader header;
- /**
- * Four-byte field that contains a timestamp of the message.
- * The 4 bytes are packed in the big-endian order.
- * @remark, used as calc timestamp when decode and encode time.
- * @remark, we use 64bits for large time for jitter detect and hls.
- */
- int64_t timestamp;
- /**
- * 4bytes.
- * Four-byte field that identifies the stream of the message. These
- * bytes are set in big-endian format.
- */
- int32_t stream_id;
-// 4.2. Message Payload
-public:
- /**
- * current message parsed size,
- * size <= header.payload_length
- * for the payload maybe sent in multiple chunks.
- */
- int size;
- /**
- * the payload of message, the SrsCommonMessage never know about the detail of payload,
- * user must use SrsProtocol.decode_message to get concrete packet.
- * @remark, not all message payload can be decoded to packet. for example,
- * video/audio packet use raw bytes, no video/audio packet.
- */
- char* payload;
-private:
- class SrsSharedPtrPayload
- {
- public:
- // shared message header.
- // @see https://github.com/simple-rtmp-server/srs/issues/251
- SrsSharedMessageHeader header;
- // actual shared payload.
- char* payload;
- // size of payload.
- int size;
- // the reference count
- int shared_count;
- public:
- SrsSharedPtrPayload();
- virtual ~SrsSharedPtrPayload();
- };
- SrsSharedPtrPayload* ptr;
-public:
- SrsSharedPtrMessage();
- virtual ~SrsSharedPtrMessage();
-public:
- /**
- * create shared ptr message,
- * copy header, manage the payload of msg,
- * set the payload to NULL to prevent double free.
- * @remark payload of msg set to NULL if success.
- */
- virtual int create(SrsCommonMessage* msg);
- /**
- * create shared ptr message,
- * from the header and payload.
- * @remark user should never free the payload.
- * @param pheader, the header to copy to the message. NULL to ignore.
- */
- virtual int create(SrsMessageHeader* pheader, char* payload, int size);
- /**
- * get current reference count.
- * when this object created, count set to 0.
- * if copy() this object, count increase 1.
- * if this or copy deleted, free payload when count is 0, or count--.
- * @remark, assert object is created.
- */
- virtual int count();
- /**
- * check perfer cid and stream id.
- * @return whether stream id already set.
- */
- virtual bool check(int stream_id);
-public:
- virtual bool is_av();
- virtual bool is_audio();
- virtual bool is_video();
-public:
- /**
- * generate the chunk header to cache.
- * @return the size of header.
- */
- virtual int chunk_header(char* cache, int nb_cache, bool c0);
-public:
- /**
- * copy current shared ptr message, use ref-count.
- * @remark, assert object is created.
- */
- virtual SrsSharedPtrMessage* copy();
-};
/**
* the decoded message payload.
diff --git a/trunk/src/protocol/srs_rtmp_utility.cpp b/trunk/src/protocol/srs_rtmp_utility.cpp
index 7c892ad36..4d346ed0c 100644
--- a/trunk/src/protocol/srs_rtmp_utility.cpp
+++ b/trunk/src/protocol/srs_rtmp_utility.cpp
@@ -23,6 +23,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include
+// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+#include
+#endif
+
#include
using namespace std;
@@ -32,6 +37,8 @@ using namespace std;
#include
#include
#include
+#include
+#include
void srs_discovery_tc_url(
string tcUrl,
@@ -159,136 +166,6 @@ bool srs_bytes_equals(void* pa, void* pb, int size)
return true;
}
-int srs_chunk_header_c0(
- int perfer_cid, u_int32_t timestamp, int32_t payload_length,
- int8_t message_type, int32_t stream_id,
- char* cache, int nb_cache
-)
-{
- // to directly set the field.
- char* pp = NULL;
-
- // generate the header.
- char* p = cache;
-
- // no header.
- if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) {
- return 0;
- }
-
- // write new chunk stream header, fmt is 0
- *p++ = 0x00 | (perfer_cid & 0x3F);
-
- // chunk message header, 11 bytes
- // timestamp, 3bytes, big-endian
- if (timestamp < RTMP_EXTENDED_TIMESTAMP) {
- pp = (char*)×tamp;
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
- } else {
- *p++ = 0xFF;
- *p++ = 0xFF;
- *p++ = 0xFF;
- }
-
- // message_length, 3bytes, big-endian
- pp = (char*)&payload_length;
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
-
- // message_type, 1bytes
- *p++ = message_type;
-
- // stream_id, 4bytes, little-endian
- pp = (char*)&stream_id;
- *p++ = pp[0];
- *p++ = pp[1];
- *p++ = pp[2];
- *p++ = pp[3];
-
- // for c0
- // chunk extended timestamp header, 0 or 4 bytes, big-endian
- //
- // for c3:
- // chunk extended timestamp header, 0 or 4 bytes, big-endian
- // 6.1.3. Extended Timestamp
- // This field is transmitted only when the normal time stamp in the
- // chunk message header is set to 0x00ffffff. If normal time stamp is
- // set to any value less than 0x00ffffff, this field MUST NOT be
- // present. This field MUST NOT be present if the timestamp field is not
- // present. Type 3 chunks MUST NOT have this field.
- // adobe changed for Type3 chunk:
- // FMLE always sendout the extended-timestamp,
- // must send the extended-timestamp to FMS,
- // must send the extended-timestamp to flash-player.
- // @see: ngx_rtmp_prepare_message
- // @see: http://blog.csdn.net/win_lin/article/details/13363699
- // TODO: FIXME: extract to outer.
- if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
- pp = (char*)×tamp;
- *p++ = pp[3];
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
- }
-
- // always has header
- return p - cache;
-}
-
-int srs_chunk_header_c3(
- int perfer_cid, u_int32_t timestamp,
- char* cache, int nb_cache
-)
-{
- // to directly set the field.
- char* pp = NULL;
-
- // generate the header.
- char* p = cache;
-
- // no header.
- if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) {
- return 0;
- }
-
- // write no message header chunk stream, fmt is 3
- // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header,
- // SRS will rollback to 1B chunk header.
- *p++ = 0xC0 | (perfer_cid & 0x3F);
-
- // for c0
- // chunk extended timestamp header, 0 or 4 bytes, big-endian
- //
- // for c3:
- // chunk extended timestamp header, 0 or 4 bytes, big-endian
- // 6.1.3. Extended Timestamp
- // This field is transmitted only when the normal time stamp in the
- // chunk message header is set to 0x00ffffff. If normal time stamp is
- // set to any value less than 0x00ffffff, this field MUST NOT be
- // present. This field MUST NOT be present if the timestamp field is not
- // present. Type 3 chunks MUST NOT have this field.
- // adobe changed for Type3 chunk:
- // FMLE always sendout the extended-timestamp,
- // must send the extended-timestamp to FMS,
- // must send the extended-timestamp to flash-player.
- // @see: ngx_rtmp_prepare_message
- // @see: http://blog.csdn.net/win_lin/article/details/13363699
- // TODO: FIXME: extract to outer.
- if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
- pp = (char*)×tamp;
- *p++ = pp[3];
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
- }
-
- // always has header
- return p - cache;
-}
-
int srs_do_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg)
{
int ret = ERROR_SUCCESS;
@@ -362,3 +239,43 @@ std::string srs_generate_stream_url(std::string vhost, std::string app, std::str
return url;
}
+int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite)
+{
+ int ret = ERROR_SUCCESS;
+
+ // the limits of writev iovs.
+ // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+ // for linux, generally it's 1024.
+ static int limits = sysconf(_SC_IOV_MAX);
+#else
+ static int limits = 1024;
+#endif
+
+ // send in a time.
+ if (size < limits) {
+ if ((ret = skt->writev(iovs, size, pnwrite)) != ERROR_SUCCESS) {
+ if (!srs_is_client_gracefully_close(ret)) {
+ srs_error("send with writev failed. ret=%d", ret);
+ }
+ return ret;
+ }
+ return ret;
+ }
+
+ // send in multiple times.
+ int cur_iov = 0;
+ while (cur_iov < size) {
+ int cur_count = srs_min(limits, size - cur_iov);
+ if ((ret = skt->writev(iovs + cur_iov, cur_count, pnwrite)) != ERROR_SUCCESS) {
+ if (!srs_is_client_gracefully_close(ret)) {
+ srs_error("send with writev failed. ret=%d", ret);
+ }
+ return ret;
+ }
+ cur_iov += cur_count;
+ }
+
+ return ret;
+}
+
diff --git a/trunk/src/protocol/srs_rtmp_utility.hpp b/trunk/src/protocol/srs_rtmp_utility.hpp
index 09e834d09..9b4c3b35a 100644
--- a/trunk/src/protocol/srs_rtmp_utility.hpp
+++ b/trunk/src/protocol/srs_rtmp_utility.hpp
@@ -29,12 +29,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include
+// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
+#ifndef _WIN32
+#include
+#endif
+
#include
#include
class SrsMessageHeader;
class SrsSharedPtrMessage;
+class ISrsProtocolReaderWriter;
/**
* parse the tcUrl, output the schema, host, vhost, app and port.
@@ -90,29 +96,6 @@ extern std::string srs_generate_tc_url(
*/
extern bool srs_bytes_equals(void* pa, void* pb, int size);
-/**
-* generate the c0 chunk header for msg.
-* @param cache, the cache to write header.
-* @param nb_cache, the size of cache.
-* @return the size of header. 0 if cache not enough.
-*/
-extern int srs_chunk_header_c0(
- int perfer_cid, u_int32_t timestamp, int32_t payload_length,
- int8_t message_type, int32_t stream_id,
- char* cache, int nb_cache
-);
-
-/**
-* generate the c3 chunk header for msg.
-* @param cache, the cache to write header.
-* @param nb_cache, the size of cache.
-* @return the size of header. 0 if cache not enough.
-*/
-extern int srs_chunk_header_c3(
- int perfer_cid, u_int32_t timestamp,
- char* cache, int nb_cache
-);
-
/**
* create shared ptr message from bytes.
* @param data the packet bytes. user should never free it.
@@ -123,5 +106,8 @@ extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int s
// get the stream identify, vhost/app/stream.
extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream);
+// write large numbers of iovs.
+extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL);
+
#endif