From 2a05783d5cb854f690fa09eb9b84f6c5fff23fd6 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 22 Jan 2015 18:32:10 +0800 Subject: [PATCH] for #293, support http ts stream. 2.0.101 --- trunk/src/app/srs_app_hls.cpp | 353 +--------------------------- trunk/src/app/srs_app_hls.hpp | 20 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_avc.cpp | 348 +++++++++++++++++++++++++++ trunk/src/kernel/srs_kernel_avc.hpp | 25 ++ trunk/src/kernel/srs_kernel_ts.cpp | 42 +++- trunk/src/kernel/srs_kernel_ts.hpp | 4 + 7 files changed, 419 insertions(+), 375 deletions(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index d2954ed608..93bde04e2f 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -51,369 +51,23 @@ using namespace std; #include #include -// max PES packets size to flush the video. -#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024 - // drop the segment when duration of ts too small. #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 -// @see: NGX_RTMP_HLS_DELAY, -// 63000: 700ms, ts_tbn=90000 -#define SRS_AUTO_HLS_DELAY 63000 - -// @see: ngx_rtmp_mpegts_header -u_int8_t mpegts_header[] = { - /* TS */ - 0x47, 0x40, 0x00, 0x10, 0x00, - /* PSI */ - 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, - /* PAT */ - 0x00, 0x01, 0xf0, 0x01, - /* CRC */ - 0x2e, 0x70, 0x19, 0x05, - /* stuffing 167 bytes */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - - /* TS */ - 0x47, 0x50, 0x01, 0x10, 0x00, - /* PSI */ - 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, - /* PMT */ - 0xe1, 0x00, - 0xf0, 0x00, - // must generate header with/without video, @see: - // https://github.com/winlinvip/simple-rtmp-server/issues/40 - 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ - 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ - /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ - /* CRC */ - 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ - /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ - /* stuffing 157 bytes */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff -}; - -// @see: ngx_rtmp_mpegts.c -// TODO: support full mpegts feature in future. -class SrsMpegtsWriter -{ -public: - static int write_header(SrsFileWriter* writer) - { - int ret = ERROR_SUCCESS; - - if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) { - ret = ERROR_HLS_WRITE_FAILED; - srs_error("write ts file header failed. ret=%d", ret); - return ret; - } - - return ret; - } - static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) - { - int ret = ERROR_SUCCESS; - - if (!buffer->bytes() || buffer->length() <= 0) { - return ret; - } - - char* last = buffer->bytes() + buffer->length(); - char* pos = buffer->bytes(); - - bool first = true; - while (pos < last) { - static char packet[188]; - char* p = packet; - - frame->cc++; - - // sync_byte; //8bits - *p++ = 0x47; - // pid; //13bits - *p++ = (frame->pid >> 8) & 0x1f; - // payload_unit_start_indicator; //1bit - if (first) { - p[-1] |= 0x40; - } - *p++ = frame->pid; - - // transport_scrambling_control; //2bits - // adaption_field_control; //2bits, 0x01: PayloadOnly - // continuity_counter; //4bits - *p++ = 0x10 | (frame->cc & 0x0f); - - if (first) { - first = false; - if (frame->key) { - p[-1] |= 0x20; // Both Adaption and Payload - *p++ = 7; // size - *p++ = 0x50; // random access + PCR - p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY); - } - - // PES header - // packet_start_code_prefix; //24bits, '00 00 01' - *p++ = 0x00; - *p++ = 0x00; - *p++ = 0x01; - //8bits - *p++ = frame->sid; - - // pts(33bits) need 5bytes. - u_int8_t header_size = 5; - u_int8_t flags = 0x80; // pts - - // dts(33bits) need 5bytes also - if (frame->dts != frame->pts) { - header_size += 5; - flags |= 0x40; // dts - } - - // 3bytes: flag fields from PES_packet_length to PES_header_data_length - int pes_size = (last - pos) + header_size + 3; - if (pes_size > 0xffff) { - /** - * when actual packet length > 0xffff(65535), - * which exceed the max u_int16_t packet length, - * use 0 packet length, the next unit start indicates the end of packet. - */ - pes_size = 0; - } - - // PES_packet_length; //16bits - *p++ = (pes_size >> 8); - *p++ = pes_size; - - // PES_scrambling_control; //2bits, '10' - // PES_priority; //1bit - // data_alignment_indicator; //1bit - // copyright; //1bit - // original_or_copy; //1bit - *p++ = 0x80; /* H222 */ - - // PTS_DTS_flags; //2bits - // ESCR_flag; //1bit - // ES_rate_flag; //1bit - // DSM_trick_mode_flag; //1bit - // additional_copy_info_flag; //1bit - // PES_CRC_flag; //1bit - // PES_extension_flag; //1bit - *p++ = flags; - - // PES_header_data_length; //8bits - *p++ = header_size; - - // pts; // 33bits - p = write_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY); - - // dts; // 33bits - if (frame->dts != frame->pts) { - p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY); - } - } - - int body_size = sizeof(packet) - (p - packet); - int in_size = last - pos; - - if (body_size <= in_size) { - memcpy(p, pos, body_size); - pos += body_size; - } else { - p = fill_stuff(p, packet, body_size, in_size); - memcpy(p, pos, in_size); - pos = last; - } - - // write ts packet - if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) { - ret = ERROR_HLS_WRITE_FAILED; - srs_error("write ts file failed. ret=%d", ret); - return ret; - } - } - - return ret; - } -private: - static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) - { - char* p = pes_body_end; - - // insert the stuff bytes before PES body - int stuff_size = (body_size - in_size); - - // adaption_field_control; //2bits - if (packet[3] & 0x20) { - // has adaptation - // packet[4]: adaption_field_length - // packet[5]: adaption field data - // base: start of PES body - char* base = &packet[5] + packet[4]; - int len = p - base; - p = (char*)memmove(base + stuff_size, base, len) + len; - // increase the adaption field size. - packet[4] += stuff_size; - - return p; - } - - // create adaption field. - // adaption_field_control; //2bits - packet[3] |= 0x20; - // base: start of PES body - char* base = &packet[4]; - int len = p - base; - p = (char*)memmove(base + stuff_size, base, len) + len; - // adaption_field_length; //8bits - packet[4] = (stuff_size - 1); - if (stuff_size >= 2) { - // adaption field flags. - packet[5] = 0; - // adaption data. - if (stuff_size > 2) { - memset(&packet[6], 0xff, stuff_size - 2); - } - } - - return p; - } - static char* write_pcr(char* p, int64_t pcr) - { - // the pcr=dts-delay - // and the pcr maybe negative - // @see https://github.com/winlinvip/simple-rtmp-server/issues/268 - int64_t v = srs_max(0, pcr); - - *p++ = (char) (v >> 25); - *p++ = (char) (v >> 17); - *p++ = (char) (v >> 9); - *p++ = (char) (v >> 1); - *p++ = (char) (v << 7 | 0x7e); - *p++ = 0; - - return p; - } - static char* write_pts(char* p, u_int8_t fb, int64_t pts) - { - int32_t val; - - val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; - *p++ = val; - - val = (((pts >> 15) & 0x7fff) << 1) | 1; - *p++ = (val >> 8); - *p++ = val; - - val = (((pts) & 0x7fff) << 1) | 1; - *p++ = (val >> 8); - *p++ = val; - - return p; - } -}; - -SrsTSMuxer::SrsTSMuxer() -{ - writer = new SrsFileWriter(); -} - -SrsTSMuxer::~SrsTSMuxer() -{ - close(); - srs_freep(writer); -} - -int SrsTSMuxer::open(string _path) -{ - int ret = ERROR_SUCCESS; - - path = _path; - - close(); - - if ((ret = writer->open(path)) != ERROR_SUCCESS) { - return ret; - } - - // write mpegts header - if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) -{ - int ret = ERROR_SUCCESS; - - if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) -{ - int ret = ERROR_SUCCESS; - - if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -void SrsTSMuxer::close() -{ - writer->close(); -} - SrsHlsSegment::SrsHlsSegment() { duration = 0; sequence_no = 0; - muxer = new SrsTSMuxer(); segment_start_dts = 0; is_sequence_header = false; + writer = new SrsFileWriter(); + muxer = new SrsTSMuxer(writer); } SrsHlsSegment::~SrsHlsSegment() { srs_freep(muxer); + srs_freep(writer); } void SrsHlsSegment::update_duration(int64_t current_frame_dts) @@ -926,6 +580,7 @@ int SrsHlsCache::write_audio(SrsAvcAacCodec* codec, SrsHlsMuxer* muxer, int64_t return ret; } } + // TODO: config it. // in ms, audio delay to flush the audios. int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 0537e463f3..f7a03b07f6 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -52,25 +52,6 @@ class SrsSimpleBuffer; class SrsTsAacJitter; class SrsTsCache; -/** -* write data from frame(header info) and buffer(data) to ts file. -* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter -*/ -class SrsTSMuxer -{ -private: - SrsFileWriter* writer; - std::string path; -public: - SrsTSMuxer(); - virtual ~SrsTSMuxer(); -public: - virtual int open(std::string _path); - virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); - virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); - virtual void close(); -}; - /** * the wrapper of m3u8 segment from specification: * @@ -89,6 +70,7 @@ class SrsHlsSegment // ts full file to write. std::string full_path; // the muxer to write ts. + SrsFileWriter* writer; SrsTSMuxer* muxer; // current segment start dts for m3u8 int64_t segment_start_dts; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 6dc62aff16..a365d41690 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 101 +#define VERSION_REVISION 102 // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_avc.cpp b/trunk/src/kernel/srs_kernel_avc.cpp index e8420a28c8..f450aabe2d 100644 --- a/trunk/src/kernel/srs_kernel_avc.cpp +++ b/trunk/src/kernel/srs_kernel_avc.cpp @@ -28,6 +28,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include + +using namespace std; // in ms, for HLS aac sync time. #define SRS_CONF_DEFAULT_AAC_SYNC 100 @@ -65,6 +68,294 @@ int aac_sample_rates[] = 7350, 0, 0, 0 }; +// @see: NGX_RTMP_HLS_DELAY, +// 63000: 700ms, ts_tbn=90000 +#define SRS_AUTO_HLS_DELAY 63000 + +// @see: ngx_rtmp_mpegts_header +u_int8_t mpegts_header[] = { + /* TS */ + 0x47, 0x40, 0x00, 0x10, 0x00, + /* PSI */ + 0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, + /* PAT */ + 0x00, 0x01, 0xf0, 0x01, + /* CRC */ + 0x2e, 0x70, 0x19, 0x05, + /* stuffing 167 bytes */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + + /* TS */ + 0x47, 0x50, 0x01, 0x10, 0x00, + /* PSI */ + 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, + /* PMT */ + 0xe1, 0x00, + 0xf0, 0x00, + // must generate header with/without video, @see: + // https://github.com/winlinvip/simple-rtmp-server/issues/40 + 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264, pid=0x100=256 */ + 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac, pid=0x101=257 */ + /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ + /* CRC */ + 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ + /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ + /* stuffing 157 bytes */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +// @see: ngx_rtmp_mpegts.c +// TODO: support full mpegts feature in future. +class SrsMpegtsWriter +{ +public: + static int write_header(SrsFileWriter* writer) + { + int ret = ERROR_SUCCESS; + + if ((ret = writer->write(mpegts_header, sizeof(mpegts_header), NULL)) != ERROR_SUCCESS) { + ret = ERROR_HLS_WRITE_FAILED; + srs_error("write ts file header failed. ret=%d", ret); + return ret; + } + + return ret; + } + static int write_frame(SrsFileWriter* writer, SrsMpegtsFrame* frame, SrsSimpleBuffer* buffer) + { + int ret = ERROR_SUCCESS; + + if (!buffer->bytes() || buffer->length() <= 0) { + return ret; + } + + char* last = buffer->bytes() + buffer->length(); + char* pos = buffer->bytes(); + + bool first = true; + while (pos < last) { + static char packet[188]; + char* p = packet; + + frame->cc++; + + // sync_byte; //8bits + *p++ = 0x47; + // pid; //13bits + *p++ = (frame->pid >> 8) & 0x1f; + // payload_unit_start_indicator; //1bit + if (first) { + p[-1] |= 0x40; + } + *p++ = frame->pid; + + // transport_scrambling_control; //2bits + // adaption_field_control; //2bits, 0x01: PayloadOnly + // continuity_counter; //4bits + *p++ = 0x10 | (frame->cc & 0x0f); + + if (first) { + first = false; + if (frame->key) { + p[-1] |= 0x20; // Both Adaption and Payload + *p++ = 7; // size + *p++ = 0x50; // random access + PCR + p = write_pcr(p, frame->dts - SRS_AUTO_HLS_DELAY); + } + + // PES header + // packet_start_code_prefix; //24bits, '00 00 01' + *p++ = 0x00; + *p++ = 0x00; + *p++ = 0x01; + //8bits + *p++ = frame->sid; + + // pts(33bits) need 5bytes. + u_int8_t header_size = 5; + u_int8_t flags = 0x80; // pts + + // dts(33bits) need 5bytes also + if (frame->dts != frame->pts) { + header_size += 5; + flags |= 0x40; // dts + } + + // 3bytes: flag fields from PES_packet_length to PES_header_data_length + int pes_size = (last - pos) + header_size + 3; + if (pes_size > 0xffff) { + /** + * when actual packet length > 0xffff(65535), + * which exceed the max u_int16_t packet length, + * use 0 packet length, the next unit start indicates the end of packet. + */ + pes_size = 0; + } + + // PES_packet_length; //16bits + *p++ = (pes_size >> 8); + *p++ = pes_size; + + // PES_scrambling_control; //2bits, '10' + // PES_priority; //1bit + // data_alignment_indicator; //1bit + // copyright; //1bit + // original_or_copy; //1bit + *p++ = 0x80; /* H222 */ + + // PTS_DTS_flags; //2bits + // ESCR_flag; //1bit + // ES_rate_flag; //1bit + // DSM_trick_mode_flag; //1bit + // additional_copy_info_flag; //1bit + // PES_CRC_flag; //1bit + // PES_extension_flag; //1bit + *p++ = flags; + + // PES_header_data_length; //8bits + *p++ = header_size; + + // pts; // 33bits + p = write_pts(p, flags >> 6, frame->pts + SRS_AUTO_HLS_DELAY); + + // dts; // 33bits + if (frame->dts != frame->pts) { + p = write_pts(p, 1, frame->dts + SRS_AUTO_HLS_DELAY); + } + } + + int body_size = sizeof(packet) - (p - packet); + int in_size = last - pos; + + if (body_size <= in_size) { + memcpy(p, pos, body_size); + pos += body_size; + } else { + p = fill_stuff(p, packet, body_size, in_size); + memcpy(p, pos, in_size); + pos = last; + } + + // write ts packet + if ((ret = writer->write(packet, sizeof(packet), NULL)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("write ts file failed. ret=%d", ret); + } + return ret; + } + } + + return ret; + } +private: + static char* fill_stuff(char* pes_body_end, char* packet, int body_size, int in_size) + { + char* p = pes_body_end; + + // insert the stuff bytes before PES body + int stuff_size = (body_size - in_size); + + // adaption_field_control; //2bits + if (packet[3] & 0x20) { + // has adaptation + // packet[4]: adaption_field_length + // packet[5]: adaption field data + // base: start of PES body + char* base = &packet[5] + packet[4]; + int len = p - base; + p = (char*)memmove(base + stuff_size, base, len) + len; + // increase the adaption field size. + packet[4] += stuff_size; + + return p; + } + + // create adaption field. + // adaption_field_control; //2bits + packet[3] |= 0x20; + // base: start of PES body + char* base = &packet[4]; + int len = p - base; + p = (char*)memmove(base + stuff_size, base, len) + len; + // adaption_field_length; //8bits + packet[4] = (stuff_size - 1); + if (stuff_size >= 2) { + // adaption field flags. + packet[5] = 0; + // adaption data. + if (stuff_size > 2) { + memset(&packet[6], 0xff, stuff_size - 2); + } + } + + return p; + } + static char* write_pcr(char* p, int64_t pcr) + { + // the pcr=dts-delay + // and the pcr maybe negative + // @see https://github.com/winlinvip/simple-rtmp-server/issues/268 + int64_t v = srs_max(0, pcr); + + *p++ = (char) (v >> 25); + *p++ = (char) (v >> 17); + *p++ = (char) (v >> 9); + *p++ = (char) (v >> 1); + *p++ = (char) (v << 7 | 0x7e); + *p++ = 0; + + return p; + } + static char* write_pts(char* p, u_int8_t fb, int64_t pts) + { + int32_t val; + + val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; + *p++ = val; + + val = (((pts >> 15) & 0x7fff) << 1) | 1; + *p++ = (val >> 8); + *p++ = val; + + val = (((pts) & 0x7fff) << 1) | 1; + *p++ = (val >> 8); + *p++ = val; + + return p; + } +}; + SrsMpegtsFrame::SrsMpegtsFrame() { pts = dts = 0; @@ -72,6 +363,63 @@ SrsMpegtsFrame::SrsMpegtsFrame() key = false; } +SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w) +{ + writer = w; +} + +SrsTSMuxer::~SrsTSMuxer() +{ + close(); +} + +int SrsTSMuxer::open(string _path) +{ + int ret = ERROR_SUCCESS; + + path = _path; + + close(); + + if ((ret = writer->open(path)) != ERROR_SUCCESS) { + return ret; + } + + // write mpegts header + if ((ret = SrsMpegtsWriter::write_header(writer)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsTSMuxer::write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMpegtsWriter::write_frame(writer, af, ab)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsTSMuxer::write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsMpegtsWriter::write_frame(writer, vf, vb)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +void SrsTSMuxer::close() +{ + writer->close(); +} + SrsTsAacJitter::SrsTsAacJitter() { base_pts = 0; diff --git a/trunk/src/kernel/srs_kernel_avc.hpp b/trunk/src/kernel/srs_kernel_avc.hpp index 302f7a97b8..3116eeb1fc 100644 --- a/trunk/src/kernel/srs_kernel_avc.hpp +++ b/trunk/src/kernel/srs_kernel_avc.hpp @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include + #include class SrsStream; @@ -37,6 +39,7 @@ class SrsMpegtsFrame; class SrsSimpleBuffer; class SrsAvcAacCodec; class SrsCodecSample; +class SrsFileWriter; /** * the public data, event HLS disable, others can use it. @@ -57,6 +60,9 @@ extern int aac_sample_rates[]; // in ms, for HLS aac flush the audio #define SRS_CONF_DEFAULT_AAC_DELAY 100 +// max PES packets size to flush the video. +#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 1024 * 1024 + /** * the FLV/RTMP supported audio sample size. * Size of each audio sample. This parameter only pertains to @@ -103,6 +109,25 @@ class SrsMpegtsFrame SrsMpegtsFrame(); }; +/** +* write data from frame(header info) and buffer(data) to ts file. +* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter +*/ +class SrsTSMuxer +{ +private: + SrsFileWriter* writer; + std::string path; +public: + SrsTSMuxer(SrsFileWriter* w); + virtual ~SrsTSMuxer(); +public: + virtual int open(std::string _path); + virtual int write_audio(SrsMpegtsFrame* af, SrsSimpleBuffer* ab); + virtual int write_video(SrsMpegtsFrame* vf, SrsSimpleBuffer* vb); + virtual void close(); +}; + /** * jitter correct for audio, * the sample rate 44100/32000 will lost precise, diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 97802f87be..6f86169d08 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -36,18 +36,23 @@ using namespace std; #include #include #include +#include SrsTsEncoder::SrsTsEncoder() { _fs = NULL; codec = new SrsAvcAacCodec(); sample = new SrsCodecSample(); + cache = new SrsTsCache(); + muxer = NULL; } SrsTsEncoder::~SrsTsEncoder() { srs_freep(codec); srs_freep(sample); + srs_freep(cache); + srs_freep(muxer); } int SrsTsEncoder::initialize(SrsFileWriter* fs) @@ -63,6 +68,13 @@ int SrsTsEncoder::initialize(SrsFileWriter* fs) } _fs = fs; + + srs_freep(muxer); + muxer = new SrsTSMuxer(fs); + + if ((ret = muxer->open("")) != ERROR_SUCCESS) { + return ret; + } return ret; } @@ -91,10 +103,20 @@ int SrsTsEncoder::write_audio(int64_t timestamp, char* data, int size) // for the packet is filtered by consumer. int64_t dts = timestamp * 90; - /*if ((ret = hls_cache->write_audio(codec, muxer, dts, sample)) != ERROR_SUCCESS) { - srs_error("http: ts cache write audio failed. ret=%d", ret); + // write audio to cache. + if ((ret = cache->cache_audio(codec, dts, sample)) != ERROR_SUCCESS) { return ret; - }*/ + } + + // flush if buffer exceed max size. + if (cache->ab->length() > SRS_AUTO_HLS_AUDIO_CACHE_SIZE) { + if ((ret = muxer->write_audio(cache->af, cache->ab)) != ERROR_SUCCESS) { + return ret; + } + + // write success, clear and free the buffer + cache->ab->erase(cache->ab->length()); + } return ret; } @@ -126,10 +148,18 @@ int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size) } int64_t dts = timestamp * 90; - /*if ((ret = hls_cache->write_video(codec, muxer, dts, sample)) != ERROR_SUCCESS) { - srs_error("http: ts cache write video failed. ret=%d", ret); + + // write video to cache. + if ((ret = cache->cache_video(codec, dts, sample)) != ERROR_SUCCESS) { return ret; - }*/ + } + + if ((ret = muxer->write_video(cache->vf, cache->vb)) != ERROR_SUCCESS) { + return ret; + } + + // write success, clear and free the buffer + cache->vb->erase(cache->vb->length()); return ret; } diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index dbd7ee1c4b..2a42d01809 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +class SrsTsCache; +class SrsTSMuxer; class SrsFileWriter; class SrsFileReader; class SrsAvcAacCodec; @@ -46,6 +48,8 @@ class SrsTsEncoder private: SrsAvcAacCodec* codec; SrsCodecSample* sample; + SrsTsCache* cache; + SrsTSMuxer* muxer; public: SrsTsEncoder(); virtual ~SrsTsEncoder();