ffmpeg音频播放代码示例-avcodec_decode_audio4

转自https://www.cnblogs.com/ansersion/p/5265033.html


一、概述

最近在学习ffmpeg解码的内容,参考了官方的教程http://dranger.com/ffmpeg/tutorial03.html,结果发现这个音频解码的教程有点问题。参考了各种博客,并同时啃ffplay.c的源码,发现avcodec_decode_audio4多了一个resample(重采样)的概念。

其解码以及播放音频的思路为:

首先,ffmpeg设置本机的audio播放参数(target format),如freq(频率)为44100,format为AV_SAMPLE_FMT_S16,channels为2。这个播放参数是SDL实际播放音频时使用的参数。

但是!但是我们的audio file(如mp3文件)的audio数据很可能有其自己的audio播放参数(source format),而这些参数不同于我们实际的SDL播放参数,于是ffmpeg在其中插入resample(重采用)的过程,将source format转换成target format。

简单的说就是一个audio参数设置思路的转变:

这个思路转变最大的好处,就是本机播放的格式可以不用再迁就audio file,而是可以根据自己的需要自行设定,缺点很显然就是ffmpeg的CPU开销会增大。

 

二、代码示例(源码见“附录”)

源码在官方教程基础上把其中视频部分删除,在main函数最后加上一个无限循环,并添加resample函数,最后将resample插入到sdl的回调函数之中。

源码中关于queue的代码为官网教程原版复制,其主要作用就是让main函数和SDL audio线程互斥的push queue和get queue,以下不再赘述。

1、main函数代码结构

main函数伪代码结构如下:

复制代码
 1 SDL Initialization
 2 ffmpeg open audio file
 3 Set SDL audio parameters
 4 Set ffmpeg audio parameters(target format)
 5 while(ffmpeg_read_frame(pkt)) {
 6     packet_queue_put(pkt);
 7 }
 8 while(1) {
 9     sleep(1);
10 }
复制代码

ffmpeg从audio file中不停的读取数据,并将读出的packet放入queue中。此时我们要清楚,另外还有一个SDL audio线程在等待queue中的数据。

2、SDL audio线程

SDL audio线程主要执行一个回调函数,对应源码中的函数为audio_callback(void * userdata, Uint8 * stream, int len)。这个函数的使命就是将解码后的数据放入参数stream这个缓冲区中,以便SDL audio线程从stream缓冲区中获取数据play。这个缓冲区的大小为参数len,而userdata则是用户自定的参数。其伪代码结构如下:

复制代码
1 audio_buf_index = 0;
2 while(len > 0) {
3     audio_size = audio_decode_frame(audio_buf_tmp);
4     memcpy(stream, audio_buf_tmp, audio_size);
5     len -= audio_size;
6     stream += audio_size;
7     audio_buf_index += audio_size;
8 }
复制代码

其中audio_decode_frame函数会从queue中取出packet,并对packet中的frame进行解码和resample,然后将数据放入audio_buf_tmp缓冲区中。

3、Resample函数

Resample的过程和结构体SwrContext息息相关。使用这个结构体共需要2步。

1、先初始化SwrContex,指定target format和source format;

2、使用已初始化的SwrContext,对frame进行resample。

Resample的伪代码如下:

复制代码
 1 struct SwrContext * swr_ctx = NULL;
 2 audio_hw_params_src = audio_hw_params_tgt
 3 int resample(AVFrame * af, uint8_t * audio_buf, int * audio_buf_size)
 4 {
 5     if(audio_hw_params_src != audio_hw_params(af)) {
 6         swr_ctx = swr_alloc_set_opts(audio_hw_params_tgt, audio_hw_params(af));
 7         audio_hw_params_src = audio_hw_params(af);
 8     }
 9     in = af;
10     swr_convert(swr_ctx, out, in);
11     audio_buf = out;
12 } 
复制代码

一开始,audio_hw_parames_src(source format)被初始化为target format,在resample获得第一个frame后,会从该frame中提取source format,并将其赋值给audio_hw_params_src,同时初始化SwrContext这个结构体,指定target format和source format。然后swr_convert对输入的frame进行resample(swr_convert),然后将resample后得到的数据放进resample函数指定的缓冲区(audio_buf)中。

 

附录:

 

