From e492fa53531ee8f7bc95642e495e064485dfa1ff Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 24 Nov 2014 16:28:52 +0800 Subject: [PATCH] fix #212, support publish aac adts raw stream. 2.0.31. --- README.md | 5 + trunk/research/librtmp/Makefile | 6 +- trunk/research/librtmp/srs_aac_raw_publish.c | 198 +++++++++++ .../research/librtmp/srs_audio_raw_publish.c | 2 +- trunk/research/librtmp/srs_h264_raw_publish.c | 18 +- trunk/src/app/srs_app_avc_aac.cpp | 3 - trunk/src/app/srs_app_avc_aac.hpp | 24 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_codec.hpp | 19 ++ trunk/src/kernel/srs_kernel_error.hpp | 3 + trunk/src/libs/srs_librtmp.cpp | 323 ++++++++++++++++-- trunk/src/libs/srs_librtmp.hpp | 73 ++-- trunk/src/rtmp/srs_protocol_utility.cpp | 22 +- trunk/src/rtmp/srs_protocol_utility.hpp | 7 + trunk/src/srs/srs.upp | 1 + 15 files changed, 620 insertions(+), 86 deletions(-) create mode 100644 trunk/research/librtmp/srs_aac_raw_publish.c diff --git a/README.md b/README.md index e621562c4..462b4c45c 100755 --- a/README.md +++ b/README.md @@ -451,6 +451,10 @@ Supported operating systems and hardware: 1. Support compile [srs-librtmp on windows](https://github.com/winlinvip/srs.librtmp), [bug #213](https://github.com/winlinvip/simple-rtmp-server/issues/213). 1. Support [7.5k+ clients](https://github.com/winlinvip/simple-rtmp-server/issues/217), 4Gbps per process. +1. Support publish aac adts raw stream( +[CN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-audio-raw-stream), +[EN](https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_SrsLibrtmp#publish-audio-raw-stream) +) by srs-librtmp. 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). 1. [no-plan] Support multiple processes, for both origin and edge @@ -483,6 +487,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v2.0, 2014-11-24, fix [#212](https://github.com/winlinvip/simple-rtmp-server/issues/212), support publish aac adts raw stream. 2.0.31. * v2.0, 2014-11-22, fix [#217](https://github.com/winlinvip/simple-rtmp-server/issues/217), remove timeout recv, support 7.5k+ 250kbps clients. 2.0.30. * v2.0, 2014-11-21, srs-librtmp add rtmp prefix for rtmp/utils/human apis. 2.0.29. * v2.0, 2014-11-21, refine examples of srs-librtmp, add srs_print_rtmp_packet. 2.0.28. diff --git a/trunk/research/librtmp/Makefile b/trunk/research/librtmp/Makefile index d5f19606e..f8e9db4f9 100755 --- a/trunk/research/librtmp/Makefile +++ b/trunk/research/librtmp/Makefile @@ -7,7 +7,7 @@ else objs/srs_flv_injecter objs/srs_publish objs/srs_play \ objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \ objs/srs_bandwidth_check objs/srs_h264_raw_publish \ - objs/srs_audio_raw_publish + objs/srs_audio_raw_publish objs/srs_aac_raw_publish endif .PHONY: default clean help ssl nossl @@ -26,6 +26,7 @@ help: @echo " srs_publish publish program using srs-librtmp" @echo " srs_h264_raw_publish publish raw h.264 stream to SSR by srs-librtmp" @echo " srs_audio_raw_publish publish raw audio stream to SSR by srs-librtmp" + @echo " srs_aac_raw_publish publish raw aac stream to SSR by srs-librtmp" @echo " srs_play play program using srs-librtmp" @echo " srs_ingest_flv ingest flv file and publish to RTMP server." @echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server." @@ -90,6 +91,9 @@ objs/srs_h264_raw_publish: srs_h264_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIB objs/srs_audio_raw_publish: srs_audio_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_audio_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_audio_raw_publish +objs/srs_aac_raw_publish: srs_aac_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) + $(GCC) srs_aac_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_aac_raw_publish + objs/srs_play: srs_play.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(GCC) srs_play.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(EXTRA_CXX_FLAG) -o objs/srs_play diff --git a/trunk/research/librtmp/srs_aac_raw_publish.c b/trunk/research/librtmp/srs_aac_raw_publish.c new file mode 100644 index 000000000..b1e951139 --- /dev/null +++ b/trunk/research/librtmp/srs_aac_raw_publish.c @@ -0,0 +1,198 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** +gcc srs_aac_raw_publish.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_aac_raw_publish +*/ + +#include +#include +#include + +// for open audio raw file. +#include +#include +#include + +#include "../../objs/include/srs_librtmp.h" + +// https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-63648892 +// allspace: +// Take this file as an example: https://github.com/allspace/files/blob/master/srs.pcm +// It's captured using SDK callback method. I have filtered out h264 video, so it's audio only now. +// For every frame, it's a 8 bytes vendor specific header, following 160 bytes audio frame. +// The header part can be ignored. +int read_audio_frame(char* data, int size, char** pp, char** frame, int* frame_size) +{ + char* p = *pp; + + // @remark, for this demo, to publish aac raw file to SRS, + // we search the adts frame from the buffer which cached the aac data. + // please get aac adts raw data from device, it always a encoded frame. + if (!srs_aac_is_adts(p, size - (p - data))) { + srs_human_trace("aac adts raw data invalid."); + return -1; + } + + // @see srs_audio_write_raw_frame + // each frame prefixed aac adts header, '1111 1111 1111'B, that is 0xFFF., + // for instance, frame = FF F1 5C 80 13 A0 FC 00 D0 33 83 E8 5B + *frame = p; + // skip some data. + // @remark, user donot need to do this. + p += srs_aac_adts_frame_size(p, size - (p - data)); + + *pp = p; + *frame_size = p - *frame; + if (*frame_size <= 0) { + srs_human_trace("aac adts raw data invalid."); + return -1; + } + + return 0; +} + +int main(int argc, char** argv) +{ + printf("publish raw audio as rtmp stream to server like FMLE/FFMPEG/Encoder\n"); + printf("SRS(simple-rtmp-server) client librtmp library.\n"); + printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision()); + + if (argc <= 2) { + printf("Usage: %s \n", argv[0]); + printf(" audio_raw_file: the audio raw steam file.\n"); + printf(" rtmp_publish_url: the rtmp publish url.\n"); + printf("For example:\n"); + printf(" %s ./audio.raw.aac rtmp://127.0.0.1:1935/live/livestream\n", argv[0]); + printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/audio.raw.aac\n"); + printf("See: https://github.com/winlinvip/simple-rtmp-server/issues/212\n"); + exit(-1); + } + + const char* raw_file = argv[1]; + const char* rtmp_url = argv[2]; + srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url); + + // open file + int raw_fd = open(raw_file, O_RDONLY); + if (raw_fd < 0) { + srs_human_trace("open audio raw file %s failed.", raw_fd); + goto rtmp_destroy; + } + + off_t file_size = lseek(raw_fd, 0, SEEK_END); + if (file_size <= 0) { + srs_human_trace("audio raw file %s empty.", raw_file); + goto rtmp_destroy; + } + srs_human_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024)); + + char* audio_raw = (char*)malloc(file_size); + if (!audio_raw) { + srs_human_trace("alloc raw buffer failed for file %s.", raw_file); + goto rtmp_destroy; + } + + lseek(raw_fd, 0, SEEK_SET); + ssize_t nb_read = 0; + if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) { + srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.", + raw_file, (int)(file_size / 1024), (int)(nb_read / 1024)); + goto rtmp_destroy; + } + + // connect rtmp context + srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url); + + if (srs_rtmp_handshake(rtmp) != 0) { + srs_human_trace("simple handshake failed."); + goto rtmp_destroy; + } + srs_human_trace("simple handshake success"); + + if (srs_rtmp_connect_app(rtmp) != 0) { + srs_human_trace("connect vhost/app failed."); + goto rtmp_destroy; + } + srs_human_trace("connect vhost/app success"); + + if (srs_rtmp_publish_stream(rtmp) != 0) { + srs_human_trace("publish stream failed."); + goto rtmp_destroy; + } + srs_human_trace("publish stream success"); + + u_int32_t timestamp = 0; + u_int32_t time_delta = 45; + // @remark, to decode the file. + char* p = audio_raw; + for (;p < audio_raw + file_size;) { + // @remark, read a frame from file buffer. + char* data = NULL; + int size = 0; + if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) { + srs_human_trace("read a frame from file buffer failed."); + goto rtmp_destroy; + } + + // 0 = Linear PCM, platform endian + // 1 = ADPCM + // 2 = MP3 + // 7 = G.711 A-law logarithmic PCM + // 8 = G.711 mu-law logarithmic PCM + // 10 = AAC + // 11 = Speex + char sound_format = 10; + // 2 = 22 kHz + char sound_rate = 2; + // 1 = 16-bit samples + char sound_size = 1; + // 1 = Stereo sound + char sound_type = 1; + + timestamp += time_delta; + + int ret = 0; + if ((ret = srs_audio_write_raw_frame(rtmp, + sound_format, sound_rate, sound_size, sound_type, + data, size, timestamp)) != 0 + ) { + srs_human_trace("send audio raw data failed. ret=%d", ret); + goto rtmp_destroy; + } + + srs_human_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d", + srs_human_flv_tag_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size, + sound_type); + + // @remark, when use encode device, it not need to sleep. + usleep(1000 * time_delta); + } + +rtmp_destroy: + srs_rtmp_destroy(rtmp); + close(raw_fd); + free(audio_raw); + + return 0; +} + diff --git a/trunk/research/librtmp/srs_audio_raw_publish.c b/trunk/research/librtmp/srs_audio_raw_publish.c index 9e11023ba..4c9c89914 100644 --- a/trunk/research/librtmp/srs_audio_raw_publish.c +++ b/trunk/research/librtmp/srs_audio_raw_publish.c @@ -166,7 +166,7 @@ int main(int argc, char** argv) if (srs_audio_write_raw_frame(rtmp, sound_format, sound_rate, sound_size, sound_type, - 0, data, size, timestamp) != 0 + data, size, timestamp) != 0 ) { srs_human_trace("send audio raw data failed."); goto rtmp_destroy; diff --git a/trunk/research/librtmp/srs_h264_raw_publish.c b/trunk/research/librtmp/srs_h264_raw_publish.c index 0b7bee1ad..d85da46d1 100644 --- a/trunk/research/librtmp/srs_h264_raw_publish.c +++ b/trunk/research/librtmp/srs_h264_raw_publish.c @@ -166,16 +166,16 @@ int main(int argc, char** argv) } // send out the h264 packet over RTMP - int error = srs_h264_write_raw_frames(rtmp, data, size, dts, pts); - if (error != 0) { - if (srs_h264_is_dvbsp_error(error)) { - srs_human_trace("ignore drop video error, code=%d", error); - } else if (srs_h264_is_duplicated_sps_error(error)) { - srs_human_trace("ignore duplicated sps, code=%d", error); - } else if (srs_h264_is_duplicated_pps_error(error)) { - srs_human_trace("ignore duplicated pps, code=%d", error); + int ret = srs_h264_write_raw_frames(rtmp, data, size, dts, pts); + if (ret != 0) { + if (srs_h264_is_dvbsp_error(ret)) { + srs_human_trace("ignore drop video error, code=%d", ret); + } else if (srs_h264_is_duplicated_sps_error(ret)) { + srs_human_trace("ignore duplicated sps, code=%d", ret); + } else if (srs_h264_is_duplicated_pps_error(ret)) { + srs_human_trace("ignore duplicated pps, code=%d", ret); } else { - srs_human_trace("send h264 raw data failed."); + srs_human_trace("send h264 raw data failed. ret=%d", ret); goto rtmp_destroy; } } diff --git a/trunk/src/app/srs_app_avc_aac.cpp b/trunk/src/app/srs_app_avc_aac.cpp index 182da61ae..7c5c5cb45 100644 --- a/trunk/src/app/srs_app_avc_aac.cpp +++ b/trunk/src/app/srs_app_avc_aac.cpp @@ -249,9 +249,6 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample return ret; } - // aac_profile = audioObjectType - 1 - aac_profile--; - // TODO: FIXME: to support aac he/he-v2, see: ngx_rtmp_codec_parse_aac_header // @see: https://github.com/winlinvip/nginx-rtmp-module/commit/3a5f9eea78fc8d11e8be922aea9ac349b9dcbfc2 // diff --git a/trunk/src/app/srs_app_avc_aac.hpp b/trunk/src/app/srs_app_avc_aac.hpp index 96b13e6f4..24a83b8d8 100644 --- a/trunk/src/app/srs_app_avc_aac.hpp +++ b/trunk/src/app/srs_app_avc_aac.hpp @@ -38,25 +38,6 @@ class SrsAmf0Object; #define __SRS_SRS_MAX_CODEC_SAMPLE 128 #define __SRS_AAC_SAMPLE_RATE_UNSET 15 -/** -* the FLV/RTMP supported audio sample rate. -* Sampling rate. The following values are defined: -* 0 = 5.5 kHz = 5512 Hz -* 1 = 11 kHz = 11025 Hz -* 2 = 22 kHz = 22050 Hz -* 3 = 44 kHz = 44100 Hz -*/ -enum SrsCodecAudioSampleRate -{ - // set to the max value to reserved, for array map. - SrsCodecAudioSampleRateReserved = 4, - - SrsCodecAudioSampleRate5512 = 0, - SrsCodecAudioSampleRate11025 = 1, - SrsCodecAudioSampleRate22050 = 2, - SrsCodecAudioSampleRate44100 = 3, -}; - /** * the FLV/RTMP supported audio sample size. * Size of each audio sample. This parameter only pertains to @@ -224,8 +205,9 @@ public: public: /** * audio specified - * 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. + * audioObjectType, in 1.6.2.1 AudioSpecificConfig, page 33, + * 1.5.1.1 Audio object type definition, page 23, + * in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf. */ u_int8_t aac_profile; /** diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b988a1fbb..b8b947437 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 30 +#define VERSION_REVISION 31 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index 32b019bd5..507fc0b55 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -144,6 +144,25 @@ enum SrsCodecAudio SrsCodecAudioReservedDeviceSpecificSound = 15, }; +/** +* the FLV/RTMP supported audio sample rate. +* Sampling rate. The following values are defined: +* 0 = 5.5 kHz = 5512 Hz +* 1 = 11 kHz = 11025 Hz +* 2 = 22 kHz = 22050 Hz +* 3 = 44 kHz = 44100 Hz +*/ +enum SrsCodecAudioSampleRate +{ + // set to the max value to reserved, for array map. + SrsCodecAudioSampleRateReserved = 4, + + SrsCodecAudioSampleRate5512 = 0, + SrsCodecAudioSampleRate11025 = 1, + SrsCodecAudioSampleRate22050 = 2, + SrsCodecAudioSampleRate44100 = 3, +}; + /** * Annex E. The FLV File Format * @see SrsAvcAacCodec for the media stream codec. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f76f8129a..14e94378b 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -189,6 +189,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_H264_DROP_BEFORE_SPS_PPS 3043 #define ERROR_H264_DUPLICATED_SPS 3044 #define ERROR_H264_DUPLICATED_PPS 3045 +#define ERROR_AAC_REQUIRED_ADTS 3046 +#define ERROR_AAC_ADTS_HEADER 3047 +#define ERROR_AAC_DATA_INVALID 3048 /** * whether the error code is an system control error. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 362963d57..b6f415567 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -75,7 +75,7 @@ struct Context int stream_id; // for h264 raw stream, - // see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 SrsStream h264_raw_stream; // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 std::string h264_sps; @@ -87,6 +87,11 @@ struct Context // @see https://github.com/winlinvip/simple-rtmp-server/issues/204 bool h264_sps_changed; bool h264_pps_changed; + // for aac raw stream, + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146250 + SrsStream aac_raw_stream; + // the aac sequence header. + std::string aac_specific_config; Context() { rtmp = NULL; @@ -859,22 +864,18 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* } /** -* write audio raw frame to SRS. +* directly write a audio frame. */ -int srs_audio_write_raw_frame(srs_rtmp_t rtmp, +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 ) { - Context* context = (Context*)rtmp; - srs_assert(context); - - // TODO: FIXME: for aac, must send the sequence header first. // for audio frame, there is 1 or 2 bytes header: // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType - // 1bytes, AACPacketType for SoundFormat == 10 + // 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header. int size = frame_size + 1; - if (aac_packet_type == SrsCodecAudioAAC) { + if (sound_format == SrsCodecAudioAAC) { size += 1; } char* data = new char[size]; @@ -887,7 +888,7 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, *p++ = audio_header; - if (aac_packet_type == SrsCodecAudioAAC) { + if (sound_format == SrsCodecAudioAAC) { *p++ = aac_packet_type; } @@ -896,6 +897,278 @@ int srs_audio_write_raw_frame(srs_rtmp_t rtmp, 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 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) & 0x70; + // 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 + context->aac_specific_config += ch; + + if ((ret = __srs_write_audio_raw_frame(context, + sound_format, sound_rate, sound_size, sound_type, + 0, (char*)context->aac_specific_config.data(), + context->aac_specific_config.length(), + 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); +} + +/** +* write aac frames in adts. +*/ +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 +) { + int ret = ERROR_SUCCESS; + + SrsStream* stream = &context->aac_raw_stream; + if ((ret = stream->initialize(frame, frame_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* raw_data = stream->data() + stream->pos(); + if ((ret = __srs_write_aac_adts_frame(context, + sound_format, sound_rate, sound_size, sound_type, + fh_Profile_ObjectType, fh_sampling_frequency_index, fh_channel_configuration, + raw_data, raw_data_size, timestamp)) != ERROR_SUCCESS + ) { + return ret; + } + stream->skip(raw_data_size); + } + + return ret; +} + +/** +* write audio raw frame to SRS. +*/ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char* frame, int frame_size, u_int32_t timestamp +) { + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + srs_assert(context); + + if (sound_format == SrsCodecAudioAAC) { + // for aac, the frame must be ADTS format. + if (!srs_aac_is_adts(frame, frame_size)) { + return ERROR_AAC_REQUIRED_ADTS; + } + + // for aac, demux the ADTS to RTMP format. + return __srs_write_aac_adts_frames(context, + sound_format, sound_rate, sound_size, sound_type, + frame, frame_size, timestamp); + } else { + // 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 ret; +} + +/** +* whether aac raw data is in adts format, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +*/ +srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) +{ + SrsStream stream; + if (stream.initialize(aac_raw_data, ac_raw_size) != ERROR_SUCCESS) { + return false; + } + + return srs_aac_startswith_adts(&stream); +} + +/** +* parse the adts header to get the frame size. +*/ +int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) +{ + int size = -1; + + if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { + return size; + } + + // adts always 7bytes. + if (ac_raw_size <= 7) { + return size; + } + + // last 2bits + int16_t ch3 = aac_raw_data[3]; + // whole 8bits + int16_t ch4 = aac_raw_data[4]; + // first 3bits + int16_t ch5 = aac_raw_data[5]; + + size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); + + return size; +} + /** * write h264 packet, with rtmp header. * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. @@ -1224,22 +1497,22 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, return error_code_return; } -srs_h264_bool srs_h264_is_dvbsp_error(int error_code) +srs_bool srs_h264_is_dvbsp_error(int error_code) { return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; } -srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code) +srs_bool srs_h264_is_duplicated_sps_error(int error_code) { return error_code == ERROR_H264_DUPLICATED_SPS; } -srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code) +srs_bool srs_h264_is_duplicated_pps_error(int error_code) { return error_code == ERROR_H264_DUPLICATED_PPS; } -int srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) +srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) { SrsStream stream; if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) { @@ -1417,17 +1690,17 @@ void srs_flv_lseek(srs_flv_t flv, int64_t offset) context->reader.lseek(offset); } -srs_flv_bool srs_flv_is_eof(int error_code) +srs_bool srs_flv_is_eof(int error_code) { return error_code == ERROR_SYSTEM_FILE_EOF; } -srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size) +srs_bool srs_flv_is_sequence_header(char* data, int32_t size) { return SrsFlvCodec::video_is_sequence_header(data, (int)size); } -srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size) +srs_bool srs_flv_is_keyframe(char* data, int32_t size) { return SrsFlvCodec::video_is_keyframe(data, (int)size); } @@ -1517,43 +1790,43 @@ int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) return ret; } -srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0) +srs_bool srs_amf0_is_string(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_string(); } -srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0) +srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_boolean(); } -srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0) +srs_bool srs_amf0_is_number(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_number(); } -srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0) +srs_bool srs_amf0_is_null(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_null(); } -srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0) +srs_bool srs_amf0_is_object(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_object(); } -srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) +srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_ecma_array(); } -srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->is_strict_array(); @@ -1565,7 +1838,7 @@ const char* srs_amf0_to_string(srs_amf0_t amf0) return any->to_str_raw(); } -srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0) +srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; return any->to_boolean(); diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 371a2ca83..b941219dd 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -84,6 +84,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extern "C"{ #endif +// typedefs +typedef int srs_bool; + /************************************************************* ************************************************************** * srs-librtmp version @@ -303,12 +306,15 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, * @param sound_type Mono or stereo sound * 0 = Mono sound * 1 = Stereo sound -* @param aac_packet_type The following values are defined: -* 0 = AAC sequence header -* 1 = AAC raw * @param timestamp The timestamp of audio. * -* @remark Ignore aac_packet_type if not aac(sound_format!=10). +* @example /trunk/research/librtmp/srs_aac_raw_publish.c +* @example /trunk/research/librtmp/srs_audio_raw_publish.c +* +* @remark for aac, the frame must be in ADTS format. +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS +* @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23, 1.5.1.1 Audio object type * * @see https://github.com/winlinvip/simple-rtmp-server/issues/212 * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf @@ -317,15 +323,38 @@ extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, */ extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, 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, u_int32_t timestamp ); +/** +* whether aac raw data is in adts format, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +* @param aac_raw_data the input aac raw data, a encoded aac frame data. +* @param ac_raw_size the size of aac raw data. +* +* @reamrk used to check whether current frame is in adts format. +* @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS +* @example /trunk/research/librtmp/srs_aac_raw_publish.c +* +* @return 0 false; otherwise, true. +*/ +extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); + +/** +* parse the adts header to get the frame size, +* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. +* @param aac_raw_data the input aac raw data, a encoded aac frame data. +* @param ac_raw_size the size of aac raw data. +* +* @return failed when <=0 failed; otherwise, ok. +*/ +extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); + /************************************************************* ************************************************************** * h264 raw codec ************************************************************** *************************************************************/ -typedef int srs_h264_bool; /** * write h.264 raw frame over RTMP to rtmp server. * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. @@ -392,21 +421,21 @@ extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), * this will cause SRS server to disable HLS. */ -extern srs_h264_bool srs_h264_is_dvbsp_error(int error_code); +extern srs_bool srs_h264_is_dvbsp_error(int error_code); /** * whether error_code is duplicated sps error. * * @see https://github.com/winlinvip/simple-rtmp-server/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ -extern srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code); +extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); /** * whether error_code is duplicated pps error. * * @see https://github.com/winlinvip/simple-rtmp-server/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ -extern srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code); +extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); /** * whether h264 raw data starts with the annexb, * which bytes sequence matches N[00] 00 00 01, where N>=0. @@ -420,7 +449,7 @@ extern srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code); * * @return 0 false; otherwise, true. */ -extern int srs_h264_startswith_annexb( +extern srs_bool srs_h264_startswith_annexb( char* h264_raw_data, int h264_raw_size, int* pnb_start_code ); @@ -435,7 +464,6 @@ extern int srs_h264_startswith_annexb( ************************************************************** *************************************************************/ typedef void* srs_flv_t; -typedef int srs_flv_bool; /* open flv file for both read/write. */ extern srs_flv_t srs_flv_open_read(const char* file); extern srs_flv_t srs_flv_open_write(const char* file); @@ -510,20 +538,20 @@ extern int64_t srs_flv_tellg(srs_flv_t flv); extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); /* error code */ /* whether the error code indicates EOF */ -extern srs_flv_bool srs_flv_is_eof(int error_code); +extern srs_bool srs_flv_is_eof(int error_code); /* media codec */ /** * whether the video body is sequence header * @param data, the data of tag, read by srs_flv_read_tag_data(). * @param size, the size of tag, read by srs_flv_read_tag_data(). */ -extern srs_flv_bool srs_flv_is_sequence_header(char* data, int32_t size); +extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); /** * whether the video body is keyframe * @param data, the data of tag, read by srs_flv_read_tag_data(). * @param size, the size of tag, read by srs_flv_read_tag_data(). */ -extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); +extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); /************************************************************* ************************************************************** @@ -534,7 +562,6 @@ extern srs_flv_bool srs_flv_is_keyframe(char* data, int32_t size); *************************************************************/ /* the output handler. */ typedef void* srs_amf0_t; -typedef int srs_amf0_bool; typedef double srs_amf0_number; /** * parse amf0 from data. @@ -552,16 +579,16 @@ extern void srs_amf0_free_bytes(char* data); extern int srs_amf0_size(srs_amf0_t amf0); extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); /* type detecter */ -extern srs_amf0_bool srs_amf0_is_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_boolean(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_number(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_null(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_object(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); /* value converter */ extern const char* srs_amf0_to_string(srs_amf0_t amf0); -extern srs_amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); /* value setter */ extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); diff --git a/trunk/src/rtmp/srs_protocol_utility.cpp b/trunk/src/rtmp/srs_protocol_utility.cpp index a4ee30e56..b79e45829 100644 --- a/trunk/src/rtmp/srs_protocol_utility.cpp +++ b/trunk/src/rtmp/srs_protocol_utility.cpp @@ -167,12 +167,12 @@ bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) } // not match - if (p[0] != 0x00 || p[1] != 0x00) { + if (p[0] != (char)0x00 || p[1] != (char)0x00) { return false; } // match N[00] 00 00 01, where N>=0 - if (p[2] == 0x01) { + if (p[2] == (char)0x01) { if (pnb_start_code) { *pnb_start_code = (int)(p - bytes) + 3; } @@ -185,3 +185,21 @@ bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code) return false; } +bool srs_aac_startswith_adts(SrsStream* stream) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; + + if (!stream->require(p - bytes + 2)) { + return false; + } + + // matched 12bits 0xFFF, + // @remark, we must cast the 0xff to char to compare. + if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) { + return false; + } + + return true; +} + diff --git a/trunk/src/rtmp/srs_protocol_utility.hpp b/trunk/src/rtmp/srs_protocol_utility.hpp index b4293ab10..2ec909133 100644 --- a/trunk/src/rtmp/srs_protocol_utility.hpp +++ b/trunk/src/rtmp/srs_protocol_utility.hpp @@ -96,5 +96,12 @@ extern bool srs_bytes_equals(void* pa, void* pb, int size); */ extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL); +/** +* whether stream starts with the aac ADTS +* from aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS. +* start code must be '1111 1111 1111'B, that is 0xFFF +*/ +extern bool srs_aac_startswith_adts(SrsStream* stream); + #endif diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index e45a4738c..94f0fd339 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -130,6 +130,7 @@ file ..\utest\srs_utest_reload.hpp, ..\utest\srs_utest_reload.cpp, research readonly separator, + ..\..\research\librtmp\srs_aac_raw_publish.c, ..\..\research\librtmp\srs_audio_raw_publish.c, ..\..\research\librtmp\srs_bandwidth_check.c, ..\..\research\librtmp\srs_detect_rtmp.c,