使用libfdk-aac解码AAC-ELD格式的音频

前段时间尝试在XBMC的框架中添加对Airplay Screen Mirror的功能,有关Airplay的协议可以参考(当然是第三方破解的)

http://nto.github.com/AirPlay.html

本文指针对AAC-ELD音频的解析做一定说明,对于Airplay Screen Mirror本身暂不扩展。


如果是普通的AAC音频,自然可以使用FAAD的库进行解码,或者直接使用ffmpeg,网上有很多的资料,不过FAAD解不了AAC-ELD等级的AAC音频。

但问题有两点,第一是流媒体的格式的音频,也就是没有文件封装格式,流媒体本身是通过RTSP来传递一些配置信息,再利用RTP来传输数据。

第二,AAC-ELD的编码格式,目前能参考的资料不多,不过最新版的ffmepg(>ffmpeg-1.0)或者libav,已经支持了AAC-ELD格式的编码,可以

查看libavcodec目录下是否有libfdk-aacenc.c文件,其实本质是添加了对libfdk-aac音频库的支持,不过遗憾的是没有提供解码的代码。


其实,有关libfdk-aac解码的代码是有的,只是ffmepg没有将其吸纳进来,可以参考github上第三方修改的libav库,地址是

https://github.com/Arcen/libav

同时fdk-aac的库地址是

https://github.com/Arcen/fdk-aac

可以看出libavcodec目录下已经有了libfdk-aacdec.c文件,网友可以自行下载编译,找一个音频文件,先将其转换成AAC-ELD格式的音频,然后在尝试解码。

编码的命令可以参考这种格式

ffmpeg -y -i test.wav -c libfdk_aac -profile:a aac_eld test.mp4

(注意:ffmpeg对于编码一般都采用第三方的库,例如x264等,但解码,ffmpeg本身会调用自己重写的解码代码,所以不一定就能够调用到libfdk-aac来解码

AAC-ELD格式的音频,我当时是修改了ffmpeg的注册模块,强制利用fdk-aac来解码aac的音频来验证的)


以上解决的是文件封装的AAC-ELD音频,可以很容易验证,但对于RTP过来的裸音频数据,就要了解一下AAC的知识了,其中最最重要的是MPEG标准中规定,AAC

格式的音频需要一个AudioSpecificConfig配置,如果你上面生成了mp4文件,就可以利用mp4info.exe这个工具查看esds字段(路径大概在trac->media->

->minf->stbl->stsd->m4a->esds),然后可以参照esds的语法查找相应的AudioSpecificConfig字段。

下面贴一段参考代码,直接利用的fdk-aac的库,在ubuntu上运行的代码

