From 16afe7ddbb43a000b848679501a746cef25b07d7 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 31 Jan 2015 23:20:00 +0800 Subject: [PATCH] fix #250, support push MPEGTS over UDP to SRS. 2.0.111 --- README.md | 3 +- trunk/src/app/srs_app_mpegts_udp.cpp | 188 ++++++++++++++------ trunk/src/app/srs_app_mpegts_udp.hpp | 10 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 241 +++++-------------------- trunk/src/protocol/srs_raw_avc.cpp | 252 +++++++++++++++++++++++++++ trunk/src/protocol/srs_raw_avc.hpp | 58 ++++++ 7 files changed, 505 insertions(+), 249 deletions(-) diff --git a/README.md b/README.md index 6d7c20ca32..9d94f39d0d 100755 --- a/README.md +++ b/README.md @@ -486,7 +486,7 @@ Supported operating systems and hardware: ). 1. Support HLS(h.264+mp3) streaming, read [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301). -1. [dev] Support push MPEG-TS over UDP to SRS, read +1. Support push MPEG-TS over UDP to SRS, read [#250](https://github.com/winlinvip/simple-rtmp-server/issues/250). 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). @@ -525,6 +525,7 @@ Supported operating systems and hardware: ### SRS 2.0 history +* v2.0, 2015-01-31, for [#250](https://github.com/winlinvip/simple-rtmp-server/issues/250), support push MPEGTS over UDP to SRS. 2.0.111 * v2.0, 2015-01-29, build libfdk-aac in ffmpeg. 2.0.108 * v2.0, 2015-01-25, for [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301), hls support h.264+mp3, ok for vlc. 2.0.107 * v2.0, 2015-01-25, for [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301), http ts stream support h.264+mp3. 2.0.106 diff --git a/trunk/src/app/srs_app_mpegts_udp.cpp b/trunk/src/app/srs_app_mpegts_udp.cpp index aa86cb0d7a..1aa1c380b4 100644 --- a/trunk/src/app/srs_app_mpegts_udp.cpp +++ b/trunk/src/app/srs_app_mpegts_udp.cpp @@ -76,10 +76,20 @@ int SrsMpegtsQueue::push(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; - if (msgs.find(msg->timestamp) != msgs.end()) { - srs_warn("mpegts: free the msg for dts exists, dts=%"PRId64, msg->timestamp); - srs_freep(msg); - return ret; + // TODO: FIXME: use right way. + for (int i = 0; i < 10; i++) { + if (msgs.find(msg->timestamp) == msgs.end()) { + break; + } + + // adjust the ts, add 1ms. + msg->timestamp += 1; + + if (i >= 5) { + srs_warn("mpegts: free the msg for dts exists, dts=%"PRId64, msg->timestamp); + srs_freep(msg); + return ret; + } } if (msg->is_audio()) { @@ -114,6 +124,8 @@ SrsSharedPtrMessage* SrsMpegtsQueue::dequeue() if (msg->is_video()) { nb_videos--; } + + return msg; } return NULL; @@ -131,6 +143,7 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) stfd = NULL; stream_id = 0; avc = new SrsRawH264Stream(); + aac = new SrsRawAacStream(); h264_sps_changed = false; h264_pps_changed = false; h264_sps_pps_sent = false; @@ -145,6 +158,7 @@ SrsMpegtsOverUdp::~SrsMpegtsOverUdp() srs_freep(stream); srs_freep(context); srs_freep(avc); + srs_freep(aac); srs_freep(queue); } @@ -309,6 +323,9 @@ int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg) if (msg->channel->stream == SrsTsStreamVideoH264) { return on_ts_video(msg, &avs); } + if (msg->channel->stream == SrsTsStreamAudioAAC) { + return on_ts_audio(msg, &avs); + } // TODO: FIXME: implements it. return ret; @@ -326,6 +343,10 @@ int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs) // ts tbn to flv tbn. u_int32_t dts = msg->dts / 90; u_int32_t pts = msg->dts / 90; + + // the whole ts pes video packet must be a flv frame packet. + char* ibpframe = avs->data() + avs->pos(); + int ibpframe_size = avs->size() - avs->pos(); // send each frame. while (!avs->empty()) { @@ -342,59 +363,50 @@ int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs) continue; } - // it may be return error, but we must process all packets. - if ((ret = write_h264_raw_frame(frame, frame_size, dts, pts)) != ERROR_SUCCESS) { - if (ret == ERROR_H264_DROP_BEFORE_SPS_PPS) { + // for sps + if (avc->is_sps(frame, frame_size)) { + std::string sps; + if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { + return ret; + } + + if (h264_sps == sps) { continue; } - return ret; - } - - // for video, drop others with same pts/dts. - break; - } - - return ret; -} - -int SrsMpegtsOverUdp::write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) -{ - int ret = ERROR_SUCCESS; - - // for sps - if (avc->is_sps(frame, frame_size)) { - std::string sps; - if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { - return ret; - } + h264_sps_changed = true; + h264_sps = sps; - if (h264_sps == sps) { - return ret; + if ((ret = write_h264_sps_pps(dts, pts)) != ERROR_SUCCESS) { + return ret; + } + continue; } - h264_sps_changed = true; - h264_sps = sps; - - return write_h264_sps_pps(dts, pts); - } - // for pps - if (avc->is_pps(frame, frame_size)) { - std::string pps; - if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { - return ret; - } + // for pps + if (avc->is_pps(frame, frame_size)) { + std::string pps; + if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { + return ret; + } - if (h264_pps == pps) { - return ret; - } - h264_pps_changed = true; - h264_pps = pps; + if (h264_pps == pps) { + continue; + } + h264_pps_changed = true; + h264_pps = pps; - return write_h264_sps_pps(dts, pts); + if ((ret = write_h264_sps_pps(dts, pts)) != ERROR_SUCCESS) { + return ret; + } + continue; + } + + break; } // ibp frame. - return write_h264_ipb_frame(frame, frame_size, dts, pts); + srs_info("mpegts: demux avc ibp frame size=%d, dts=%d", ibpframe_size, dts); + return write_h264_ipb_frame(ibpframe, ibpframe_size, dts, pts); } int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts) @@ -421,14 +433,18 @@ int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts) return ret; } + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; + if ((ret = rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv)) != ERROR_SUCCESS) { + return ret; + } + // reset sps and pps. h264_sps_changed = false; h264_pps_changed = false; h264_sps_pps_sent = true; - - // the timestamp in rtmp message header is dts. - u_int32_t timestamp = dts; - return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); + + return ret; } int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) @@ -459,6 +475,72 @@ int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_ return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); } +int SrsMpegtsOverUdp::on_ts_audio(SrsTsMessage* msg, SrsStream* avs) +{ + int ret = ERROR_SUCCESS; + + // ensure rtmp connected. + if ((ret = connect()) != ERROR_SUCCESS) { + return ret; + } + + // ts tbn to flv tbn. + u_int32_t dts = msg->dts / 90; + + // send each frame. + while (!avs->empty()) { + char* frame = NULL; + int frame_size = 0; + SrsRawAacStreamCodec codec; + if ((ret = aac->adts_demux(avs, &frame, &frame_size, codec)) != ERROR_SUCCESS) { + return ret; + } + + // ignore invalid frame, + // * atleast 1bytes for aac to decode the data. + if (frame_size <= 0) { + continue; + } + srs_info("mpegts: demux aac frame size=%d, dts=%d", frame_size, dts); + + // generate sh. + if (aac_specific_config.empty()) { + std::string sh; + if ((ret = aac->mux_sequence_header(&codec, sh)) != ERROR_SUCCESS) { + return ret; + } + aac_specific_config = sh; + + codec.aac_packet_type = 0; + + if ((ret = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, dts)) != ERROR_SUCCESS) { + return ret; + } + } + + // audio raw data. + codec.aac_packet_type = 1; + if ((ret = write_audio_raw_frame(frame, frame_size, &codec, dts)) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + +int SrsMpegtsOverUdp::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts) +{ + int ret = ERROR_SUCCESS; + + char* data = NULL; + int size = 0; + if ((ret = aac->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != ERROR_SUCCESS) { + return ret; + } + + return rtmp_write_packet(SrsCodecFlvTagAudio, dts, data, size); +} + int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size) { int ret = ERROR_SUCCESS; @@ -482,6 +564,10 @@ int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* da if ((msg = queue->dequeue()) == NULL) { break; } + + // TODO: FIXME: use pithy print. + srs_info("mpegts: send msg %s dts=%"PRId64", size=%d", + msg->is_audio()? "A":msg->is_video()? "V":"N", msg->timestamp, msg->size); // send out encoded msg. if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { diff --git a/trunk/src/app/srs_app_mpegts_udp.hpp b/trunk/src/app/srs_app_mpegts_udp.hpp index 323732722d..0455c1eb5b 100644 --- a/trunk/src/app/srs_app_mpegts_udp.hpp +++ b/trunk/src/app/srs_app_mpegts_udp.hpp @@ -45,6 +45,8 @@ class SrsStSocket; class SrsRequest; class SrsRawH264Stream; class SrsSharedPtrMessage; +class SrsRawAacStream; +class SrsRawAacStreamCodec; #include #include @@ -114,6 +116,10 @@ class SrsMpegtsOverUdp : virtual public ISrsTsHandler std::string h264_pps; bool h264_pps_changed; bool h264_sps_pps_sent; +private: + SrsRawAacStream* aac; + std::string aac_specific_config; +private: SrsMpegtsQueue* queue; public: SrsMpegtsOverUdp(SrsConfDirective* c); @@ -126,9 +132,11 @@ class SrsMpegtsOverUdp : virtual public ISrsTsHandler virtual int on_ts_message(SrsTsMessage* msg); private: virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs); - virtual int write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); virtual int write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); + virtual int on_ts_audio(SrsTsMessage* msg, SrsStream* avs); + virtual int write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts); +private: virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size); private: // connect to rtmp output url. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index a2ce31ebc9..90142fb6f3 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 110 +#define VERSION_REVISION 111 // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 3a8bb5d236..1a1705f259 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -81,9 +81,10 @@ struct Context SimpleSocketStream* skt; int stream_id; - // for h264 raw stream, - // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 + // the remux raw codec. SrsRawH264Stream avc_raw; + SrsRawAacStream aac_raw; + // for h264 raw stream, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 SrsStream h264_raw_stream; @@ -1073,106 +1074,44 @@ srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size) * directly write a audio frame. */ int __srs_write_audio_raw_frame(Context* context, - char sound_format, char sound_rate, char sound_size, char sound_type, - char aac_packet_type, char* frame, int frame_size, u_int32_t timestamp + char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t timestamp ) { - - // for audio frame, there is 1 or 2 bytes header: - // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType - // 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header. - int size = frame_size + 1; - if (sound_format == SrsCodecAudioAAC) { - size += 1; - } - char* data = new char[size]; - char* p = data; - - u_int8_t audio_header = sound_type & 0x01; - audio_header |= (sound_size << 1) & 0x02; - audio_header |= (sound_rate << 2) & 0x0c; - audio_header |= (sound_format << 4) & 0xf0; - - *p++ = audio_header; - - if (sound_format == SrsCodecAudioAAC) { - *p++ = aac_packet_type; + int ret = ERROR_SUCCESS; + + char* data = NULL; + int size = 0; + if ((ret = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != ERROR_SUCCESS) { + return ret; } - memcpy(p, frame, frame_size); - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); } /** * write aac frame in adts. */ -int __srs_write_aac_adts_frame(Context* context, - char sound_format, char sound_rate, char sound_size, char sound_type, - char aac_profile, char aac_samplerate, char aac_channel, - char* frame, int frame_size, u_int32_t timestamp +int __srs_write_aac_adts_frame(Context* context, + SrsRawAacStreamCodec* codec, char* frame, int frame_size, u_int32_t timestamp ) { int ret = ERROR_SUCCESS; - // override the aac samplerate by user specified. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899 - switch (sound_rate) { - case SrsCodecAudioSampleRate11025: - aac_samplerate = 0x0a; break; - case SrsCodecAudioSampleRate22050: - aac_samplerate = 0x07; break; - case SrsCodecAudioSampleRate44100: - aac_samplerate = 0x04; break; - default: - break; - } - // send out aac sequence header if not sent. if (context->aac_specific_config.empty()) { - char ch = 0; - // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf - // AudioSpecificConfig (), page 33 - // 1.6.2.1 AudioSpecificConfig - // audioObjectType; 5 bslbf - ch = (aac_profile << 3) & 0xf8; - // 3bits left. - - // samplingFrequencyIndex; 4 bslbf - ch |= (aac_samplerate >> 1) & 0x07; - context->aac_specific_config += ch; - ch = (aac_samplerate << 7) & 0x80; - if (aac_samplerate == 0x0f) { - return ERROR_AAC_DATA_INVALID; - } - // 7bits left. - - // channelConfiguration; 4 bslbf - ch |= (aac_channel << 3) & 0x78; - // 3bits left. - - // only support aac profile 1-4. - if (aac_profile < 1 || aac_profile > 4) { - return ERROR_AAC_DATA_INVALID; + std::string sh; + if ((ret = context->aac_raw.mux_sequence_header(codec, sh)) != ERROR_SUCCESS) { + return ret; } - // GASpecificConfig(), page 451 - // 4.4.1 Decoder configuration (GASpecificConfig) - // frameLengthFlag; 1 bslbf - // dependsOnCoreCoder; 1 bslbf - // extensionFlag; 1 bslbf - context->aac_specific_config += ch; - - char* sh = (char*)context->aac_specific_config.data(); - int nb_sh = (int)context->aac_specific_config.length(); - if ((ret = __srs_write_audio_raw_frame(context, - sound_format, sound_rate, sound_size, sound_type, - 0, sh, nb_sh, timestamp)) != ERROR_SUCCESS - ) { + context->aac_specific_config = sh; + + codec->aac_packet_type = 0; + + if ((ret = __srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) { return ret; } } - return __srs_write_audio_raw_frame(context, - sound_format, sound_rate, sound_size, sound_type, - 1, frame, frame_size, timestamp); + codec->aac_packet_type = 1; + return __srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp); } /** @@ -1180,126 +1119,32 @@ int __srs_write_aac_adts_frame(Context* context, */ int __srs_write_aac_adts_frames(Context* context, char sound_format, char sound_rate, char sound_size, char sound_type, - char* frame, int frame_size, u_int32_t timestamp + char* frames, int frames_size, u_int32_t timestamp ) { int ret = ERROR_SUCCESS; SrsStream* stream = &context->aac_raw_stream; - if ((ret = stream->initialize(frame, frame_size)) != ERROR_SUCCESS) { + if ((ret = stream->initialize(frames, frames_size)) != ERROR_SUCCESS) { return ret; } while (!stream->empty()) { - int adts_header_start = stream->pos(); - - // decode the ADTS. - // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, - // 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS - // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885 - // byte_alignment() - - // adts_fixed_header: - // 12bits syncword, - // 16bits left. - // adts_variable_header: - // 28bits - // 12+16+28=56bits - // adts_error_check: - // 16bits if protection_absent - // 56+16=72bits - // if protection_absent: - // require(7bytes)=56bits - // else - // require(9bytes)=72bits - if (!stream->require(7)) { - return ERROR_AAC_ADTS_HEADER; - } - - // for aac, the frame must be ADTS format. - if (!srs_aac_startswith_adts(stream)) { - return ERROR_AAC_REQUIRED_ADTS; - } - - // Syncword 12 bslbf - stream->read_1bytes(); - // 4bits left. - // adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS - // ID 1 bslbf - // Layer 2 uimsbf - // protection_absent 1 bslbf - int8_t fh0 = (stream->read_1bytes() & 0x0f); - /*int8_t fh_id = (fh0 >> 3) & 0x01;*/ - /*int8_t fh_layer = (fh0 >> 1) & 0x03;*/ - int8_t fh_protection_absent = fh0 & 0x01; - - int16_t fh1 = stream->read_2bytes(); - // Profile_ObjectType 2 uimsbf - // sampling_frequency_index 4 uimsbf - // private_bit 1 bslbf - // channel_configuration 3 uimsbf - // original/copy 1 bslbf - // home 1 bslbf - int8_t fh_Profile_ObjectType = (fh1 >> 14) & 0x03; - int8_t fh_sampling_frequency_index = (fh1 >> 10) & 0x0f; - /*int8_t fh_private_bit = (fh1 >> 9) & 0x01;*/ - int8_t fh_channel_configuration = (fh1 >> 6) & 0x07; - /*int8_t fh_original = (fh1 >> 5) & 0x01;*/ - /*int8_t fh_home = (fh1 >> 4) & 0x01;*/ - // @remark, Emphasis is removed, - // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736 - //int8_t fh_Emphasis = (fh1 >> 2) & 0x03; - // 4bits left. - // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS - // copyright_identification_bit 1 bslbf - // copyright_identification_start 1 bslbf - /*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/ - /*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/ - // aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes. - // use the left 2bits as the 13 and 12 bit, - // the aac_frame_length is 13bits, so we move 13-2=11. - int16_t fh_aac_frame_length = (fh1 << 11) & 0x0800; - - int32_t fh2 = stream->read_3bytes(); - // aac_frame_length 13 bslbf: consume the first 13-2=11bits - // the fh2 is 24bits, so we move right 24-11=13. - fh_aac_frame_length |= (fh2 >> 13) & 0x07ff; - // adts_buffer_fullness 11 bslbf - /*int16_t fh_adts_buffer_fullness = (fh2 >> 2) & 0x7ff;*/ - // no_raw_data_blocks_in_frame 2 uimsbf - /*int16_t fh_no_raw_data_blocks_in_frame = fh2 & 0x03;*/ - // adts_error_check(), 1.A.2.2.3 Error detection - if (!fh_protection_absent) { - if (!stream->require(2)) { - return ERROR_AAC_ADTS_HEADER; - } - // crc_check 16 Rpchof - /*int16_t crc_check = */stream->read_2bytes(); - } - - // TODO: check the fh_sampling_frequency_index - // TODO: check the fh_channel_configuration - - // raw_data_blocks - int adts_header_size = stream->pos() - adts_header_start; - int raw_data_size = fh_aac_frame_length - adts_header_size; - if (!stream->require(raw_data_size)) { - return ERROR_AAC_ADTS_HEADER; + char* frame = NULL; + int frame_size = 0; + SrsRawAacStreamCodec codec; + if ((ret = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != ERROR_SUCCESS) { + return ret; } - - // the profile = object_id + 1 - // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, - // Table 1. A.9 – MPEG-2 Audio profiles and MPEG-4 Audio object types - char aac_profile = fh_Profile_ObjectType + 1; - - char* raw_data = stream->data() + stream->pos(); - if ((ret = __srs_write_aac_adts_frame(context, - sound_format, sound_rate, sound_size, sound_type, - aac_profile, fh_sampling_frequency_index, fh_channel_configuration, - raw_data, raw_data_size, timestamp)) != ERROR_SUCCESS - ) { + + // override by user specified. + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; + + if ((ret = __srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) { return ret; } - stream->skip(raw_data_size); } return ret; @@ -1328,10 +1173,16 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, sound_format, sound_rate, sound_size, sound_type, frame, frame_size, timestamp); } else { + // use codec info for aac. + SrsRawAacStreamCodec codec; + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; + codec.aac_packet_type = 0; + // for other data, directly write frame. - return __srs_write_audio_raw_frame(context, - sound_format, sound_rate, sound_size, sound_type, - 0, frame, frame_size, timestamp); + return __srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp); } diff --git a/trunk/src/protocol/srs_raw_avc.cpp b/trunk/src/protocol/srs_raw_avc.cpp index 084a638b54..65f42c683a 100644 --- a/trunk/src/protocol/srs_raw_avc.cpp +++ b/trunk/src/protocol/srs_raw_avc.cpp @@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace std; #include +#include #include #include #include @@ -312,3 +313,254 @@ int SrsRawH264Stream::mux_avc2flv(string video, int8_t frame_type, int8_t avc_pa return ret; } +SrsRawAacStream::SrsRawAacStream() +{ +} + +SrsRawAacStream::~SrsRawAacStream() +{ +} + +int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec) +{ + int ret = ERROR_SUCCESS; + + while (!stream->empty()) { + int adts_header_start = stream->pos(); + + // decode the ADTS. + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, + // 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885 + // byte_alignment() + + // adts_fixed_header: + // 12bits syncword, + // 16bits left. + // adts_variable_header: + // 28bits + // 12+16+28=56bits + // adts_error_check: + // 16bits if protection_absent + // 56+16=72bits + // if protection_absent: + // require(7bytes)=56bits + // else + // require(9bytes)=72bits + if (!stream->require(7)) { + return ERROR_AAC_ADTS_HEADER; + } + + // for aac, the frame must be ADTS format. + if (!srs_aac_startswith_adts(stream)) { + return ERROR_AAC_REQUIRED_ADTS; + } + + // Syncword 12 bslbf + stream->read_1bytes(); + // 4bits left. + // adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS + // ID 1 bslbf + // Layer 2 uimsbf + // protection_absent 1 bslbf + int8_t fh0 = (stream->read_1bytes() & 0x0f); + /*int8_t fh_id = (fh0 >> 3) & 0x01;*/ + /*int8_t fh_layer = (fh0 >> 1) & 0x03;*/ + int8_t fh_protection_absent = fh0 & 0x01; + + int16_t fh1 = stream->read_2bytes(); + // Profile_ObjectType 2 uimsbf + // sampling_frequency_index 4 uimsbf + // private_bit 1 bslbf + // channel_configuration 3 uimsbf + // original/copy 1 bslbf + // home 1 bslbf + int8_t fh_Profile_ObjectType = (fh1 >> 14) & 0x03; + int8_t fh_sampling_frequency_index = (fh1 >> 10) & 0x0f; + /*int8_t fh_private_bit = (fh1 >> 9) & 0x01;*/ + int8_t fh_channel_configuration = (fh1 >> 6) & 0x07; + /*int8_t fh_original = (fh1 >> 5) & 0x01;*/ + /*int8_t fh_home = (fh1 >> 4) & 0x01;*/ + // @remark, Emphasis is removed, + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736 + //int8_t fh_Emphasis = (fh1 >> 2) & 0x03; + // 4bits left. + // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS + // copyright_identification_bit 1 bslbf + // copyright_identification_start 1 bslbf + /*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/ + /*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/ + // aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes. + // use the left 2bits as the 13 and 12 bit, + // the aac_frame_length is 13bits, so we move 13-2=11. + int16_t fh_aac_frame_length = (fh1 << 11) & 0x0800; + + int32_t fh2 = stream->read_3bytes(); + // aac_frame_length 13 bslbf: consume the first 13-2=11bits + // the fh2 is 24bits, so we move right 24-11=13. + fh_aac_frame_length |= (fh2 >> 13) & 0x07ff; + // adts_buffer_fullness 11 bslbf + /*int16_t fh_adts_buffer_fullness = (fh2 >> 2) & 0x7ff;*/ + // no_raw_data_blocks_in_frame 2 uimsbf + /*int16_t fh_no_raw_data_blocks_in_frame = fh2 & 0x03;*/ + // adts_error_check(), 1.A.2.2.3 Error detection + if (!fh_protection_absent) { + if (!stream->require(2)) { + return ERROR_AAC_ADTS_HEADER; + } + // crc_check 16 Rpchof + /*int16_t crc_check = */stream->read_2bytes(); + } + + // TODO: check the fh_sampling_frequency_index + // TODO: check the fh_channel_configuration + + // raw_data_blocks + int adts_header_size = stream->pos() - adts_header_start; + int raw_data_size = fh_aac_frame_length - adts_header_size; + if (!stream->require(raw_data_size)) { + return ERROR_AAC_ADTS_HEADER; + } + + // the profile = object_id + 1 + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, + // Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types + char aac_profile = fh_Profile_ObjectType + 1; + + // the codec info. + codec.protection_absent = fh_protection_absent; + codec.Profile_ObjectType = fh_Profile_ObjectType; + codec.sampling_frequency_index = fh_sampling_frequency_index; + codec.channel_configuration = fh_channel_configuration; + codec.aac_frame_length = fh_aac_frame_length; + + codec.aac_profile = aac_profile; + codec.aac_samplerate = fh_sampling_frequency_index; + codec.aac_channel = fh_channel_configuration; + + // @see srs_audio_write_raw_frame(). + codec.sound_format = 10; // AAC + if (fh_sampling_frequency_index <= 0x0c && fh_sampling_frequency_index > 0x0a) { + codec.sound_rate = SrsCodecAudioSampleRate5512; + } else if (fh_sampling_frequency_index <= 0x0a && fh_sampling_frequency_index > 0x07) { + codec.sound_rate = SrsCodecAudioSampleRate11025; + } else if (fh_sampling_frequency_index <= 0x07 && fh_sampling_frequency_index > 0x04) { + codec.sound_rate = SrsCodecAudioSampleRate22050; + } else if (fh_sampling_frequency_index <= 0x04) { + codec.sound_rate = SrsCodecAudioSampleRate44100; + } else { + codec.sound_rate = SrsCodecAudioSampleRate44100; + srs_warn("adts invalid sample rate for flv, rate=%#x", fh_sampling_frequency_index); + } + codec.sound_size = srs_max(0, srs_min(1, fh_channel_configuration - 1)); + // TODO: FIXME: finger it out the sound size by adts. + codec.sound_size = 1; // 0(8bits) or 1(16bits). + + // frame data. + *pframe = stream->data() + stream->pos(); + *pnb_frame = raw_data_size; + stream->skip(raw_data_size); + + break; + } + + return ret; +} + +int SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, string& sh) +{ + int ret = ERROR_SUCCESS; + + char aac_channel = codec->aac_channel; + char aac_profile = codec->aac_profile; + char aac_samplerate = codec->aac_samplerate; + + // override the aac samplerate by user specified. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899 + switch (codec->sound_rate) { + case SrsCodecAudioSampleRate11025: + aac_samplerate = 0x0a; break; + case SrsCodecAudioSampleRate22050: + aac_samplerate = 0x07; break; + case SrsCodecAudioSampleRate44100: + aac_samplerate = 0x04; break; + default: + break; + } + + sh = ""; + + char ch = 0; + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf + // AudioSpecificConfig (), page 33 + // 1.6.2.1 AudioSpecificConfig + // audioObjectType; 5 bslbf + ch = (aac_profile << 3) & 0xf8; + // 3bits left. + + // samplingFrequencyIndex; 4 bslbf + ch |= (aac_samplerate >> 1) & 0x07; + sh += ch; + ch = (aac_samplerate << 7) & 0x80; + if (aac_samplerate == 0x0f) { + return ERROR_AAC_DATA_INVALID; + } + // 7bits left. + + // channelConfiguration; 4 bslbf + ch |= (aac_channel << 3) & 0x78; + // 3bits left. + + // only support aac profile 1-4. + if (aac_profile < 1 || aac_profile > 4) { + return ERROR_AAC_DATA_INVALID; + } + // GASpecificConfig(), page 451 + // 4.4.1 Decoder configuration (GASpecificConfig) + // frameLengthFlag; 1 bslbf + // dependsOnCoreCoder; 1 bslbf + // extensionFlag; 1 bslbf + sh += ch; + + return ret; +} + +int SrsRawAacStream::mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv) +{ + int ret = ERROR_SUCCESS; + + char sound_format = codec->sound_format; + char sound_type = codec->sound_type; + char sound_size = codec->sound_size; + char sound_rate = codec->sound_rate; + char aac_packet_type = codec->aac_packet_type; + + // for audio frame, there is 1 or 2 bytes header: + // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType + // 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header. + int size = nb_frame + 1; + if (sound_format == SrsCodecAudioAAC) { + size += 1; + } + char* data = new char[size]; + char* p = data; + + u_int8_t audio_header = sound_type & 0x01; + audio_header |= (sound_size << 1) & 0x02; + audio_header |= (sound_rate << 2) & 0x0c; + audio_header |= (sound_format << 4) & 0xf0; + + *p++ = audio_header; + + if (sound_format == SrsCodecAudioAAC) { + *p++ = aac_packet_type; + } + + memcpy(p, frame, nb_frame); + + *flv = data; + *nb_flv = size; + + return ret; +} + diff --git a/trunk/src/protocol/srs_raw_avc.hpp b/trunk/src/protocol/srs_raw_avc.hpp index a175e540f4..5fcdf62a00 100644 --- a/trunk/src/protocol/srs_raw_avc.hpp +++ b/trunk/src/protocol/srs_raw_avc.hpp @@ -86,4 +86,62 @@ class SrsRawH264Stream virtual int mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv); }; +/** +* the header of adts sample. +*/ +struct SrsRawAacStreamCodec +{ + int8_t protection_absent; + int8_t Profile_ObjectType; + int8_t sampling_frequency_index; + int8_t channel_configuration; + int16_t aac_frame_length; + + // calc by Profile_ObjectType+1 + char aac_profile; + char aac_samplerate; + char aac_channel; + + char sound_format; + char sound_rate; + char sound_size; + char sound_type; + // 0 for sh; 1 for raw data. + int8_t aac_packet_type; +}; + +/** +* the raw aac stream, in adts. +*/ +class SrsRawAacStream +{ +public: + SrsRawAacStream(); + virtual ~SrsRawAacStream(); +public: + /** + * demux the stream in adts format. + * @param stream the input stream bytes. + * @param pframe the output aac frame in stream. user should never free it. + * @param pnb_frame the output aac frame size. + * @param codec the output codec info. + */ + virtual int adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec); + /** + * aac raw data to aac packet, without flv payload header. + * mux the aac specific config to flv sequence header packet. + * @param sh output the sequence header. + */ + virtual int mux_sequence_header(SrsRawAacStreamCodec* codec, std::string& sh); + /** + * mux the aac audio packet to flv audio packet. + * @param frame the aac raw data. + * @param nb_frame the count of aac frame. + * @param codec the codec info of aac. + * @param flv output the muxed flv packet. + * @param nb_flv output the muxed flv size. + */ + virtual int mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv); +}; + #endif