复制代码
  1 #include <libavcodec/avcodec.h>
  2 #include <libavformat/avformat.h>
  3 #include <libswscale/swscale.h>
  4 #include <libswresample/swresample.h>
  5 
  6 #include <SDL.h>
  7 #include <SDL_thread.h>
  8 
  9 #ifdef __MINGW32__
 10 #undef main /* Prevents SDL from overriding main() */
 11 #endif
 12 
 13 #include <stdio.h>
 14 #include <assert.h>
 15 #include <sys/types.h>
 16 #include <sys/stat.h>
 17 #include <fcntl.h>
 18 
 19 // compatibility with newer API
 20 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
 21 #define av_frame_alloc avcodec_alloc_frame
 22 #define av_frame_free avcodec_free_frame
 23 #endif
 24 
 25 #define SDL_AUDIO_BUFFER_SIZE 1024
 26 #define MAX_AUDIO_FRAME_SIZE 192000
 27 
 28 #include <signal.h>
 29 
 30 typedef struct AudioParams {
 31     int freq;
 32     int channels;
 33     int64_t channel_layout;
 34     enum AVSampleFormat fmt;
 35     int frame_size;
 36     int bytes_per_sec;
 37 } AudioParams;
 38 int sample_rate, nb_channels;
 39 int64_t channel_layout;
 40 AudioParams audio_hw_params_tgt;
 41 AudioParams audio_hw_params_src;
 42 
 43 int resample(AVFrame * af, uint8_t * audio_buf, int * audio_buf_size);
 44 
 45 struct SwrContext * swr_ctx = NULL;
 46 
 47 int resample(AVFrame * af, uint8_t * audio_buf, int * audio_buf_size)
 48 {
 49     int data_size = 0;
 50     int resampled_data_size = 0;
 51     int64_t dec_channel_layout;
 52     data_size = av_samples_get_buffer_size(NULL, 
 53             av_frame_get_channels(af),
 54             af->nb_samples,
 55             af->format,
 56             1);
 57 
 58     dec_channel_layout =
 59         (af->channel_layout && av_frame_get_channels(af) == av_get_channel_layout_nb_channels(af->channel_layout)) ?
 60         af->channel_layout : av_get_default_channel_layout(av_frame_get_channels(af));
 61     if(     af->format              != audio_hw_params_src.fmt                 ||
 62             af->sample_rate     != audio_hw_params_src.freq              ||
 63             dec_channel_layout     != audio_hw_params_src.channel_layout     ||
 64             !swr_ctx) {
 65         swr_free(&swr_ctx);
 66         swr_ctx = swr_alloc_set_opts(NULL, 
 67                                         audio_hw_params_tgt.channel_layout, audio_hw_params_tgt.fmt, audio_hw_params_tgt.freq, 
 68                                         dec_channel_layout, af->format, af->sample_rate, 
 69                                         0, NULL);
 70         if (!swr_ctx || swr_init(swr_ctx) < 0) {
 71             av_log(NULL, AV_LOG_ERROR,
 72                    "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
 73                     af->sample_rate, av_get_sample_fmt_name(af->format), av_frame_get_channels(af),
 74                     audio_hw_params_tgt.freq, av_get_sample_fmt_name(audio_hw_params_tgt.fmt), audio_hw_params_tgt.channels);
 75             swr_free(&swr_ctx);
 76             return -1;
 77         }
 78         printf("swr_init\n");
 79         audio_hw_params_src.channels = av_frame_get_channels(af);
 80         audio_hw_params_src.fmt = af->format;
 81         audio_hw_params_src.freq = af->sample_rate;
 82     }
 83 
 84     if (swr_ctx) {
 85         const uint8_t **in = (const uint8_t **)af->extended_data;
 86         uint8_t **out = &audio_buf;
 87         int out_count = (int64_t)af->nb_samples * audio_hw_params_tgt.freq / af->sample_rate + 256;
 88         int out_size  = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, out_count, audio_hw_params_tgt.fmt, 0);
 89         int len2;
 90         if (out_size < 0) {
 91             av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
 92             return -1;
 93         }
 94         av_fast_malloc(&audio_buf, audio_buf_size, out_size);
 95         if (!audio_buf)
 96             return AVERROR(ENOMEM);
 97         len2 = swr_convert(swr_ctx, out, out_count, in, af->nb_samples);
 98         if (len2 < 0) {
 99             av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
100             return -1;
101         }
102         if (len2 == out_count) {
103             av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
104             if (swr_init(swr_ctx) < 0)
105                 swr_free(&swr_ctx);
106         }
107         resampled_data_size = len2 * audio_hw_params_tgt.channels * av_get_bytes_per_sample(audio_hw_params_tgt.fmt);
108     } else {
109         audio_buf = af->data[0];
110         resampled_data_size = data_size;
111     }
112 
113     return resampled_data_size;
114 }
115 
116 static void sigterm_handler(int sig)
117 {
118     exit(123);
119 }
120 
121 typedef struct PacketQueue {
122   AVPacketList *first_pkt, *last_pkt;
123   int nb_packets;
124   int size;
125   SDL_mutex *mutex;
126   SDL_cond *cond;
127 } PacketQueue;
128 
129 PacketQueue audioq;
130 
131 int quit = 0;
132 
133 void packet_queue_init(PacketQueue *q) {
134   memset(q, 0, sizeof(PacketQueue));
135   q->mutex = SDL_CreateMutex();
136   q->cond = SDL_CreateCond();
137 }
138 int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
139 
140   AVPacketList *pkt1;
141   if(av_dup_packet(pkt) < 0) {
142     return -1;
143   }
144   pkt1 = av_malloc(sizeof(AVPacketList));
145   if (!pkt1)
146     return -1;
147   pkt1->pkt = *pkt;
148   pkt1->next = NULL;
149   
150   
151   SDL_LockMutex(q->mutex);
152   
153   if (!q->last_pkt)
154     q->first_pkt = pkt1;
155   else
156     q->last_pkt->next = pkt1;
157   q->last_pkt = pkt1;
158   q->nb_packets++;
159   q->size += pkt1->pkt.size;
160   SDL_CondSignal(q->cond);
161   
162   SDL_UnlockMutex(q->mutex);
163   return 0;
164 }
165 static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
166 {
167   AVPacketList *pkt1;
168   int ret;
169   
170   SDL_LockMutex(q->mutex);
171   
172   for(;;) {
173     
174     if(quit) {
175       ret = -1;
176       break;
177     }
178 
179     pkt1 = q->first_pkt;
180     if (pkt1) {
181       q->first_pkt = pkt1->next;
182       if (!q->first_pkt)
183     q->last_pkt = NULL;
184       q->nb_packets--;
185       q->size -= pkt1->pkt.size;
186       *pkt = pkt1->pkt;
187       av_free(pkt1);
188       ret = 1;
189       break;
190     } else if (!block) {
191       ret = 0;
192       break;
193     } else {
194       SDL_CondWait(q->cond, q->mutex);
195     }
196   }
197   SDL_UnlockMutex(q->mutex);
198   return ret;
199 }
200 
201 AVFrame frame;
202 int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {
203 
204     static AVPacket pkt;
205     static uint8_t *audio_pkt_data = NULL;
206     static int audio_pkt_size = 0;
207 
208     int len1, data_size = 0;
209 
210     for(;;) {
211         while(audio_pkt_size > 0) {
212             int got_frame = 0;
213             len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
214             if(len1 < 0) {
215                 /* if error, skip frame */
216                 audio_pkt_size = 0;
217                 break;
218             }
219             audio_pkt_data += len1;
220             audio_pkt_size -= len1;
221             data_size = 0;
222             if(got_frame) {
223                 data_size = resample(&frame, audio_buf, &buf_size);
224                 // data_size = av_samples_get_buffer_size(NULL, 
225                 //         aCodecCtx->channels,
226                 //         frame.nb_samples,
227                 //         aCodecCtx->sample_fmt,
228                 //         1);
229                 assert(data_size <= buf_size);
230                 // memcpy(audio_buf, frame.data[0], data_size);
231             }
232             if(data_size <= 0) {
233                 /* No data yet, get more frames */
234                 continue;
235             }
236             // memcpy(audio_buf, frame.data[0], data_size);
237 
238             /* We have data, return it and come back for more later */
239             return data_size;
240         }
241         if(pkt.data)
242             av_free_packet(&pkt);
243 
244         if(quit) {
245             return -1;
246         }
247 
248         if(packet_queue_get(&audioq, &pkt, 1) < 0) {
249             return -1;
250         }
251         audio_pkt_data = pkt.data;
252         audio_pkt_size = pkt.size;
253     }
254 }
255 
256 void audio_callback(void *userdata, Uint8 *stream, int len) {
257 
258   AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
259   int len1, audio_size;
260 
261   static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
262   static unsigned int audio_buf_size = 0;
263   static unsigned int audio_buf_index = 0;
264 
265   while(len > 0) {
266       if(audio_buf_index >= audio_buf_size) {
267           /* We have already sent all our data; get more */
268           audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
269           if(audio_size < 0) {
270               /* If error, output silence */
271               audio_buf_size = 1024; // arbitrary?
272               memset(audio_buf, 0, audio_buf_size);
273           } else {
274               audio_buf_size = audio_size;
275           }
276           audio_buf_index = 0;
277       }
278       len1 = audio_buf_size - audio_buf_index;
279       if(len1 > len)
280           len1 = len;
281       memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
282       len -= len1;
283       stream += len1;
284       audio_buf_index += len1;
285   }
286 }
287 
288 int main(int argc, char *argv[]) {
289 
290   AVFormatContext *pFormatCtx = NULL;
291   int             i, audioStream;
292   AVPacket        packet;
293   
294   AVCodecContext  *aCodecCtxOrig = NULL;
295   AVCodecContext  *aCodecCtx = NULL;
296   AVCodec         *aCodec = NULL;
297 
298   SDL_Event       event;
299   SDL_AudioSpec   wanted_spec, spec;
300 
301   signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
302   signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */
303 
304   if(argc < 2) {
305     fprintf(stderr, "Usage: test <file>\n");
306     exit(1);
307   }
308   // Register all formats and codecs
309   av_register_all();
310   
311   if(SDL_Init(SDL_INIT_AUDIO)) {
312     fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
313     exit(1);
314   }
315 
316   // Open video file
317   if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
318     return -1; // Couldn't open file
319   
320   // Retrieve stream information
321   if(avformat_find_stream_info(pFormatCtx, NULL)<0)
322     return -1; // Couldn't find stream information
323   
324   // Dump information about file onto standard error
325   av_dump_format(pFormatCtx, 0, argv[1], 0);
326     
327   // Find the first video stream
328   audioStream=-1;
329   for(i=0; i<pFormatCtx->nb_streams; i++) {
330     if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
331        audioStream < 0) {
332       audioStream=i;
333     }
334   }
335   // if(videoStream==-1)
336   //   return -1; // Didn't find a video stream
337   if(audioStream==-1)
338     return -1;
339    
340   aCodecCtxOrig=pFormatCtx->streams[audioStream]->codec;
341   aCodec = avcodec_find_decoder(aCodecCtxOrig->codec_id);
342   if(!aCodec) {
343     fprintf(stderr, "Unsupported codec!\n");
344     return -1;
345   }
346 
347   // Copy context
348   aCodecCtx = avcodec_alloc_context3(aCodec);
349   if(avcodec_copy_context(aCodecCtx, aCodecCtxOrig) != 0) {
350     fprintf(stderr, "Couldn't copy codec context");
351     return -1; // Error copying codec context
352   }
353 
354   avcodec_open2(aCodecCtx, aCodec, NULL);
355 
356   sample_rate = aCodecCtx->sample_rate;
357   nb_channels = aCodecCtx->channels;
358   channel_layout = aCodecCtx->channel_layout;
359 
360   printf("channel_layout=%" PRId64 "\n", channel_layout);
361   printf("nb_channels=%d\n", nb_channels);
362   printf("freq=%d\n", sample_rate);
363 
364   if (!channel_layout || nb_channels != av_get_channel_layout_nb_channels(channel_layout)) {
365       channel_layout = av_get_default_channel_layout(nb_channels);
366       channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
367       printf("correction\n");
368   }
369 
370   // Set audio settings from codec info
371   wanted_spec.freq = sample_rate;
372   wanted_spec.format = AUDIO_S16SYS;
373   wanted_spec.channels = nb_channels;
374   wanted_spec.silence = 0;
375   wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
376   wanted_spec.callback = audio_callback;
377   wanted_spec.userdata = aCodecCtx;
378   
379   if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {
380     fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
381     return -1;
382   }
383   printf("freq: %d\tchannels: %d\n", spec.freq, spec.channels);
384 
385   audio_hw_params_tgt.fmt = AV_SAMPLE_FMT_S16;
386   audio_hw_params_tgt.freq = spec.freq;
387   audio_hw_params_tgt.channel_layout = channel_layout;
388   audio_hw_params_tgt.channels =  spec.channels;
389   audio_hw_params_tgt.frame_size = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, 1, audio_hw_params_tgt.fmt, 1);
390   audio_hw_params_tgt.bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, audio_hw_params_tgt.freq, audio_hw_params_tgt.fmt, 1);
391   if (audio_hw_params_tgt.bytes_per_sec <= 0 || audio_hw_params_tgt.frame_size <= 0) {
392       printf("size error\n");
393       return -1;
394   }
395   audio_hw_params_src = audio_hw_params_tgt;
396 
397   // audio_st = pFormatCtx->streams[index]
398   packet_queue_init(&audioq);
399   SDL_PauseAudio(0);
400 
401   // Read frames and save first five frames to disk
402   i=0;
403   while(av_read_frame(pFormatCtx, &packet)>=0) {
404     if(packet.stream_index==audioStream) {
405       packet_queue_put(&audioq, &packet);
406     } else {
407       av_free_packet(&packet);
408     }
409     // Free the packet that was allocated by av_read_frame
410     SDL_PollEvent(&event);
411     switch(event.type) {
412     case SDL_QUIT:
413       quit = 1;
414       SDL_Quit();
415       exit(0);
416       break;
417     default:
418       break;
419     }
420 
421   }
422 
423   while(1) SDL_Delay(1000);
424   
425   // Close the codecs
426   avcodec_close(aCodecCtxOrig);
427   avcodec_close(aCodecCtx);
428   
429   // Close the video file
430   avformat_close_input(&pFormatCtx);
431   
432   return 0;
433 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值