ffmpeg编程示例-解码aac

提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study

ffmpeg -i Jasmine.flv -acodec copy -vn Jasmine.aac

进入工程中的player目录,使用上述命令,生成待处理的Jasmine.aac文件。

#include "spdlog/spdlog.h"

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavutil/frame.h"
#include "libavutil/mem.h"
}

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096

char error_buf[1024] = {0};
char* av_get_err(int err_num) {
  av_strerror(err_num, error_buf, sizeof(error_buf));
  return error_buf;
}

void print_sample_format(const AVFrame* frame) {
  SPDLOG_INFO("ar-samplerate: {}", frame->sample_rate);
  SPDLOG_INFO("ac-channel: {}", frame->channels);
  SPDLOG_INFO("f-format: {}",
              frame->format);  //实际存储到本地文件时已经改成交错模式
}

void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame,
            FILE* pcm_file) {
  // send the packet with the compressed data to the decoder
  int ret = avcodec_send_packet(dec_ctx, pkt);
  if (ret == AVERROR(EAGAIN)) {
    SPDLOG_ERROR(
        "Receive_frame and send_packet both returned EAGAIN, which is an API "
        "violation.");
  } else if (ret < 0) {
    SPDLOG_WARN(
        "Error submitting the packet to the decoder, err: {}, pkt_size: {}",
        av_get_err(ret), pkt->size);
      return;
  }

  // read all the output frames (in general there may be any number of them
  while (ret >= 0) {
    ret = avcodec_receive_frame(dec_ctx, frame);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
      return;
    } else if (ret < 0) {
      SPDLOG_ERROR("Error during decoding");
    }

    int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
    if (data_size < 0) {
      // This should not occur, checking just for paranoia
      SPDLOG_ERROR("Failed to calculate data size");
    }

    static int s_print_format = 0;
    if (s_print_format == 0) {
      s_print_format = 1;
      print_sample_format(frame);
    }
    
    for (int i = 0; i < frame->nb_samples; i++) {
      for (int ch = 0; ch < dec_ctx->channels; ch++) {
        fwrite(frame->data[ch] + data_size * i, 1, data_size, pcm_file);
      }
    }
  }
}

int main() {
  const char* aac_file_name = "Jasmine.aac";
  const char* pcm_file_name = "Jasmine.pcm";

  FILE* aac_file = fopen(aac_file_name, "rb");
  if (!aac_file) {
    SPDLOG_ERROR("Could not open {}", aac_file_name);
  }

  FILE* pcm_file = fopen(pcm_file_name, "wb");
  if (!pcm_file) {
    SPDLOG_ERROR("Could not open {}", pcm_file_name);
  }

  //查找解码器
  const AVCodec* audio_codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
  if (!audio_codec) {
    SPDLOG_ERROR("Audio codec not found");
  }

  //获取裸流的解析器
  AVCodecParserContext* audio_parser_ctx = av_parser_init(audio_codec->id);
  if (!audio_parser_ctx) {
    SPDLOG_ERROR("Parser not found");
  }

  //分配解码上下文
  AVCodecContext* audio_codec_ctx = avcodec_alloc_context3(audio_codec);
  if (!audio_codec_ctx) {
    SPDLOG_ERROR("Could not allocate audio codec context");
  }

  //将解码器和解码器上下文进行关联
  int ret = avcodec_open2(audio_codec_ctx, audio_codec, NULL);
  if (ret < 0) {
    SPDLOG_ERROR("Could not oepn audio codec");
  }

  uint8_t aac_buf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
  uint8_t* data = aac_buf;

  AVPacket* pkt = av_packet_alloc();
  AVFrame* decoded_frame = NULL;

  size_t data_size = fread(aac_buf, 1, AUDIO_INBUF_SIZE, aac_file);

  while (data_size > 0) {
    if (!decoded_frame) {
      SPDLOG_WARN("decode frame is NULL");
      if (!(decoded_frame = av_frame_alloc())) {
        SPDLOG_ERROR("Could not allocate audio frame");
      }
    }

    ret = av_parser_parse2(audio_parser_ctx, audio_codec_ctx, &pkt->data,
                           &pkt->size, data, data_size, AV_NOPTS_VALUE,
                           AV_NOPTS_VALUE, 0);
    if (ret < 0) {
      SPDLOG_ERROR("Error while parsing");
    }

    data += ret;  // 跳过已经解析的数据
    data_size -= ret;
    if (pkt->size > 0) {
      decode(audio_codec_ctx, pkt, decoded_frame, pcm_file);
    }

    if (data_size < AUDIO_REFILL_THRESH) {
      memmove(aac_buf, data, data_size);
      data = aac_buf;
      int len =
          fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, aac_file);
      if (len > 0) {
        data_size += len;
      }
    }
  }

  pkt->data = NULL;
  pkt->size = 0;
  decode(audio_codec_ctx, pkt, decoded_frame, pcm_file);  // 冲刷解码器

  fclose(aac_file);
  fclose(pcm_file);

  avcodec_free_context(&audio_codec_ctx);
  av_parser_close(audio_parser_ctx);
  av_frame_free(&decoded_frame);
  av_packet_free(&pkt);

  SPDLOG_INFO("audio decode successful");

  return 0;
}
[2021-09-14 21:30:46.182] [warning] [main.cpp:123] decode frame is NULL
[2021-09-14 21:30:46.184] [info] [main.cpp:19] ar-samplerate: 48000
[2021-09-14 21:30:46.184] [info] [main.cpp:20] ac-channel: 2
[2021-09-14 21:30:46.184] [info] [main.cpp:22] f-format: 8
[2021-09-14 21:30:48.442] [info] [main.cpp:165] audio decode successful

使用如下命令播放生成的pcm文件:

ffplay -f f32le -ar 48000 -ac 2 Jasmine.pcm
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值