关于librtmp接收数据

来源:互联网 发布:单片机进阶书籍 编辑:程序博客网 时间:2024/06/06 17:41

关于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。

 

流程图

使用librtmp接收RTMP流的函数执行流程图如下图所示。

流程图中关键函数的作用如下所列:
InitSockets():初始化Socket
RTMP_Alloc():为结构体“RTMP”分配内存。
RTMP_Init():初始化结构体“RTMP”中的成员变量。
RTMP_SetupURL():设置输入的RTMP连接的URL。
RTMP_Connect():建立RTMP连接,创建一个RTMP协议规范中的NetConnection。
RTMP_ConnectStream():创建一个RTMP协议规范中的NetStream。
RTMP_Read():从服务器读取数据。
RTMP_Close():关闭RTMP连接。
RTMP_Free():释放结构体“RTMP”。
CleanupSockets():关闭Socket。
 
其中NetStream和NetConnection是RTMP协议规范中的两个逻辑结构。NetStream建立在NetConnection之上。一个NetConnection可以包含多个NetStream。它们之间的关系如下图所示。

 
 

源代码

[cpp] view plaincopy
  1. /** 
  2.  * Simplest Librtmp Receive 
  3.  * 
  4.  * 雷霄骅,张晖 
  5.  * leixiaohua1020@126.com 
  6.  * zhanghuicuc@gmail.com 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 本程序用于接收RTMP流媒体并在本地保存成FLV格式的文件。 
  12.  * This program can receive rtmp live stream and save it as local flv file. 
  13.  */  
  14. #include <stdio.h>  
  15. #include "librtmp/rtmp_sys.h"  
  16. #include "librtmp/log.h"  
  17.   
  18. int InitSockets()  
  19. {  
  20.     WORD version;  
  21.     WSADATA wsaData;  
  22.     version = MAKEWORD(1, 1);  
  23.     return (WSAStartup(version, &wsaData) == 0);  
  24. }  
  25.   
  26. void CleanupSockets()  
  27. {  
  28.     WSACleanup();  
  29. }  
  30.   
  31. int main(int argc, char* argv[])  
  32. {  
  33.     InitSockets();  
  34.       
  35.     double duration=-1;  
  36.     int nRead;  
  37.     //is live stream ?  
  38.     bool bLiveStream=true;                
  39.       
  40.       
  41.     int bufsize=1024*1024*10;             
  42.     char *buf=(char*)malloc(bufsize);  
  43.     memset(buf,0,bufsize);  
  44.     long countbufsize=0;  
  45.       
  46.     FILE *fp=fopen("receive.flv","wb");  
  47.     if (!fp){  
  48.         RTMP_LogPrintf("Open File Error.\n");  
  49.         CleanupSockets();  
  50.         return -1;  
  51.     }  
  52.       
  53.     /* set log level */  
  54.     //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;  
  55.     //RTMP_LogSetLevel(loglvl);  
  56.   
  57.     RTMP *rtmp=RTMP_Alloc();  
  58.     RTMP_Init(rtmp);  
  59.     //set connection timeout,default 30s  
  60.     rtmp->Link.timeout=10;     
  61.     // HKS's live URL  
  62.     if(!RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks"))  
  63.     {  
  64.         RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");  
  65.         RTMP_Free(rtmp);  
  66.         CleanupSockets();  
  67.         return -1;  
  68.     }  
  69.     if (bLiveStream){  
  70.         rtmp->Link.lFlags|=RTMP_LF_LIVE;  
  71.     }  
  72.       
  73.     //1hour  
  74.     RTMP_SetBufferMS(rtmp, 3600*1000);        
  75.       
  76.     if(!RTMP_Connect(rtmp,NULL)){  
  77.         RTMP_Log(RTMP_LOGERROR,"Connect Err\n");  
  78.         RTMP_Free(rtmp);  
  79.         CleanupSockets();  
  80.         return -1;  
  81.     }  
  82.   
  83.     if(!RTMP_ConnectStream(rtmp,0)){  
  84.         RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");  
  85.         RTMP_Close(rtmp);  
  86.         RTMP_Free(rtmp);  
  87.         CleanupSockets();  
  88.         return -1;  
  89.     }  
  90.   
  91.     while(nRead=RTMP_Read(rtmp,buf,bufsize)){  
  92.         fwrite(buf,1,nRead,fp);  
  93.   
  94.         countbufsize+=nRead;  
  95.         RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n",nRead,countbufsize*1.0/1024);  
  96.     }  
  97.   
  98.     if(fp)  
  99.         fclose(fp);  
  100.   
  101.     if(buf){  
  102.         free(buf);  
  103.     }  
  104.   
  105.     if(rtmp){  
  106.         RTMP_Close(rtmp);  
  107.         RTMP_Free(rtmp);  
  108.         CleanupSockets();  
  109.         rtmp=NULL;  
  110.     }     
  111.     return 0;  
  112. }  


运行结果 

程序运行后,会将URL为“rtmp://live.hkstv.hk.lxdns.com/live/hks”的直播流(实际上是香港卫视)在本地保存为“receive.flv”。保存后的文件使用播放器就可以观看。


下载


Simplest LibRTMP Example
 

项目主页

SourceForge:https://sourceforge.net/projects/simplestlibrtmpexample/

Github:https://github.com/leixiaohua1020/simplest_librtmp_example

开源中国:http://git.oschina.net/leixiaohua1020/simplest_librtmp_example


CSDN下载:http://download.csdn.net/detail/leixiaohua1020/8291757
 
本工程包含了LibRTMP的使用示例,包含如下子工程:
simplest_librtmp_receive: 接收RTMP流媒体并在本地保存成FLV格式的文件。
simplest_librtmp_send_flv: 将FLV格式的视音频文件使用RTMP推送至RTMP流媒体服务器。
simplest_librtmp_send264: 将内存中的H.264数据推送至RTMP流媒体服务器。


上述内容  比较简明;







如果想通过librtmp自己分开接收音视频数据,要怎么处理呢?

视频接收:

可以参考 http://www.programering.com/a/MTOyQzNwATc.html


The H264 bitstream extraction to receive the flv stream in librtmp: according to

Rtmpdump can download the RTMP flow and save as flv file. 
If the audio or video convection in a separate processing, to extract respectively based on flv protocol. 
Simple modifications to the rtmpdump code, to increase the corresponding function. 
1 audio extraction:
Rtmpdump download cycle in the Download function:
....
 do
 {
....
nRead = RTMP_Read(rtmp, buffer, bufferSize);
....
}while(!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp));
....

