关于librtmp接收数据,过程比较简单,但是细节还是比较麻烦;
简单的librtmp接收数据,可以参考最简单的基于librtmp的示例:接收(RTMP保存为FLV)
=====================================================
最简单的基于libRTMP的示例系列文章列表:
最简单的基于librtmp的示例:接收(RTMP保存为FLV)
最简单的基于librtmp的示例:发布(FLV通过RTMP发布)
最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)
=====================================================
本文记录一个基于libRTMP的接收流媒体的程序:Simplest libRTMP Receive。该程序可以将RTMP流保存成本地FLV文件。实际上本文记录的程序就是一个“精简”过的RTMPDump。RTMPDump功能比较多,因而其代码比较复杂导致很多初学者不知从何下手。而本文记录的这个程序只保留了RTMPDump中最核心的函数,更加方便新手入门学习libRTMP。
程序运行后,会将URL为“rtmp://live.hkstv.hk.lxdns.com/live/hks”的直播流(实际上是香港卫视)在本地保存为“receive.flv”。保存后的文件使用播放器就可以观看。
项目主页
SourceForge:https://sourceforge.net/projects/simplestlibrtmpexample/
Github:https://github.com/leixiaohua1020/simplest_librtmp_example
开源中国:http://git.oschina.net/leixiaohua1020/simplest_librtmp_example
上述内容 比较简明;
如果想通过librtmp自己分开接收音视频数据,要怎么处理呢?
视频接收:
可以参考 http://www.programering.com/a/MTOyQzNwATc.html
ADTS全称是(Audio Data Transport Stream),是AAC的一种十分常见的传输格式。
记得第一次做demux的时候,把AAC音频的ES流从FLV封装格式中抽出来送给硬件解码器时,不能播;保存到本地用pc的播放器播时,我靠也不能播。当时崩溃了,后来通过查找资料才知道。一般的AAC解码器都需要把AAC的ES流打包成ADTS的格式,一般是在AAC ES流前添加7个字节的ADTS header。也就是说你可以吧ADTS这个头看作是AAC的frameheader。
ADTS AAC
|
||||||
ADTS_header | AAC ES | ADTS_header | AAC ES |
...
|
ADTS_header | AAC ES |
ADTS 头中相对有用的信息 采样率、声道数、帧长度。想想也是,我要是解码器的话,你给我一堆得AAC音频ES流我也解不出来。每一个带ADTS头信息的AAC流会清晰的告送解码器他需要的这些信息。
一般情况下ADTS的头信息都是7个字节,分为2部分:
adts_fixed_header();
adts_variable_header();
syncword :同步头 总是0xFFF, all bits must be 1,代表着一个ADTS帧的开始
ID:MPEG Version: 0 for MPEG-4, 1 for MPEG-2
Layer:always: '00'
profile:表示使用哪个级别的AAC,有些芯片只支持AAC LC 。在MPEG-2 AAC中定义了3种:
sampling_frequency_index:表示使用的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。
There are 13 supported frequencies:
frame_length : 一个ADTS帧的长度包括ADTS头和AAC原始流.
adts_buffer_fullness:0x7FF 说明是码率可变的码流
如果是通过嵌入式高清解码芯片做产品的话,一般情况的解码工作都是由硬件来完成的。所以大部分的工作是把AAC原始流打包成ADTS的格式,然后丢给硬件就行了。
通过对ADTS格式的了解,很容易就能把AAC打包成ADTS。我们只需得到封装格式里面关于音频采样率、声道数、元数据长度、aac格式类型等信息。然后在每个AAC原始流前面加上个ADTS头就OK了。
贴上ffmpeg中添加ADTS头的代码,就可以很清晰的了解ADTS头的结构:
想做什么:使用 Android 的 MediaCodec 将编码到原始的 AAC 文件的原始 PCM 音频采样。
的问题:当我使用 FFMPEG 生成原始的 AAC 文件打包到 M4A 容器,FFMPEG 抱怨缺少的编解码器参数文件中。
详细信息:
由于生成的输出 AAC 文件的音频编码器为找不到任何 MediaCodec 的示例代码,我试着修改到音频编码器的视频编码器。这里的原始代码是: source_code
我配置的音频编码器,像这样:
mEncoderFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", (int)mAudioSampleRate, 2);
// redundant?
mEncoderFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
mEncoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectELD);
mEncoderFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, kSampleRates);
mEncoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates);
mEncoderFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
testEncoderWithFormat("audio/mp4a-latm", mEncoderFormat);
try {
codec.configure(
mEncoderFormat,
null /* surface */,
null /* crypto */,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IllegalStateException e) {
Log.e(TAG, "codec '" + componentName + "' failed configuration.");
return;
}
Log.d(TAG, " testEncoder configured with format = " + format);
然后我喂编码器与价值的 PCM 样品每帧 10ms年。编码器需要每个帧,将生成的位流,一个框架和 FileOutputStream 位流写入。循环继续直到输入文件的末尾。
代码运行到终点。我做 '亚行拉' 生成的 AAC 文件从设备获取到我的 PC,并使用 FFMPEG 来阅读它。下面是命令和 FFMPEG 吐出的错误:
$ ffmpeg -f aac -i BlessedNoColor_nexus7_api18.aac
ffmpeg version N-45739-g04bf2e7 Copyright (c) 2000-2012 the FFmpeg developers
built on Oct 20 2012 00:20:36 with gcc 4.7.2 (GCC)
configuration: --enable-gpl --enable-version3 --disable-pthreads --enable-runt
ime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libass -
-enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enab
le-libgsm --enable-libmp3lame --enable-libnut --enable-libopenjpeg --enable-libo
pus --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libtheo
ra --enable-libutvideo --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-li
bvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --ena
ble-zlib
libavutil 51. 76.100 / 51. 76.100
libavcodec 54. 67.100 / 54. 67.100
libavformat 54. 33.100 / 54. 33.100
libavdevice 54. 3.100 / 54. 3.100
libavfilter 3. 19.103 / 3. 19.103
libswscale 2. 1.101 / 2. 1.101
libswresample 0. 16.100 / 0. 16.100
libpostproc 52. 1.100 / 52. 1.100
[aac @ 00000000002efae0] channel element 2.0 is not allocated
[aac @ 00000000003cf520] decoding for stream 0 failed
[aac @ 00000000003cf520] Could not find codec parameters for stream 0 (Audio: aac, 0 channels, s16): unspecified sample rate
Consider increasing the value for the 'analyzeduration' and 'probesize' options
[aac @ 00000000003cf520] Estimating duration from bitrate, this may be inaccurate
BlessedNoColor_nexus7_api18.aac: could not find codec parameters
我的问题:
任何可以帮助将深为感激。如果有一个示例项目,不会想在这里做什么,那就再好不过。如果我的源代码可以帮你帮助我,我就把它寄。我需要做一些清理。谢谢 !
编辑: 从"初级 AAC 文件由 MediaCodec 缺少的编解码器的参数生成"更改标题为"如何生成 AAC ADTS 基本流与 Android MediaCodec"
我最后生成的 android 系统的设备和 Windows 主机计算机上可播放的 AAC 文件。我张贴我的解决方案在这里,希望它能帮助其他人。
首先,Android MediaCodec 编码器生成基本的 AAC 流我先前假设不是准确的。MediaCodec 编码器生成的原始的 AAC 流。这就是为什么无法播放文件。原始的 AAC 流需要转换成一个可播放格式,例如ADTS流。我已更改以反映我的新认识这篇文章的标题。有另一篇文章,问了一个类似的问题,并有一个优秀的答案。然而,一个新手未必了解那里的简要说明。我相当没我阅读这篇文章的第一次。
所以,为了生成 AAC 码流可以发挥的一个媒体播放器,我开始给出的 EncoderTest 示例由法登在他第一次的意见,但修改的原代码添加 ADTS 头每输出帧 (访问单位),并将结果流写入到一个文件中 (更换线路通过 267 248 的原代码与下面的代码段):
if (index >= 0) {
int outBitsSize = info.size;
int outPacketSize = outBitsSize + 7; // 7 is ADTS size
ByteBuffer outBuf = codecOutputBuffers[index];
outBuf.position(info.offset);
outBuf.limit(info.offset + outBitsSize);
try {
byte[] data = new byte[outPacketSize]; //space for ADTS header included
addADTStoPacket(data, outPacketSize);
outBuf.get(data, 7, outBitsSize);
outBuf.position(info.offset);
mFileStream.write(data, 0, outPacketSize); //open FileOutputStream beforehand
} catch (IOException e) {
Log.e(TAG, "failed writing bitstream data to file");
e.printStackTrace();
}
numBytesDequeued += info.size;
outBuf.clear();
codec.releaseOutputBuffer(index, false /* render */);
Log.d(TAG, " dequeued " + outBitsSize + " bytes of output data.");
Log.d(TAG, " wrote " + outPacketSize + " bytes into output file.");
}
else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
}
else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
codecOutputBuffers = codec.getOutputBuffers();
}
在循环外我定义函数 addADTStoPacket 像这样:
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
**/
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = 2; //CPE
// fill in ADTS data
packet[0] = (byte)0xFF;
packet[1] = (byte)0xF9;
packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
packet[4] = (byte)((packetLen&0x7FF) >> 3);
packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
packet[6] = (byte)0xFC;
}
我还添加了代码来控制如何停止生成 AAC ADTS 流,但这是应用程序特定的所以我在这里不会详细说明。所有这些变化,生成的 AAC 文件可以在 Android 设备上,在我 Windows PC 上,演奏和 ffmpeg 是和他们一起快乐。
本文引用了下面几个网友的文章:
http://sun3eyes.blog.163.com/blog/#m=0&t=3&c=rtmp
http://sun3eyes.blog.163.com/blog/static/1070797922012913337667/
http://sun3eyes.blog.163.com/blog/static/107079792201291112451996/
http://blog.csdn.net/helunlixing/article/details/7417778
直播的视频用H264,音频用AAC,从FAAC里面压缩出来的一帧音频数据,要经过简单处理才能打包用RTMP协议发送到FMS上,包括保存成FLV文件,都要稍微处理一下,主要是把AAC的帧头去掉,并提取出相应的信息。
1024字节的G.711A数据,AAC一般也就300多个字节。
可以把FAAC压缩出来的帧直接保存成AAC文件,用windows7自带的播放器可以播放的,方便测试。
AAC的帧头一般7个字节,或者包含CRC校验的话9个字节,这里面包括了声音的相关参数。
结构如下:
Structure
AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
Header consists of 7 or 9 bytes (without or with CRC).
Letter | Length (bits) | Description |
---|---|---|
A | 12 | syncword 0xFFF, all bits must be 1 |
B | 1 | MPEG Version: 0 for MPEG-4, 1 for MPEG-2 |
C | 2 | Layer: always 0 |
D | 1 | protection absent, Warning, set to 1 if there is no CRC and 0 if there is CRC |
E | 2 | profile, the MPEG-4 Audio Object Type minus 1 |
F | 4 | MPEG-4 Sampling Frequency Index (15 is forbidden) |
G | 1 | private stream, set to 0 when encoding, ignore when decoding |
H | 3 | MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE) |
I | 1 | originality, set to 0 when encoding, ignore when decoding |
J | 1 | home, set to 0 when encoding, ignore when decoding |
K | 1 | copyrighted stream, set to 0 when encoding, ignore when decoding |
L | 1 | copyright start, set to 0 when encoding, ignore when decoding |
M | 13 | frame length, this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) |
O | 11 | Buffer fullness |
P | 2 | Number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame |
Q | 16 | CRC if protection absent is 0 |
http://wiki.multimedia.cx/index.php?title=ADTS
其中最重要的就是E,F,H。
E就是类型了
1: AAC Main
2: AAC LC (Low Complexity)
3: AAC SSR (Scalable Sample Rate)
4: AAC LTP (Long Term Prediction)
F就是采样频率
0: 96000 Hz
H就是声道
1: 1 channel: front-center
有了这三个参数,就可以发送音频的第一帧了,然后后面的帧,把包头的7个字节去掉?,打包到RTMP协议发送就可以了。
http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Object_Types
当然发送的时候要打上RTMP的包头,数据部分用 AF 00 代替AAC的包头。长度再计算一下。时间戳用采样的时间也可以,自己另算也可以。
第一个音频包那就是AAC header.
如:af 00 13 90。包长4个字节,解释如下,
1)第一个字节af,a就是10代表的意思是AAC,
Format of SoundData. The following values are defined:
0 = Linear PCM, platform endian
1 = ADPCM
2 = MP3
3 = Linear PCM, little endian
4 = Nellymoser 16 kHz mono
5 = Nellymoser 8 kHz mono
6 = Nellymoser
7 = G.711 A-law logarithmic PCM
8 = G.711 mu-law logarithmic PCM
9 = reserved
10 = AAC
11 = Speex
14 = MP3 8 kHz
15 = Device-specific sound
Formats 7, 8, 14, and 15 are reserved.
AAC is supported in Flash Player 9,0,115,0 and higher.
Speex is supported in Flash Player 10 and higher.
2)第一个字节中的后四位f代表如下
前2个bit的含义 抽样频率,这里是二进制11,代表44kHZ
Sampling rate. The following values are defined:
0 = 5.5 kHz
1 = 11 kHz
2 = 22 kHz
3 = 44 kHz
第3个bit,代表 音频用16位的
Size of each audio sample. This parameter only pertains to
uncompressed formats. Compressed formats always decode
to 16 bits internally.
0 = 8-bit samples
1 = 16-bit samples
第4个bit代表声道
Mono or stereo sound
0 = Mono sound
1 = Stereo sound
3)第2个字节
AACPacketType,这个字段来表示AACAUDIODATA的类型:0 = AAC sequence header,1 = AAC raw。第一个音频包用0,后面的都用1
4)第3,4个字节内容AudioSpecificConfig如下
AAC sequence header存放的是AudioSpecificConfig结构,该结构则在“ISO-14496-3 Audio”中描述。AudioSpecificConfig结构的描述非常复杂,这里我做一下简化,事先设定要将要编码的音频格式,其中,选择"AAC-LC"为音频编码,音频采样率为44100,于是AudioSpecificConfig简化为下表:
0x13 0x90(1001110010000) 表示 ObjectProfile=2, AAC-LC,SamplingFrequencyIndex=7,ChannelConfiguration=声道2
AudioSpecificConfig,即为ObjectProfile,SamplingFrequencyIndex,ChannelConfiguration,TFSpecificConfig。
其中,ObjectProfile (AAC main ~1, AAC lc ~2, AAC ssr ~3);
SamplingFrequencyIndex (0 ~ 96000, 1~88200, 2~64000, 3~48000, 4~44100, 5~32000, 6~24000, 7~ 22050, 8~16000...),通常aac固定选中44100,即应该对应为4,但是试验结果表明,当音频采样率小于等于44100时,应该选择3,而当音频采样率为48000时,应该选择2;
ChannelConfiguration对应的是音频的频道数目。单声道对应1,双声道对应2,依次类推。
TFSpecificConfig的说明见标准14496-3中(1.2 T/F Audio Specific Configuration)的讲解,这里恒定的设置为0;
索引值如下含义:
There are 13 supported frequencies:
channel_configuration: 表示声道数
后面的视频包都是AF 01 + 去除7个字节头的音频AAC数据