专注于音视频处理及流媒体开发!

罗索实验室

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

时间:2016-03-04 18:11来源:ChinaUnix 作者:jgfntu 点击:
当时由于对流媒体和mpeg4等标准不是很熟,走了很多的弯路,并且fdk-aac本身的教程只有文档说明,并没有一个代码实例,同时我实现的又是流媒体音频,所以有些坎坷,知道看到了第三方的libav中有了对AAC-ELD的解码支持的代码,在了解了AudioSpecificConfig的含义后,才成
TAG: AAC  libfdk-aac  AAC-LD  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.     repo_ptr = repo; 
  94.   
  95. /* 
  96.  * alloc input pkt buffer from input_aac_data[] 
  97.  */ 
  98. static void *alloc_pkt_buf(void
  99.     int space; 
  100.   
  101.     space = AAC_BUFFER_SIZE - (repo_ptr - repo); 
  102.   
  103.     if (space < THRESHOLD) { 
  104.         repo_ptr = repo; 
  105.         return repo; 
  106.     } 
  107.       
  108.     return repo_ptr; 
  109.   
  110. static void set_pkt_size(int size) 
  111.     repo_ptr += size; 
  112. /* ---------------------------------------------------------- */ 
  113.   
  114. static void packet_queue_init(PacketQueue *q) 
  115.     memset(q, 0, sizeof(PacketQueue)); 
  116.     q->mutex = SDL_CreateMutex(); 
  117.     q->cond = SDL_CreateCond(); 
  118.   
  119. static int fdk_dup_packet(AACPacket *pkt) 
  120.     u8 *repo_ptr; 
  121.   
  122.     repo_ptr = alloc_pkt_buf(); 
  123.     memcpy(repo_ptr, pkt->data, pkt->size); 
  124.     pkt->data = repo_ptr; 
  125.   
  126.     set_pkt_size(pkt->size); 
  127.   
  128.     return 0; 
  129.   
  130. static int packet_queue_put(PacketQueue *q, AACPacket *pkt) 
  131.     //fprintf(stderr, "p"); 
  132.     AACPacketList *pkt1; 
  133.       
  134.     /* memcpy data from xbmc */ 
  135.     fdk_dup_packet(pkt); 
  136.   
  137.     pkt1 = malloc(sizeof(AACPacketList)); 
  138.     if (!pkt1) 
  139.         return -1; 
  140.     pkt1->pkt = *pkt; 
  141.     pkt1->next = NULL; 
  142.   
  143.     SDL_LockMutex(q->mutex); 
  144.   
  145.     if (!q->last_pkt) 
  146.         q->first_pkt = pkt1; 
  147.     else 
  148.         q->last_pkt->next = pkt1; 
  149.     q->last_pkt = pkt1; 
  150.     q->nb_packets++; 
  151.     q->size += pkt1->pkt.size; 
  152.   
  153.     SDL_CondSignal(q->cond); 
  154.     SDL_UnlockMutex(q->mutex); 
  155.   
  156.     return 0; 
  157.   
  158. /* 
  159.  * called by external, aac data input queue 
  160.  */ 
  161. int decode_copy_aac_data(u8 *data, int size) 
  162.     AACPacket pkt; 
  163.   
  164.     pkt.data = data; 
  165.     pkt.size = size; 
  166.   
  167.     packet_queue_put(&audioq, &pkt); 
  168.   
  169.     return 0; 
  170.   
  171. static int packet_queue_get(PacketQueue *q, AACPacket *pkt, int block) 
  172.     //fprintf(stderr, "g"); 
  173.     AACPacketList *pkt1; 
  174.     int ret; 
  175.   
  176.     SDL_LockMutex(q->mutex); 
  177.   
  178.     for (;;) { 
  179.         if (quit) { 
  180.             ret = -1; 
  181.             break
  182.         } 
  183.   
  184.         pkt1 = q->first_pkt; 
  185.         if (pkt1) { 
  186.             q->first_pkt = pkt1->next; 
  187.             if (!q->first_pkt) 
  188.                 q->last_pkt = NULL; 
  189.             q->nb_packets--; 
  190.             q->size -= pkt1->pkt.size; 
  191.             *pkt = pkt1->pkt; 
  192.             free(pkt1); 
  193.             ret = 1; 
  194.             break
  195.         } else if (!block) { 
  196.             ret = 0; 
  197.             break
  198.         } else { 
  199.             SDL_CondWait(q->cond, q->mutex); 
  200.         } 
  201.     } 
  202.   
  203.     SDL_UnlockMutex(q->mutex); 
  204.   
  205.     //fprintf(stderr, "o"); 
  206.     return ret; 
  207.   
  208. /* 
  209.  * decoding AAC format audio data by libfdk_aac 
  210.  */ 
  211. int fdk_decode_audio(INT_PCM *output_buf, int *output_size, u8 *buffer, int size) 
  212.     int ret = 0; 
  213.     int pkt_size = size; 
  214.     UINT valid_size = size; 
  215.     UCHAR *input_buf[1] = {buffer}; 
  216.   
  217.     /* step 1 -> fill aac_data_buf to decoder's internal buf */ 
  218.     ret = aacDecoder_Fill(phandle, input_buf, &pkt_size, &valid_size); 
  219.     if (ret != AAC_DEC_OK) { 
  220.         fprintf(stderr, "Fill failed: %x\n", ret); 
  221.         *output_size  = 0; 
  222.         return 0; 
  223.     } 
  224.   
  225.     /* step 2 -> call decoder function */ 
  226.     ret = aacDecoder_DecodeFrame(phandle, output_buf, pcm_pkt_size, fdk_flags); 
  227.     if (ret == AAC_DEC_NOT_ENOUGH_BITS) { 
  228.         fprintf(stderr, "not enough\n"); 
  229.         *output_size  = 0; 
  230.         /* 
  231.          * TODO FIXME 
  232.          * if not enough, get more data 
  233.          * 
  234.          */ 
  235.     } 
  236.     if (ret != AAC_DEC_OK) { 
  237.         fprintf(stderr, "aacDecoder_DecodeFrame : 0x%x\n", ret); 
  238.         *output_size  = 0; 
  239.         return 0; 
  240.     } 
  241.       
  242.     *output_size = pcm_pkt_size; 
  243.   
  244. #ifdef ENABLE_PCM_SAVE 
  245.     fwrite((u8 *)output_buf, 1, pcm_pkt_size, pout);     
  246. #endif 
  247.     /* return aac decode size */ 
  248.     return (size - valid_size); 
  249.   
  250. int audio_decode_frame(uint8_t *audio_buf, int buf_size) 
  251.     static AACPacket pkt; 
  252.     static uint8_t *audio_pkt_data = NULL; 
  253.     static int audio_pkt_size = 0; 
  254.   
  255.     int len1, data_size; 
  256.   
  257.     for (;;) { 
  258.         while (audio_pkt_size > 0) { 
  259.             data_size = buf_size; 
  260.             len1 = fdk_decode_audio((INT_PCM *)audio_buf, &data_size,  
  261.                     audio_pkt_data, audio_pkt_size); 
  262.             if (len1 < 0) { 
  263.                 /* if error, skip frame */ 
  264.                 audio_pkt_size = 0; 
  265.                 break
  266.             } 
  267.             audio_pkt_data += len1; 
  268.             audio_pkt_size -= len1; 
  269.             if (data_size <= 0) { 
  270.                 /* No data yet, get more frames */ 
  271.                 continue
  272.             } 
  273.             /* We have data, return it and come back for more later */ 
  274.             //fprintf(stderr, "\ndata size = %d\n", data_size); 
  275.             return data_size; 
  276.         } 
  277.         /* FIXME  
  278.          * add by juguofeng  
  279.          * only no nead in this code, because we alloc a memcpy ourselves 
  280.          */ 
  281.         //if(pkt.data) 
  282.         //  free(pkt.data); 
  283.   
  284.         if (quit) { 
  285.             return -1; 
  286.         } 
  287.   
  288.         if (packet_queue_get(&audioq, &pkt, 1) < 0) { 
  289.             return -1; 
  290.         } 
  291.         audio_pkt_data = pkt.data; 
  292.         audio_pkt_size = pkt.size; 
  293.     } 
  294.   
  295. void audio_callback(void *userdata, Uint8 *stream, int len) 
  296.     int len1, audio_size; 
  297.   
  298.     static uint8_t audio_buf[(FDK_MAX_AUDIO_FRAME_SIZE * 3) / 2]; 
  299.     static unsigned int audio_buf_size = 0; 
  300.     static unsigned int audio_buf_index = 0; 
  301.       
  302.     //fprintf(stderr, "callback len = %d\n", len); 
  303.   
  304.     while (len > 0) { 
  305.         if (audio_buf_index >= audio_buf_size) { 
  306.             //fprintf(stderr, "c"); 
  307.             /* We have already sent all our data; get more */ 
  308.             audio_size = audio_decode_frame(audio_buf, sizeof(audio_buf)); 
  309.             if (audio_size < 0) { 
  310.                 /* If error, output silence */ 
  311.                 audio_buf_size = pcm_pkt_size;       // arbitrary? 
  312.                 memset(audio_buf, 0, audio_buf_size); 
  313.             } else { 
  314.                 audio_buf_size = audio_size; 
  315.             } 
  316.             audio_buf_index = 0; 
  317.         } 
  318.         len1 = audio_buf_size - audio_buf_index; 
  319.         if (len1 > len) 
  320.             len1 = len; 
  321.         memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1); 
  322.         len -= len1; 
  323.         stream += len1; 
  324.         audio_buf_index += len1; 
  325.     } 
  326.   
  327. /* 
  328.  * init fdk decoder 
  329.  */ 
  330. void init_fdk_decoder(void
  331.     int ret = 0; 
  332.   
  333.     phandle = aacDecoder_Open(transportFmt, nrOfLayers); 
  334.     if (phandle == NULL) { 
  335.         printf("aacDecoder open faild!\n"); 
  336.         exit(0); 
  337.     } 
  338.   
  339.     printf("conf_len = %d\n", conf_len); 
  340.     ret = aacDecoder_ConfigRaw(phandle, conf, &conf_len); 
  341.     if (ret != AAC_DEC_OK) { 
  342.         fprintf(stderr, "Unable to set configRaw\n"); 
  343.         exit(0); 
  344.     } 
  345.   
  346.     aac_stream_info = aacDecoder_GetStreamInfo(phandle); 
  347.     if (aac_stream_info == NULL) { 
  348.         printf("aacDecoder_GetStreamInfo failed!\n"); 
  349.         exit(0); 
  350.     } 
  351.     printf("> stream info: channel = %d\tsample_rate = %d
  352. \tframe_size = %d\taot = %d\tbitrate = %d\n",   \ 
  353. aac_stream_info->channelConfig, 
  354. aac_stream_info->aacSampleRate, 
  355. aac_stream_info->aacSamplesPerFrame, 
  356. aac_stream_info->aot, 
  357. aac_stream_info->bitRate); 
  358.   
  359. /* 
  360.  * first init func, called by external 
  361.  */ 
  362. void init_fdk_aac_decode(void
  363.     SDL_Event       event; 
  364.     SDL_AudioSpec   wanted_spec, spec; 
  365.       
  366.     /* init fdk decoder */ 
  367.     init_fdk_decoder(); 
  368.     init_mem_repo(); 
  369.       
  370.     /* video have already inited in the video decoder */ 
  371.     //if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) { 
  372.     //  fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); 
  373.     //  exit(1); 
  374.     //} 
  375.   
  376. #ifdef ENABLE_PCM_SAVE 
  377.     pout = fopen("/home/juguofeng/work/star.pcm""wb"); 
  378.     if (pout == NULL) { 
  379.         fprintf(stderr, "open star.pcm file failed!\n"); 
  380.         exit(1); 
  381.     } 
  382. #endif 
  383.   
  384.     // Set audio settings from codec info 
  385.     wanted_spec.freq = PCM_RATE; 
  386.     wanted_spec.format = AUDIO_S16SYS; 
  387.     wanted_spec.channels = PCM_CHANNEL; 
  388.     wanted_spec.silence = 0; 
  389.     wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; 
  390.     wanted_spec.callback = audio_callback; 
  391.     wanted_spec.userdata = NULL; 
  392.   
  393.     if (SDL_OpenAudio(&wanted_spec, &spec) < 0) { 
  394.         fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); 
  395.         //return -1; 
  396.         exit(1); 
  397.     } 
  398.   
  399.     packet_queue_init(&audioq); 
  400.     SDL_PauseAudio(0); 
  401.   
  402.     //packet_queue_put(&audioq, &packet); 
  403.   
  404. #if 0 
  405.     SDL_PollEvent(&event); 
  406.     switch(event.type) { 
  407.         case SDL_QUIT: 
  408.             quit = 1; 
  409.             SDL_Quit(); 
  410.             exit(0); 
  411.             break
  412.         default
  413.             break
  414.     } 
  415. #endif 
  416.     //exit(1); 
  417.     //return 0; 

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

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音频流)

(责任编辑:jackyhwei)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201603/17434.html]
本文出处:ChinaUnix 作者:jgfntu 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容