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,