The original program is written documents received, generate flv. 
Now, are extracted from the audio and video before writing, audio extraction is simple, direct analysis of the buffer (Reference method for the RTMP_Write function).
Note that, rtmpdump is used in the RTMP_Read to receive, pay attention to its parameters. For the sake of convenience, can be directly used RTMP_ReadPacket. Behind the video using RTMP_ReadPacket to receive and process. 

int RTMP_Write2(RTMP *r, const char *buf, int size)
{
  RTMPPacket *pkt = &r->m_write;
  char *pend, *enc;
  int s2 = size, ret, num;


 if (size <11) {
   /* FLV pkt too small */
   return 0;
 }


 if (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V')
   {
     buf += 13;
     s2 -= 13;
   }


 pkt->m_packetType = *buf++;
 pkt->m_nBodySize = AMF_DecodeInt24(buf);
 buf += 3;
 pkt->m_nTimeStamp = AMF_DecodeInt24(buf);
 buf += 3;
 pkt->m_nTimeStamp |= *buf++ <<24;
 buf += 3;
 s2 -= 11;


 if (((pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO
                || pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) &&
            !pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
   {
     pkt->m_headerType = RTMP_PACKET_SIZE_LARGE;
     if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
      pkt->m_nBodySize += 16;
   }
 else
   {
     pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
   }


BYTE outbuf2[640];
int nLen2 = 640;


AVManager::GetInstance()->Decode((BYTE*)(pkt->m_body+1), pkt->m_nBodySize-1, outbuf2, nLen2);
//The actual audio content for the pkt-> m_body+1, size is pkt-> m_nBodySize-1. Here is the voice of Speex code. 
Why to skip the first byte, can refer to: http://bbs.rosoo.net/thread-16488-1-1.html

evt_OnReceivePacket((char*)outbuf2, nLen2);//The callback.



RTMPPacket_Free(pkt);
pkt->m_nBytesRead = 0;


Video processing
You can refer to the rtmpsrv.c
The nRead = RTMP_Read (RTMP, buffer, bufferSize); change: 

RTMPPacket pc = { 0 }, ps = { 0 };
 bool bFirst = true;
while (RTMP_ReadPacket(rtmp, &pc))
{
if (RTMPPacket_IsReady(&pc))
 {
     if (pc.m_packetType == RTMP_PACKET_TYPE_VIDEO && RTMP_ClientPacket(rtmp, &pc))
    {
        bool bIsKeyFrame = false;
     if (result == 0x17)//I frame
    {
        bIsKeyFrame = true;
    }
    else if (result == 0x27)
    {
        bIsKeyFrame = false;
    }
static unsigned char const start_code[4] = {0x00, 0x00, 0x00, 0x01};
fwrite(start_code, 1, 4, pf );
//int ret = fwrite(pc.m_body + 9, 1, pc.m_nBodySize-9, pf);


if( bFirst) {


//AVCsequence header


//ioBuffer.put(foredata);


//Access to SPS


int spsnum = data[10]&0x1f;


int number_sps = 11;


int count_sps = 1;


while (count_sps<=spsnum){


int spslen =(data[number_sps]&0x000000FF)<<8 |(data[number_sps+1]&0x000000FF);


number_sps += 2;


fwrite(data+number_sps, 1, spslen, pf );
fwrite(start_code, 1, 4, pf );


//ioBuffer.put(data,number_sps, spslen);
//ioBuffer.put(foredata);


number_sps += spslen;


count_sps ++;


}


//Get PPS


int ppsnum = data[number_sps]&0x1f;


int number_pps = number_sps+1;


int count_pps = 1;


while (count_pps<=ppsnum){


int ppslen =(data[number_pps]&0x000000FF)<<8|data[number_pps+1]&0x000000FF;


number_pps += 2;


//ioBuffer.put(data,number_pps,ppslen);


//ioBuffer.put(foredata);


fwrite(data+number_pps, 1, ppslen, pf );
fwrite(start_code, 1, 4, pf );


number_pps += ppslen;


count_pps ++;


}


bFirst =false;


} else {


//AVCNALU


int len =0;


int num =5;


//ioBuffer.put(foredata);


while(num<pc.m_nBodySize) 
{


len =(data[num]&0x000000FF)<<24|(data[num+1]&0x000000FF)<<16|(data[num+2]&0x000000FF)<<8|data[num+3]&0x000000FF;


num = num+4;


//ioBuffer.put(data,num,len);


//ioBuffer.put(foredata);


fwrite(data+num, 1, len, pf );
fwrite(start_code, 1, 4, pf );


num = num + len;


}


}       


  }
}

The video analysis.



关于音频解析部分:

AAC ADTS格式分析

1.ADTS是个啥

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_headerAAC ESADTS_headerAAC ES
...
ADTS_headerAAC ES

2.ADTS内容及结构

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:

  • 0: 96000 Hz
  • 1: 88200 Hz
  • 2: 64000 Hz
  • 3: 48000 Hz
  • 4: 44100 Hz
  • 5: 32000 Hz
  • 6: 24000 Hz
  • 7: 22050 Hz
  • 8: 16000 Hz
  • 9: 12000 Hz
  • 10: 11025 Hz
  • 11: 8000 Hz
  • 12: 7350 Hz
  • 13: Reserved
  • 14: Reserved
  • 15: frequency is written explictly
channel_configuration: 表示声道数 

  • 0: Defined in AOT Specifc Config
  • 1: 1 channel: front-center
  • 2: 2 channels: front-left, front-right
  • 3: 3 channels: front-center, front-left, front-right
  • 4: 4 channels: front-center, front-left, front-right, back-center
  • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
  • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
  • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
  • 8-15: Reserved

frame_length : 一个ADTS帧的长度包括ADTS头和AAC原始流.

adts_buffer_fullness:0x7FF 说明是码率可变的码流

3.将AAC打包成ADTS格式

如果是通过嵌入式高清解码芯片做产品的话,一般情况的解码工作都是由硬件来完成的。所以大部分的工作是把AAC原始流打包成ADTS的格式,然后丢给硬件就行了。

通过对ADTS格式的了解,很容易就能把AAC打包成ADTS。我们只需得到封装格式里面关于音频采样率、声道数、元数据长度、aac格式类型等信息。然后在每个AAC原始流前面加上个ADTS头就OK了。

贴上ffmpeg中添加ADTS头的代码,就可以很清晰的了解ADTS头的结构:

[html] view plaincopy
  1. int ff_adts_write_frame_header(ADTSContext *ctx,  
  2.                                uint8_t *buf, int size, int pce_size)  
  3. {  
  4.     PutBitContext pb;  
  5.   
  6.     init_put_bits(&pb, buf, ADTS_HEADER_SIZE);  
  7.   
  8.     /* adts_fixed_header */  
  9.     put_bits(&pb, 12, 0xfff);   /* syncword */  
  10.     put_bits(&pb, 1, 0);        /* ID */  
  11.     put_bits(&pb, 2, 0);        /* layer */  
  12.     put_bits(&pb, 1, 1);        /* protection_absent */  
  13.     put_bits(&pb, 2, ctx->objecttype); /* profile_objecttype */  
  14.     put_bits(&pb, 4, ctx->sample_rate_index);  
  15.     put_bits(&pb, 1, 0);        /* private_bit */  
  16.     put_bits(&pb, 3, ctx->channel_conf); /* channel_configuration */  
  17.     put_bits(&pb, 1, 0);        /* original_copy */  
  18.     put_bits(&pb, 1, 0);        /* home */  
  19.   
  20.     /* adts_variable_header */  
  21.     put_bits(&pb, 1, 0);        /* copyright_identification_bit */  
  22.     put_bits(&pb, 1, 0);        /* copyright_identification_start */  
  23.     put_bits(&pb, 13, ADTS_HEADER_SIZE + size + pce_size); /* aac_frame_length */  
  24.     put_bits(&pb, 11, 0x7ff);   /* adts_buffer_fullness */  
  25.     put_bits(&pb, 2, 0);        /* number_of_raw_data_blocks_in_frame */  
  26.   
  27.     flush_put_bits(&pb);  
  28.   
  29.     return 0;  
  30. }  




基于libRTMP的流媒体直播之 AAC、H264 解析
http://billhoo.blog.51cto.com/2337751/1558209

        前文我们说到如何在基于 libRTMP 库的流媒体直播过程中推送 AAC 、H264 音视频流。本文以上文为基础,阐释如何对 RTMP 包进行解析、重组得到原始的 AAC 音频帧以及 H264 码流。

        在继续阅读本文之前,我们首先假设读者已经能够使用 libRTMP 库从 RTMP 直播服务器不断地获取 RTMP 包,如前提不成立,请自行阅读 [抛开flash,自己开发实现C++ RTMP直播流播放器] 一文,实现一个简单的 RtmpDownloader 测试用例。这一部分恕 Bill 不再赘述。

        废话颇多,下面让我们一起来看看如何对 RTMP 包进行解析和重组。

        根据前文所述,我们在推送音、视频包之前,会首先向服务器推送一个音、视频同步包,该包包含了 AAC 音频帧以及 H264 码流的解码信息。因此当我们首次向服务器请求 RTMP 包之后,服务器会下发给我们对应的音、视频同步包,这将决定我们如何解析并重组音、视频。


RTMP 音频包的解析及 AAC 重组                 

        当我们得到服务器下发的第一个音频包(音频同步包)之后,即可按照前文所述的封包顺序进行逆操作,解析得到我们所需要的 AAC 音频信息,根据这些信息,我们便可重组在推送时被我们去掉的那 个(或 个)字节:ADTSAudio Data Transport Stream),请参见维基百科 [ADTS]。一旦ADTS 构造完毕,直接在其后添加原始 AAC 音频数据即完成了整个 AAC 音频帧的重组。

        根据 ADTS 属性的描述,我们不难抽象出一个反映该信息的结构体如下:

wKiom1QneDSCSvzZAAEoq6e7vv0953.jpg

        接着让我们回顾一下前文提到的音频同步包的结构:

wKioL1QneQ7gzPzqAAKNXyQMvTU227.jpg

        我们只需要从服务器获得的音频同步包中获取 AACDecoderSpecificInfo 以及AudioSpecificConfig 这 个字节,并将其中的内容解析到 RtmpLiveAudioMetadataTy 这个结构中以备使用。示例代码如下:

wKiom1QneyCy6Vu9AAMM-JEbp-c240.jpg

        在获得了 AAC 音频所必要的信息之后,我们便可构造 ADTS 并重组 RTMP 服务器下发来的后续音频包。构造 ADTS 示例代码如下:

wKiom1QnfOPwtSxZAAKoOmSuPco828.jpg

wKiom1Qnfbvjs0rrAAGPacckG4E928.jpg

        至此,ADTS 7个字节便重构完毕,我们只需要在这 个字节后面添加上 AAC 的原始数据便可重组一个 AAC 音频帧。针对之后的每一个 RTMP 音频包,都进行上述步骤,不断将重组后的 AAC音频帧写入文件,使用支持的播放器播放以验证解析正确与否。



RTMP 视频包的解析及 H.264 重组               

        与重组音频帧类似的,我们首先会从服务器获得视频同步包,首先对视频同步包进行拆包解析,得到本次 H264 码流的 Sps 以及 Pps,并从 Sps 中获取视频的宽高信息以备使用。根据前文提到的视频同步包封包流程,我们进行逆向解析的示例如下:

wKiom1QngRKCQGkjAALprfsuRBc380.jpg

wKiom1QngUPhgjl-AAGPsu_PbYs052.jpg

        其中从 Sps 获取视频的宽高信息请读者自行 GoogleBill 不再赘述。解析完视频同步包,我们只需要对接下来的视频包进行分类即可,针对 H264 P 帧,我们直接将原始数据写入文件,针对 I帧,我们在其前面添加保存好的 Sps 以及 Pps,然后写入原始数据即可。针对之后从 RTMP 服务器接收的每一个视频包,均进行上述重组操作,并将获得的 H264 编码写入文件,使用 Elecard StreamEye Tools 播放即可。




如何生成 AAC ADTS 基本流与 Android MediaCodec


想做什么:使用 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.aacffmpeg 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-runtime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enable-libgsm --enable-libmp3lame --enable-libnut --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libutvideo --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --enable-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 rateConsider increasing the value for the 'analyzeduration' and 'probesize' options[aac @ 00000000003cf520] Estimating duration from bitrate, this may be inaccurateBlessedNoColor_nexus7_api18.aac: could not find codec parameters

我的问题:

  1. 我已经在调用 codec.start() 之前配置编码器。所生成的 AAC 文件为什么缺少的编解码器参数?
  2. 在原始的视频编解码器示例中,参数"惩教署-0"从编码器传递到解码器,但不是会写到码流文件中明确。我需要显式地将它们写入到 AAC 文件吗?
  3. 我把输入的 PCM 样本分为每个框架,不一定会产生一个完整的输出数据包 10ms年。为每个输入的帧,我只是写不管编码器输出到文件中。那是令人担忧吗?

任何可以帮助将深为感激。如果有一个示例项目,不会想在这里做什么,那就再好不过。如果我的源代码可以帮你帮助我,我就把它寄。我需要做一些清理。谢谢 !

编辑: 从"初级 AAC 文件由 MediaCodec 缺少的编解码器的参数生成"更改标题为"如何生成 AAC ADTS 基本流与 Android MediaCodec"

解决方法 1:

我最后生成的 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 是和他们一起快乐。







RTMP直播到FMS中的AAC音频直播  


本文引用了下面几个网友的文章:

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).

RTMP直播到FMS中的AAC音频头 AAC Frame Header (转) - niulei20012001 - niulei20012001的博客

 

LetterLength (bits)DescriptionA12syncword 0xFFF, all bits must be 1B1MPEG Version: 0 for MPEG-4, 1 for MPEG-2C2Layer: always 0D1protection absent, Warning, set to 1 if there is no CRC and 0 if there is CRCE2profile, the MPEG-4 Audio Object Type minus 1F4MPEG-4 Sampling Frequency Index (15 is forbidden)G1private stream, set to 0 when encoding, ignore when decodingH3MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE)I1originality, set to 0 when encoding, ignore when decodingJ1home, set to 0 when encoding, ignore when decodingK1copyrighted stream, set to 0 when encoding, ignore when decodingL1copyright start, set to 0 when encoding, ignore when decodingM13frame length, this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)O11Buffer fullnessP2Number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frameQ16CRC 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

  • 1: 88200 Hz
  • 2: 64000 Hz
  • 3: 48000 Hz
  • 4: 44100 Hz
  • 5: 32000 Hz
  • 6: 24000 Hz
  • 7: 22050 Hz
  • 8: 16000 Hz
  • 9: 12000 Hz
  • 10: 11025 Hz
  • 11: 8000 Hz
  • 12: 7350 Hz

    H就是声道

    1: 1 channel: front-center

  • 2: 2 channels: front-left, front-right
  • 3: 3 channels: front-center, front-left, front-right
  • 4: 4 channels: front-center, front-left, front-right, back-center
  • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
  • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
  • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel

    有了这三个参数,就可以发送音频的第一帧了,然后后面的帧,把包头的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

  • RTMP直播到FMS中的AAC音频头 AAC Frame Header (转) - niulei20012001 - niulei20012001的博客

     

    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:

    • 0: 96000 Hz
    • 1: 88200 Hz
    • 2: 64000 Hz
    • 3: 48000 Hz
    • 4: 44100 Hz
    • 5: 32000 Hz
    • 6: 24000 Hz
    • 7: 22050 Hz
    • 8: 16000 Hz
    • 9: 12000 Hz
    • 10: 11025 Hz
    • 11: 8000 Hz
    • 12: 7350 Hz
    • 13: Reserved
    • 14: Reserved
    • 15: frequency is written explictly

    channel_configuration: 表示声道数

    • 0: Defined in AOT Specifc Config
    • 1: 1 channel: front-center
    • 2: 2 channels: front-left, front-right
    • 3: 3 channels: front-center, front-left, front-right
    • 4: 4 channels: front-center, front-left, front-right, back-center
    • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
    • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
    • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
    • 8-15: Reserved

    后面的视频包都是AF 01 + 去除7个字节头的音频AAC数据

































  • 0 0