mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 19:31:53 +00:00
merge from 2.0
This commit is contained in:
commit
cd162c2848
15 changed files with 1351 additions and 929 deletions
31
README.md
31
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.<br/>
|
||||
* 2014-04-07, [Release v1.0-mainline](https://github.com/simple-rtmp-server/srs/releases/tag/1.0.mainline), support [arm](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SrsLinuxArm), [init.d](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_LinuxService), http [server](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPServer)/[api](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPApi), [ingest](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleIngest). 30000 lines.<br/>
|
||||
* 2013-12-25, [Release v0.9](https://github.com/simple-rtmp-server/srs/releases/tag/0.9), support bandwidth test, player/encoder/chat [demos](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_SampleDemo). 20926 lines.<br/>
|
||||
* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [st_load](https://github.com/winlinvip/st-load). 19186 lines.<br/>
|
||||
* 2013-12-08, [Release v0.8](https://github.com/simple-rtmp-server/srs/releases/tag/0.8), support [http hooks callback](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_HTTPCallback), update [SB](https://github.com/simple-rtmp-server/srs-bench). 19186 lines.<br/>
|
||||
* 2013-12-03, [Release v0.7](https://github.com/simple-rtmp-server/srs/releases/tag/0.7), support [live stream transcoding](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_FFMPEG). 17605 lines.<br/>
|
||||
* 2013-11-29, [Release v0.6](https://github.com/simple-rtmp-server/srs/releases/tag/0.6), support [forward](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_Forward) stream to origin/edge. 16094 lines.<br/>
|
||||
* 2013-11-26, [Release v0.5](https://github.com/simple-rtmp-server/srs/releases/tag/0.5), support [HLS(m3u8)](https://github.com/simple-rtmp-server/srs/wiki/v1_CN_DeliveryHLS), fragment and window. 14449 lines.<br/>
|
||||
|
@ -525,7 +525,7 @@ Remark:
|
|||
* 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
|
||||
|
|
|
@ -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<SrsFastFlvStreamEncoder*>(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++) {
|
||||
|
|
|
@ -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<SrsHttpHeaderField>& headers
|
||||
);
|
||||
SrsFastBuffer* body, std::vector<SrsHttpHeaderField>& 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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <sys/uio.h>
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
|
@ -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;
|
||||
|
|
|
@ -31,6 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
#include <string>
|
||||
|
||||
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,19 +37,304 @@ using namespace std;
|
|||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_kernel_file.hpp>
|
||||
#include <srs_kernel_codec.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -31,10 +31,399 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
#include <string>
|
||||
|
||||
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#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);
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ using namespace std;
|
|||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_kernel_flv.hpp>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_consts.hpp>
|
||||
#include <srs_core_performance.hpp>
|
||||
#include <srs_kernel_flv.hpp>
|
||||
|
||||
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.
|
||||
|
|
|
@ -23,6 +23,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
#include <srs_rtmp_utility.hpp>
|
||||
|
||||
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
using namespace std;
|
||||
|
||||
|
@ -32,6 +37,8 @@ using namespace std;
|
|||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_kernel_codec.hpp>
|
||||
#include <srs_kernel_consts.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_rtmp_io.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
*/
|
||||
#include <srs_core.hpp>
|
||||
|
||||
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <srs_kernel_consts.hpp>
|
||||
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in a new issue