diff --git a/README.md b/README.md index 9102b8e22..75270d32d 100755 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ url: rtmp://127.0.0.1:1935/live/livestream * nginx v1.5.0: 139524 lines
### History +* v0.5, 2013-11-24, support write ts file. * v0.5, 2013-11-21, add ts_info tool to demux ts file. * v0.5, 2013-11-16, add rtmp players(OSMF/jwplayer5/jwplayer6). * v0.4, 2013-11-10, v0.4 released. 12500 lines. diff --git a/trunk/configure b/trunk/configure old mode 100644 new mode 100755 diff --git a/trunk/src/core/srs_core_codec.cpp b/trunk/src/core/srs_core_codec.cpp index 05d2ce41c..fb15c9331 100644 --- a/trunk/src/core/srs_core_codec.cpp +++ b/trunk/src/core/srs_core_codec.cpp @@ -24,12 +24,35 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include #include +SrsCodecBuffer::SrsCodecBuffer() +{ + size = 0; + bytes = NULL; +} + +void SrsCodecBuffer::append(void* data, int len) +{ + srs_assert(data); + srs_assert(len > 0); + + bytes = (char*)realloc(bytes, size + len); + memcpy(bytes + size, data, len); + size += len; +} + +void SrsCodecBuffer::free() +{ + size = 0; + srs_freepa(bytes); +} + SrsCodecSample::SrsCodecSample() { clear(); @@ -83,8 +106,11 @@ SrsCodec::SrsCodec() video_codec_id = 0; audio_data_rate = 0; audio_codec_id = 0; - profile = 0; - level = 0; + avc_profile = 0; + avc_level = 0; + aac_profile = 0; + aac_sample_rate = 0; + aac_channels = 0; avc_extra_size = 0; avc_extra_data = NULL; aac_extra_size = 0; @@ -160,6 +186,39 @@ int SrsCodec::audio_aac_demux(int8_t* data, int size, SrsCodecSample* sample) aac_extra_data = new char[aac_extra_size]; memcpy(aac_extra_data, stream->current(), aac_extra_size); } + + // only need to decode the first 2bytes: + // audioObjectType, aac_profile, 5bits. + // samplingFrequencyIndex, aac_sample_rate, 4bits. + // channelConfiguration, aac_channels, 4bits + if (!stream->require(2)) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode audio aac sequence header failed. ret=%d", ret); + return ret; + } + aac_profile = stream->read_1bytes(); + aac_sample_rate = stream->read_1bytes(); + + aac_channels = (aac_sample_rate >> 3) & 0x0f; + aac_sample_rate = ((aac_profile << 1) & 0x0e) | ((aac_sample_rate >> 7) & 0x01); + aac_profile = (aac_profile >> 3) & 0x1f; + + if (aac_profile == 0 || aac_profile == 0x1f) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("hls decode audio aac sequence header failed, " + "adts object=%d invalid. ret=%d", aac_profile, ret); + return ret; + } + + // aac_profile = audioObjectType - 1 + aac_profile--; + + if (aac_profile > 3) { + // Mark all extended profiles as LC + // to make Android as happy as possible. + // @see: ngx_rtmp_hls_parse_aac_header + aac_profile = 1; + } } else if (aac_packet_type == SrsCodecAudioTypeRawData) { // ensure the sequence header demuxed if (aac_extra_size <= 0 || !aac_extra_data) { diff --git a/trunk/src/core/srs_core_codec.hpp b/trunk/src/core/srs_core_codec.hpp index de576c7d3..17d1e2d49 100644 --- a/trunk/src/core/srs_core_codec.hpp +++ b/trunk/src/core/srs_core_codec.hpp @@ -181,8 +181,21 @@ enum SrsCodecAudioSoundType */ struct SrsCodecBuffer { + /** + * @remark user must manage the bytes. + */ int size; char* bytes; + + SrsCodecBuffer(); + void append(void* data, int len); + + /** + * free the bytes, + * user can invoke it to free the bytes, + * the SrsCodecBuffer never free automatically. + */ + void free(); }; /** @@ -227,9 +240,9 @@ public: // @see: SrsCodecVideo int video_codec_id; // profile_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45. - u_int8_t profile; + u_int8_t avc_profile; // level_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45. - u_int8_t level; + u_int8_t avc_level; int width; int height; int video_data_rate; // in bps @@ -243,6 +256,13 @@ public: // @see: SrsCodecAudioType int audio_codec_id; int audio_data_rate; // in bps + // 1.6.2.1 AudioSpecificConfig, in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33. + // audioObjectType, value defines in 7.1 Profiles, aac-iso-13818-7.pdf, page 40. + u_int8_t aac_profile; + // samplingFrequencyIndex + u_int8_t aac_sample_rate; + // channelConfiguration + u_int8_t aac_channels; // the avc extra data, the AVC sequence header, // without the flv codec header, // @see: ffmpeg, AVCodecContext::extradata diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp index 710665350..5a6bf602c 100644 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -112,5 +112,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HLS_BUSY 602 #define ERROR_HLS_OPEN_FAILED 603 #define ERROR_HLS_WRITE_FAILED 604 +#define ERROR_HLS_AAC_FRAME_LENGTH 605 +#define ERROR_HLS_AVC_SAMPLE_SIZE 606 #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_hls.cpp b/trunk/src/core/srs_core_hls.cpp index 9c1624e58..69f3637b2 100644 --- a/trunk/src/core/srs_core_hls.cpp +++ b/trunk/src/core/srs_core_hls.cpp @@ -26,6 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include #include @@ -275,18 +277,249 @@ u_int8_t mpegts_header[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +// @see: NGX_RTMP_HLS_DELAY, 700ms, ts_tbn=90000 +#define SRS_HLS_DELAY 63000 + +// @see: ngx_rtmp_mpegts.c +// TODO: support full mpegts feature in future. +class SrsMpegtsWriter +{ +public: + static int write_header(int fd) + { + int ret = ERROR_SUCCESS; + + if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) { + ret = ERROR_HLS_WRITE_FAILED; + srs_error("write ts file header failed. ret=%d", ret); + return ret; + } + + return ret; + } + static int write_frame(int fd, mpegts_frame* frame, SrsCodecBuffer* buffer) + { + int ret = ERROR_SUCCESS; + + char* last = buffer->bytes + buffer->size; + 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_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_HLS_DELAY); + + // dts; // 33bits + if (frame->dts != frame->pts) { + p = write_pts(p, 1, frame->dts + SRS_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 (::write(fd, packet, sizeof(packet)) != sizeof(packet)) { + ret = ERROR_HLS_WRITE_FAILED; + srs_error("write ts file failed. ret=%d", ret); + return ret; + } + } + + // write success, clear and free the buffer + buffer->free(); + + 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) + { + *p++ = (char) (pcr >> 25); + *p++ = (char) (pcr >> 17); + *p++ = (char) (pcr >> 9); + *p++ = (char) (pcr >> 1); + *p++ = (char) (pcr << 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; + } +}; + // the mpegts header specifed the video/audio pid. #define TS_VIDEO_PID 256 #define TS_AUDIO_PID 257 +// ts aac stream id. +#define TS_AUDIO_AAC 0xc0 +// ts avc stream id. +#define TS_VIDEO_AVC 0xe0 + SrsTSMuxer::SrsTSMuxer() { fd = -1; + + audio_buffer = new SrsCodecBuffer(); + video_buffer = new SrsCodecBuffer(); } SrsTSMuxer::~SrsTSMuxer() { close(); + + audio_buffer->free(); + video_buffer->free(); + + srs_freep(audio_buffer); + srs_freep(video_buffer); } int SrsTSMuxer::open(std::string _path) @@ -306,9 +539,7 @@ int SrsTSMuxer::open(std::string _path) } // write mpegts header - if (::write(fd, mpegts_header, sizeof(mpegts_header)) != sizeof(mpegts_header)) { - ret = ERROR_HLS_WRITE_FAILED; - srs_error("write ts file header %s failed. ret=%d", path.c_str(), ret); + if ((ret = SrsMpegtsWriter::write_header(fd)) != ERROR_SUCCESS) { return ret; } @@ -319,7 +550,64 @@ int SrsTSMuxer::write_audio(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam { int ret = ERROR_SUCCESS; - static u_int8_t packet[188]; + for (int i = 0; i < sample->nb_buffers; i++) { + SrsCodecBuffer* buf = &sample->buffers[i]; + int32_t size = buf->size; + + if (!buf->bytes || size <= 0 || size > 0x1fff) { + ret = ERROR_HLS_AAC_FRAME_LENGTH; + srs_error("invalid aac frame length=%d, ret=%d", size, ret); + return ret; + } + + // AAC-ADTS + // 6.2 Audio Data Transport Stream, ADTS + // in aac-iso-13818-7.pdf, page 26. + // fixed 7bytes header + static u_int8_t adts_header[7] = {0xff, 0xf1, 0x00, 0x00, 0x00, 0x0f, 0xfc}; + /* + // adts_fixed_header + // 2B, 16bits + int16_t syncword; //12bits, '1111 1111 1111' + int8_t ID; //1bit, '0' + int8_t layer; //2bits, '00' + int8_t protection_absent; //1bit, can be '1' + // 12bits + int8_t profile; //2bit, 7.1 Profiles, page 40 + TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46 + int8_t private_bit; //1bit, can be '0' + int8_t channel_configuration; //3bits, Table 8 + int8_t original_or_copy; //1bit, can be '0' + int8_t home; //1bit, can be '0' + + // adts_variable_header + // 28bits + int8_t copyright_identification_bit; //1bit, can be '0' + int8_t copyright_identification_start; //1bit, can be '0' + int16_t frame_length; //13bits + int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream. + int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block() + */ + // profile, 2bits + adts_header[2] = (codec->aac_profile << 6) & 0xc0; + // sampling_frequency_index 4bits + adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c; + // channel_configuration 3bits + adts_header[2] |= (codec->aac_channels >> 1) & 0x01; + adts_header[3] = (codec->aac_channels << 5) & 0xc0; + // frame_length 13bits + adts_header[3] |= (size >> 11) & 0x03; + adts_header[4] = (size >> 3) & 0xff; + adts_header[5] = (size << 5) & 0xcf; + + // copy to audio buffer + audio_buffer->append(adts_header, sizeof(adts_header)); + audio_buffer->append(buf->bytes, buf->size); + } + + audio_frame.dts = audio_frame.pts = time * 90; + audio_frame.pid = TS_AUDIO_PID; + audio_frame.sid = TS_AUDIO_AAC; return ret; } @@ -328,7 +616,45 @@ int SrsTSMuxer::write_video(u_int32_t time, SrsCodec* codec, SrsCodecSample* sam { int ret = ERROR_SUCCESS; - static u_int8_t packet[188]; + static u_int8_t aud_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 }; + video_buffer->append(aud_nal, sizeof(aud_nal)); + + for (int i = 0; i < sample->nb_buffers; i++) { + SrsCodecBuffer* buf = &sample->buffers[i]; + int32_t size = buf->size; + + if (!buf->bytes || size <= 0) { + ret = ERROR_HLS_AVC_SAMPLE_SIZE; + srs_error("invalid avc sample length=%d, ret=%d", size, ret); + return ret; + } + + // sample start prefix, '00 00 00 01' or '00 00 01' + u_int8_t* p = aud_nal + 1; + u_int8_t* end = p + 3; + + // first AnnexB prefix is long (4 bytes) + if (i == 0) { + p = aud_nal; + } + video_buffer->append(p, end - p); + + // sample data + video_buffer->append(buf->bytes, buf->size); + } + + video_frame.dts = time * 90; + video_frame.pts = video_frame.dts + sample->cts * 90; + video_frame.pid = TS_VIDEO_PID; + video_frame.sid = TS_VIDEO_AVC; + video_frame.key = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame; + + if ((ret = SrsMpegtsWriter::write_frame(fd, &video_frame, video_buffer)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = SrsMpegtsWriter::write_frame(fd, &audio_frame, audio_buffer)) != ERROR_SUCCESS) { + return ret; + } return ret; } diff --git a/trunk/src/core/srs_core_hls.hpp b/trunk/src/core/srs_core_hls.hpp index b065613d4..fa10ed0fe 100644 --- a/trunk/src/core/srs_core_hls.hpp +++ b/trunk/src/core/srs_core_hls.hpp @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsOnMetaDataPacket; class SrsCommonMessage; class SrsCodecSample; +class SrsCodecBuffer; class SrsTSMuxer; class SrsCodec; @@ -56,11 +57,34 @@ public: virtual int on_video(SrsCommonMessage* video); }; +// @see: ngx_rtmp_mpegts_frame_t +struct mpegts_frame +{ + int64_t pts; + int64_t dts; + int pid; + int sid; + int cc; + bool key; + + mpegts_frame() + { + pts = dts = 0; + pid = sid = cc = 0; + key = false; + } +}; + class SrsTSMuxer { private: int fd; std::string path; +private: + mpegts_frame audio_frame; + SrsCodecBuffer* audio_buffer; + mpegts_frame video_frame; + SrsCodecBuffer* video_buffer; public: SrsTSMuxer(); virtual ~SrsTSMuxer();