You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用LibAV API将MP3文件转码为G.711编码WAV文件的方案咨询

流程评审结论

你规划的三步转码逻辑是完全合理的,没有方向性误解:

  • 第一步解码MP3得到原始PCM数据
  • 第二步将PCM编码为G.711 μ-law格式
  • 第三步将编码后的数据封装进WAV容器,整体流程符合LibAV转码的标准逻辑

现有简化代码需要修正的问题

你省略了几个旧版LibAV转码必须的关键步骤,直接运行会出现编码失败、音频杂音、内存泄漏、丢尾帧等问题:

  1. 缺失输入流信息解析步骤
    avformat_open_input仅打开文件头,必须调用avformat_find_stream_info解析流参数,还要通过av_find_best_stream找到音频流索引,过滤非音频包,否则解码会直接报错。
  2. 缺失重采样适配步骤
    MP3解码输出的PCM通常是44.1kHz/48kHz、双通道、S16格式,而G.711编码强制要求8kHz采样率、单通道、16位整型输入,必须通过swr_convert做重采样格式转换,否则编码输出的音频会是杂音。
  3. 缺失gotFrame有效性判断
    avcodec_decode_audio4和avcodec_encode_audio2都不是每次调用都会输出有效帧/包,只有当gotFrame返回1时,才能进行下一步处理,否则会传入空指针导致崩溃。
  4. 缺失包内存释放逻辑
    旧版LibAV中av_read_frame拿到的输入包、编码生成的输出包,使用完必须调用av_packet_unref释放引用,否则在嵌入式ARM设备上会出现内存泄漏,长时间运行触发OOM。
  5. 缺失编码器缓存冲刷步骤
    所有输入包处理完成后,需要给编码器传入NULL帧,冲刷编码器内部缓存的剩余数据,否则会丢失最后几百毫秒的音频。
  6. 输出上下文参数需要严格对齐
    初始化WAV输出容器时,音频流的采样率、通道数、编码格式必须和G.711编码器参数完全一致,否则avformat_write_header会调用失败。

适配旧版LibAV的修正后核心流程示例
// 1. 打开输入MP3,解析流信息
AVFormatContext *ifmt_ctx = NULL;
avformat_open_input(&ifmt_ctx, sMp3FileName, NULL, NULL);
avformat_find_stream_info(ifmt_ctx, NULL);
// 绑定音频解码器
int audio_stream_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &decoder, 0);
AVCodecContext *dec_ctx = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(dec_ctx, ifmt_ctx->streams[audio_stream_idx]->codecpar);
avcodec_open2(dec_ctx, decoder, NULL);

// 2. 初始化重采样上下文,适配G.711输入要求
SwrContext *swr_ctx = swr_alloc_set_opts(NULL,
    av_get_default_channel_layout(1), AV_SAMPLE_FMT_S16, 8000, // 输出:单通道、S16、8kHz
    av_get_default_channel_layout(dec_ctx->channels), dec_ctx->sample_fmt, dec_ctx->sample_rate, // 输入:MP3解码参数
    0, NULL, NULL);
swr_init(swr_ctx);

// 3. 初始化G.711 μ-law编码器
AVCodec *encoder = avcodec_find_encoder(CODEC_ID_PCM_MULAW);
AVCodecContext *enc_ctx = avcodec_alloc_context3(encoder);
enc_ctx->sample_rate = 8000;
enc_ctx->channels = 1;
enc_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
enc_ctx->bit_rate = 64000; // G.711固定码率:8kHz * 8bit = 64kbps
avcodec_open2(enc_ctx, encoder, NULL);

// 4. 初始化WAV输出容器
AVFormatContext *ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, "wav", sWavFileName);
AVStream *out_stream = avformat_new_stream(ofmt_ctx, encoder);
avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
avio_open(&ofmt_ctx->pb, sWavFileName, AVIO_FLAG_WRITE);
avformat_write_header(ofmt_ctx, NULL);

// 转码主循环
AVPacket in_pkt, out_pkt;
AVFrame *dec_frame = av_frame_alloc(), *resample_frame = av_frame_alloc();
resample_frame->format = AV_SAMPLE_FMT_S16;
resample_frame->channels = 1;
resample_frame->sample_rate = 8000;

while (av_read_frame(ifmt_ctx, &in_pkt) >= 0) {
    if (in_pkt.stream_index != audio_stream_idx) {
        av_packet_unref(&in_pkt);
        continue;
    }
    // 解码MP3包
    int got_dec = 0;
    avcodec_decode_audio4(dec_ctx, dec_frame, &got_dec, &in_pkt);
    av_packet_unref(&in_pkt);
    if (!got_dec) continue;

    // 重采样到G.711要求的格式
    resample_frame->nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, dec_ctx->sample_rate) + dec_frame->nb_samples, 8000, dec_ctx->sample_rate, AV_ROUND_UP);
    av_frame_get_buffer(resample_frame, 0);
    swr_convert(swr_ctx, resample_frame->data, resample_frame->nb_samples, (const uint8_t **)dec_frame->data, dec_frame->nb_samples);

    // 编码为G.711
    int got_enc = 0;
    av_init_packet(&out_pkt);
    avcodec_encode_audio2(enc_ctx, &out_pkt, resample_frame, &got_enc);
    if (got_enc) {
        av_interleaved_write_frame(ofmt_ctx, &out_pkt);
        av_packet_unref(&out_pkt);
    }
}

// 冲刷编码器剩余缓存
int got_enc = 0;
av_init_packet(&out_pkt);
avcodec_encode_audio2(enc_ctx, &out_pkt, NULL, &got_enc);
if (got_enc) {
    av_interleaved_write_frame(ofmt_ctx, &out_pkt);
    av_packet_unref(&out_pkt);
}

av_write_trailer(ofmt_ctx);
// 后续释放所有申请的资源即可

内容的提问来源于stack exchange,提问作者Steeve007

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 23:48:01