(大概的流程是,有个线程在往队列里面填充数据,然后通过SDL播放,SDL的回调函数会去队列中取音频数据,然后调用fdk-aac解码,然后播放)

  1. /* 
  2.  * decode AAC-ELD audio data from mac by XBMC, and play it by SDL 
  3.  * 
  4.  * modify: 
  5.  * 2012-10-31   first version (ffmpeg tutorial03.c) 
  6.  * 
  7.  */  
  8.    
  9. #include <stdio.h>  
  10. #include <stdlib.h>  
  11. #include <SDL/SDL.h>  
  12. #include <SDL/SDL_thread.h>  
  13. #include <fdk-aac/aacdecoder_lib.h>  
  14. #include "decodeAAC.h"  
  15.    
  16. #ifdef __MINGW32__  
  17. #undef main /* Prevents SDL from overriding main() */  
  18. #endif  
  19.    
  20. typedef unsigned char u8;  
  21. typedef unsigned short int u16;  
  22. typedef unsigned int u32;  
  23.    
  24. /* ---------------------------------------------------------- */  
  25. /*          enable file save, test pcm source                 */  
  26. /* ---------------------------------------------------------- */  
  27. //#define ENABLE_PCM_SAVE  
  28.    
  29. #ifdef ENABLE_PCM_SAVE  
  30. FILE *pout = NULL;  
  31. #endif  
  32. /* ---------------------------------------------------------- */  
  33. /*          next n lines is libfdk-aac config                 */  
  34. /* ---------------------------------------------------------- */  
  35. static int fdk_flags = 0;  
  36.    
  37. /* period size 480 samples */  
  38. #define N_SAMPLE 480  
  39. /* ASC config binary data */  
  40. UCHAR eld_conf[] = { 0xF8, 0xE8, 0x50, 0x00 };  
  41. UCHAR *conf[] = { eld_conf };                   //TODO just for aac eld config  
  42. static UINT conf_len = sizeof(eld_conf);  
  43.    
  44. static HANDLE_AACDECODER phandle = NULL;  
  45. static TRANSPORT_TYPE transportFmt = 0;         //raw data format  
  46. static UINT nrOfLayers = 1;                     //only one layer here  
  47. static CStreamInfo *aac_stream_info = NULL;  
  48.    
  49. static int pcm_pkt_size = 4 * N_SAMPLE;  
  50.    
  51. /* ---------------------------------------------------------- */  
  52. /*          AAC data and queue list struct definition         */  
  53. /* ---------------------------------------------------------- */  
  54. static int quit = 0;  
  55.    
  56. #define FDK_MAX_AUDIO_FRAME_SIZE    192000      //1 second of 48khz 32bit audio  
  57. #define SDL_AUDIO_BUFFER_SIZE 4 * N_SAMPLE  
  58. #define PCM_RATE        44100  
  59. #define PCM_CHANNEL     2  
  60.    
  61. typedef struct AACPacket {  
  62.     unsigned char *data;  
  63.     unsigned int size;  
  64. } AACPacket;  
  65.    
  66. typedef struct AACPacketList {  
  67.     AACPacket pkt;  
  68.     struct AACPacketList *next;  
  69. } AACPacketList;  
  70.    
  71. typedef struct PacketQueue {  
  72.     AACPacketList *first_pkt, *last_pkt;  
  73.     int nb_packets;  
  74.     int size;  
  75.     SDL_mutex *mutex;  
  76.     SDL_cond *cond;  
  77. } PacketQueue;  
  78.    
  79. static PacketQueue audioq;  
  80. /* ---------------------------------------------------------- */  
  81. /*              local memcpy malloc                           */  
  82. /* ---------------------------------------------------------- */  
  83. /* for local memcpy malloc */  
  84. #define AAC_BUFFER_SIZE 1024 * 1024  
  85. #define THRESHOLD       1 * 1024  
  86.    
  87. static u8 repo[AAC_BUFFER_SIZE] = {0};  
  88. static u8 *repo_ptr = NULL;  
  89. /* 
  90.  * init mem repo 
  91.  */  
  92. static void init_mem_repo(void)  
  93. {  
  94.     repo_ptr = repo;  
  95. }  
  96.    
  97. /* 
  98.  * alloc input pkt buffer from input_aac_data[] 
  99.  */  
  100. static void *alloc_pkt_buf(void)  
  101. {  
  102.     int space;  
  103.    
  104.     space = AAC_BUFFER_SIZE - (repo_ptr - repo);  
  105.    
  106.     if (space < THRESHOLD) {  
  107.         repo_ptr = repo;  
  108.         return repo;  
  109.     }  
  110.        
  111.     return repo_ptr;  
  112. }  
  113.    
  114. static void set_pkt_size(int size)  
  115. {  
  116.     repo_ptr += size;  
  117. }  
  118. /* ---------------------------------------------------------- */  
  119.    
  120. static void packet_queue_init(PacketQueue *q)  
  121. {  
  122.     memset(q, 0, sizeof(PacketQueue));  
  123.     q->mutex = SDL_CreateMutex();  
  124.     q->cond = SDL_CreateCond();  
  125. }  
  126.    
  127. static int fdk_dup_packet(AACPacket *pkt)  
  128. {  
  129.     u8 *repo_ptr;  
  130.    
  131.     repo_ptr = alloc_pkt_buf();  
  132.     memcpy(repo_ptr, pkt->data, pkt->size);  
  133.     pkt->data = repo_ptr;  
  134.    
  135.     set_pkt_size(pkt->size);  
  136.    
  137.     return 0;  
  138. }  
  139.    
  140. static int packet_queue_put(PacketQueue *q, AACPacket *pkt)  
  141. {  
  142.     //fprintf(stderr, "p");  
  143.     AACPacketList *pkt1;  
  144.        
  145.     /* memcpy data from xbmc */  
  146.     fdk_dup_packet(pkt);  
  147.    
  148.     pkt1 = malloc(sizeof(AACPacketList));  
  149.     if (!pkt1)  
  150.         return -1;  
  151.     pkt1->pkt = *pkt;  
  152.     pkt1->next = NULL;  
  153.    
  154.     SDL_LockMutex(q->mutex);  
  155.    
  156.     if (!q->last_pkt)  
  157.         q->first_pkt = pkt1;  
  158.     else  
  159.         q->last_pkt->next = pkt1;  
  160.     q->last_pkt = pkt1;  
  161.     q->nb_packets++;  
  162.     q->size += pkt1->pkt.size;  
  163.    
  164.     SDL_CondSignal(q->cond);  
  165.     SDL_UnlockMutex(q->mutex);  
  166.    
  167.     return 0;  
  168. }  
  169.    
  170. /* 
  171.  * called by external, aac data input queue 
  172.  */  
  173. int decode_copy_aac_data(u8 *data, int size)  
  174. {  
  175.     AACPacket pkt;  
  176.    
  177.     pkt.data = data;  
  178.     pkt.size = size;  
  179.    
  180.     packet_queue_put(&audioq, &pkt);  
  181.    
  182.     return 0;  
  183. }  
  184.    
  185. static int packet_queue_get(PacketQueue *q, AACPacket *pkt, int block)  
  186. {  
  187.     //fprintf(stderr, "g");  
  188.     AACPacketList *pkt1;  
  189.     int ret;  
  190.    
  191.     SDL_LockMutex(q->mutex);  
  192.    
  193.     for (;;) {  
  194.         if (quit) {  
  195.             ret = -1;  
  196.             break;  
  197.         }  
  198.    
  199.         pkt1 = q->first_pkt;  
  200.         if (pkt1) {  
  201.             q->first_pkt = pkt1->next;  
  202.             if (!q->first_pkt)  
  203.                 q->last_pkt = NULL;  
  204.             q->nb_packets--;  
  205.             q->size -= pkt1->pkt.size;  
  206.             *pkt = pkt1->pkt;  
  207.             free(pkt1);  
  208.             ret = 1;  
  209.             break;  
  210.         } else if (!block) {  
  211.             ret = 0;  
  212.             break;  
  213.         } else {  
  214.             SDL_CondWait(q->cond, q->mutex);  
  215.         }  
  216.     }  
  217.    
  218.     SDL_UnlockMutex(q->mutex);  
  219.    
  220.     //fprintf(stderr, "o");  
  221.     return ret;  
  222. }  
  223.    
  224. /* 
  225.  * decoding AAC format audio data by libfdk_aac 
  226.  */  
  227. int fdk_decode_audio(INT_PCM *output_buf, int *output_size, u8 *buffer, int size)  
  228. {  
  229.     int ret = 0;  
  230.     int pkt_size = size;  
  231.     UINT valid_size = size;  
  232.     UCHAR *input_buf[1] = {buffer};  
  233.    
  234.     /* step 1 -> fill aac_data_buf to decoder's internal buf */  
  235.     ret = aacDecoder_Fill(phandle, input_buf, &pkt_size, &valid_size);  
  236.     if (ret != AAC_DEC_OK) {  
  237.         fprintf(stderr, "Fill failed: %x\n", ret);  
  238.         *output_size  = 0;  
  239.         return 0;  
  240.     }  
  241.    
  242.     /* step 2 -> call decoder function */  
  243.     ret = aacDecoder_DecodeFrame(phandle, output_buf, pcm_pkt_size, fdk_flags);  
  244.     if (ret == AAC_DEC_NOT_ENOUGH_BITS) {  
  245.         fprintf(stderr, "not enough\n");  
  246.         *output_size  = 0;  
  247.         /* 
  248.          * TODO FIXME 
  249.          * if not enough, get more data 
  250.          * 
  251.          */  
  252.     }  
  253.     if (ret != AAC_DEC_OK) {  
  254.         fprintf(stderr, "aacDecoder_DecodeFrame : 0x%x\n", ret);  
  255.         *output_size  = 0;  
  256.         return 0;  
  257.     }  
  258.        
  259.     *output_size = pcm_pkt_size;  
  260.    
  261. #ifdef ENABLE_PCM_SAVE  
  262.     fwrite((u8 *)output_buf, 1, pcm_pkt_size, pout);     
  263. #endif  
  264.     /* return aac decode size */  
  265.     return (size - valid_size);  
  266. }  
  267.    
  268. int audio_decode_frame(uint8_t *audio_buf, int buf_size)  
  269. {  
  270.     static AACPacket pkt;  
  271.     static uint8_t *audio_pkt_data = NULL;  
  272.     static int audio_pkt_size = 0;  
  273.    
  274.     int len1, data_size;  
  275.    
  276.     for (;;) {  
  277.         while (audio_pkt_size > 0) {  
  278.             data_size = buf_size;  
  279.             len1 = fdk_decode_audio((INT_PCM *)audio_buf, &data_size,  
  280.                     audio_pkt_data, audio_pkt_size);  
  281.             if (len1 < 0) {  
  282.                 /* if error, skip frame */  
  283.                 audio_pkt_size = 0;  
  284.                 break;  
  285.             }  
  286.             audio_pkt_data += len1;  
  287.             audio_pkt_size -= len1;  
  288.             if (data_size <= 0) {  
  289.                 /* No data yet, get more frames */  
  290.                 continue;  
  291.             }  
  292.             /* We have data, return it and come back for more later */  
  293.             //fprintf(stderr, "\ndata size = %d\n", data_size);  
  294.             return data_size;  
  295.         }  
  296.         /* FIXME 
  297.          * add by juguofeng 
  298.          * only no nead in this code, because we alloc a memcpy ourselves 
  299.          */  
  300.         //if(pkt.data)  
  301.         //  free(pkt.data);  
  302.    
  303.         if (quit) {  
  304.             return -1;  
  305.         }  
  306.    
  307.         if (packet_queue_get(&audioq, &pkt, 1) < 0) {  
  308.             return -1;  
  309.         }  
  310.         audio_pkt_data = pkt.data;  
  311.         audio_pkt_size = pkt.size;  
  312.     }  
  313. }  
  314.    
  315. void audio_callback(void *userdata, Uint8 *stream, int len)  
  316. {  
  317.     int len1, audio_size;  
  318.    
  319.     static uint8_t audio_buf[(FDK_MAX_AUDIO_FRAME_SIZE * 3) / 2];  
  320.     static unsigned int audio_buf_size = 0;  
  321.     static unsigned int audio_buf_index = 0;  
  322.        
  323.     //fprintf(stderr, "callback len = %d\n", len);  
  324.    
  325.     while (len > 0) {  
  326.         if (audio_buf_index >= audio_buf_size) {  
  327.             //fprintf(stderr, "c");  
  328.             /* We have already sent all our data; get more */  
  329.             audio_size = audio_decode_frame(audio_buf, sizeof(audio_buf));  
  330.             if (audio_size < 0) {  
  331.                 /* If error, output silence */  
  332.                 audio_buf_size = pcm_pkt_size;       // arbitrary?  
  333.                 memset(audio_buf, 0, audio_buf_size);  
  334.             } else {  
  335.                 audio_buf_size = audio_size;  
  336.             }  
  337.             audio_buf_index = 0;  
  338.         }  
  339.         len1 = audio_buf_size - audio_buf_index;  
  340.         if (len1 > len)  
  341.             len1 = len;  
  342.         memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);  
  343.         len -= len1;  
  344.         stream += len1;  
  345.         audio_buf_index += len1;  
  346.     }  
  347. }  
  348.    
  349. /* 
  350.  * init fdk decoder 
  351.  */  
  352. void init_fdk_decoder(void)  
  353. {  
  354.     int ret = 0;  
  355.    
  356.     phandle = aacDecoder_Open(transportFmt, nrOfLayers);  
  357.     if (phandle == NULL) {  
  358.         printf("aacDecoder open faild!\n");  
  359.         exit(0);  
  360.     }  
  361.    
  362.     printf("conf_len = %d\n", conf_len);  
  363.     ret = aacDecoder_ConfigRaw(phandle, conf, &conf_len);  
  364.     if (ret != AAC_DEC_OK) {  
  365.         fprintf(stderr, "Unable to set configRaw\n");  
  366.         exit(0);  
  367.     }  
  368.    
  369.     aac_stream_info = aacDecoder_GetStreamInfo(phandle);  
  370.     if (aac_stream_info == NULL) {  
  371.         printf("aacDecoder_GetStreamInfo failed!\n");  
  372.         exit(0);  
  373.     }  
  374.     printf("> stream info: channel = %d\tsample_rate = %d\tframe_size = %d\taot = %d\tbitrate = %d\n",   \  
  375.             aac_stream_info->channelConfig, aac_stream_info->aacSampleRate,  
  376.             aac_stream_info->aacSamplesPerFrame, aac_stream_info->aot, aac_stream_info->bitRate);  
  377. }  
  378.    
  379. /* 
  380.  * first init func, called by external 
  381.  */  
  382. void init_fdk_aac_decode(void)  
  383. {  
  384.     SDL_Event       event;  
  385.     SDL_AudioSpec   wanted_spec, spec;  
  386.        
  387.     /* init fdk decoder */  
  388.     init_fdk_decoder();  
  389.     init_mem_repo();  
  390.        
  391.     /* video have already inited in the video decoder */  
  392.     //if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
  393.     //  fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());  
  394.     //  exit(1);  
  395.     //}  
  396.    
  397. #ifdef ENABLE_PCM_SAVE  
  398.     pout = fopen("/home/juguofeng/work/star.pcm""wb");  
  399.     if (pout == NULL) {  
  400.         fprintf(stderr, "open star.pcm file failed!\n");  
  401.         exit(1);  
  402.     }  
  403. #endif  
  404.    
  405.     // Set audio settings from codec info  
  406.     wanted_spec.freq = PCM_RATE;  
  407.     wanted_spec.format = AUDIO_S16SYS;  
  408.     wanted_spec.channels = PCM_CHANNEL;  
  409.     wanted_spec.silence = 0;  
  410.     wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  
  411.     wanted_spec.callback = audio_callback;  
  412.     wanted_spec.userdata = NULL;  
  413.    
  414.     if (SDL_OpenAudio(&wanted_spec, &spec) < 0) {  
  415.         fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());  
  416.         //return -1;  
  417.         exit(1);  
  418.     }  
  419.    
  420.     packet_queue_init(&audioq);  
  421.     SDL_PauseAudio(0);  
  422.    
  423.     //packet_queue_put(&audioq, &packet);  
  424.    
  425. #if 0  
  426.     SDL_PollEvent(&event);  
  427.     switch(event.type) {  
  428.         case SDL_QUIT:  
  429.             quit = 1;  
  430.             SDL_Quit();  
  431.             exit(0);  
  432.             break;  
  433.         default:  
  434.             break;  
  435.     }  
  436. #endif  
  437.     //exit(1);  
  438.     //return 0;  
  439. }  

使用时,可以利用一下接口

void init_fdk_aac_decode(void);

int decode_copy_aac_data(unsigned char *inbuf, int size);

特别注意的是,RTP流中的AAC-ELD音频是裸数据,而解码器需要AudioSpecificConfig信息,这里我是自己事先知道了这个值。


由于离这个项目有段时间了,现在才将其罗列在这里,有些细节不是交代的很清楚,日后有空慢慢补充。

(当时由于对流媒体和mpeg4等标准不是很熟,走了很多的弯路,并且fdk-aac本身的教程只有文档说明,并没有一个

代码实例,同时我实现的又是流媒体音频,所以有些坎坷,知道看到了第三方的libav中有了对AAC-ELD的解码支持的代码,

在了解了AudioSpecificConfig的含义后,才成功解码了RTP中的AAC-ELD音频流)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值