【ffmpeg学习记录】解决ffmpeg-tutorial的噪声问题

在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

你可能感兴趣的:(ffmpeg)