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

你可能感兴趣的:(音频编解码,多媒体技术)