在ffmpeg 2.5.1版本下编译ffmpeg-tutorial,发现所有与音频相关的tutorial都有噪声,而ffplay却能完美播放。问度娘谷歌无果,只好看ffplay的代码。对ffplay.c代码结构不熟悉的可以戳这里点击打开链接
在ffplay.c中,有三个与audio有关的重要函数,分别是read_thread、decoder_decode_frame、audio_decode_frame。read_thread负责从文件或流中读取数据,decoder_decode_frame则把read_thread读取到的数据解码,audio_decode_frame从名字上看好像也是在做解码的工作,实际上在ffplay.c中它只是做了格式转换,解码的工作在decoder_decode_frame做了。
对比ffplay.c和tutorial05.c两个文件中处理音频的代码,从数据获取到解码,两者基本都是一致的,唯一的差别就在audio_decode_frame。其中有一行比较关键的代码
len2 = swr_convert(is->swr_ctx, out, out_count, in, is->audio_frame.nb_samples);swr_convert这个函数应该是swresample中的,对音频进行重采样。既然我们找到了两份代码的不同点,只要把tutorial05.c中缺少的那部分补上去应该就没问题了。在这里只贴出audio_decode_frame函数的代码:
int audio_decode_frame(VideoState *is, double *pts_ptr) { int len1, data_size = 0, n, resampled_data_size; int64_t dec_channel_layout; int wanted_nb_samples; AVPacket *pkt = &is->audio_pkt; double pts; AVFrame *frame; for(;;) { while(is->audio_pkt_size > 0) { int got_frame; len1 = avcodec_decode_audio4(is->audio_st->codec, &is->audio_frame, &got_frame, pkt); if(len1 < 0) { /* if error, skip frame */ is->audio_pkt_size = 0; break; } if (got_frame) { frame = &is->audio_frame; data_size = av_samples_get_buffer_size ( NULL, is->audio_st->codec->channels, is->audio_frame.nb_samples, is->audio_st->codec->sample_fmt, 1 ); dec_channel_layout = (frame->channel_layout && av_frame_get_channels(frame) == av_get_channel_layout_nb_channels(frame->channel_layout)) ? frame->channel_layout : av_get_default_channel_layout(av_frame_get_channels(frame)); wanted_nb_samples = frame->nb_samples; if (frame->format != is->audio_src.fmt || dec_channel_layout != is->audio_src.channel_layout || frame->sample_rate != is->audio_src.freq || (wanted_nb_samples != frame->nb_samples && !is->swr_ctx)) { swr_free(&is->swr_ctx); is->swr_ctx = swr_alloc_set_opts(NULL, is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq, dec_channel_layout, frame->format, frame->sample_rate, 0, NULL); if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", frame->sample_rate, av_get_sample_fmt_name(frame->format), av_frame_get_channels(frame), is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels); swr_free(&is->swr_ctx); return -1; } is->audio_src.channel_layout = dec_channel_layout; is->audio_src.channels = av_frame_get_channels(frame); is->audio_src.freq = frame->sample_rate; is->audio_src.fmt = frame->format; } if (is->swr_ctx) { const uint8_t **in = (const uint8_t **)is->audio_frame.extended_data; uint8_t **out = &is->audio_buf1; int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / is->audio_frame.sample_rate + 256; int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0); int len2; if (out_size < 0) { av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n"); return -1; } if (wanted_nb_samples != is->audio_frame.nb_samples) { if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - is->audio_frame.nb_samples) * is->audio_tgt.freq / is->audio_frame.sample_rate, wanted_nb_samples * is->audio_tgt.freq / is->audio_frame.sample_rate) < 0) { av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n"); return -1; } } av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size); if (!is->audio_buf1) return AVERROR(ENOMEM); len2 = swr_convert(is->swr_ctx, out, out_count, in, is->audio_frame.nb_samples); if (len2 < 0) { av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n"); return -1; } if (len2 == out_count) { av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n"); if (swr_init(is->swr_ctx) < 0) swr_free(&is->swr_ctx); } is->audio_buf = is->audio_buf1; resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); } else { is->audio_buf = is->audio_frame.data[0]; resampled_data_size = data_size; } } is->audio_pkt_data += len1; is->audio_pkt_size -= len1; if(data_size <= 0) { /* No data yet, get more frames */ continue; } pts = is->audio_clock; *pts_ptr = pts; n = 2 * is->audio_st->codec->channels; is->audio_clock += (double)data_size / (double)(n * is->audio_st->codec->sample_rate); /* We have data, return it and come back for more later */ return resampled_data_size; } if(pkt->data) av_free_packet(pkt); if(is->quit) { return -1; } /* next packet */ if(packet_queue_get(&is->audioq, pkt, 1) < 0) { return -1; } is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ if(pkt->pts != AV_NOPTS_VALUE) { is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } }另外还需要补齐VideoState中的变量,include swresample,修改Make文件等。具体源码免积分直接下载: tutorial05.c