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

使用libfdk_aac编码PCM为恒定比特率失败的原因及解决方法

问题

尝试使用libfdk_aac编码器将S16格式、双声道、44100Hz的PCM音频编码为恒定比特率(CBR)的AAC,但无论先编码为AAC再通过FFmpeg命令./ffmpeg -i input.aac -c copy output.mp4封装成MP4,还是直接编码输出MP4,用MediaInfo检测结果都是可变比特率(VBR)。相关编码代码如下:

第一段代码(直接编码为AAC文件)

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>   
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}

int main(int argc, char** argv) {
    AVCodecContext* codec_context = NULL;
    const AVCodec* codec = NULL;
    AVFrame* frame = NULL;
    AVPacket* pkt = NULL;
    FILE* input_file = NULL;
    FILE* output_file = NULL;
    int ret;

    // Open input file
    const char* input_filename = "D:\\audio\\b.pcm";
    const char* output_filename = "D:\\audio\\input.aac";
    input_file = fopen(input_filename, "rb");
    output_file = fopen(output_filename, "wb");
    if (!input_file || !output_file) {
        fprintf(stderr, "Could not open input or output file\n");
        exit(1);
    }

    // Find the AAC encoder
    codec = avcodec_find_encoder_by_name("libfdk_aac");
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    codec_context = avcodec_alloc_context3(codec);
    if (!codec_context) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }

    // Set codec parameters
    codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
    codec_context->sample_rate = 44100;
    codec_context->bit_rate = 256000;
    codec_context->rc_buffer_size = codec_context->bit_rate; 
    codec_context->rc_min_rate = codec_context->bit_rate;   
    codec_context->rc_max_rate = codec_context->bit_rate;  
    av_channel_layout_default(&codec_context->ch_layout, 2);
    // Open codec
    if (avcodec_open2(codec_context, codec, &opts) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    // Initialize packet
    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "Could not allocate AVPacket\n");
        exit(1);
    }

    // Initialize frame
    frame = av_frame_alloc();
    frame->nb_samples = codec_context->frame_size;
    frame->format = codec_context->sample_fmt;
    frame->ch_layout.nb_channels = 2;

    // Allocate the data buffers
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }

    // Main loop: read from the input file, encode, write to the output file
    while (fread(frame->data[0], 1, frame->linesize[0], input_file) == frame->linesize[0]) {
        // Send the frame to the encoder
        if (avcodec_send_frame(codec_context, frame) < 0) {
            fprintf(stderr, "Error sending frame to codec\n");
            exit(1);
        }

        // Get the encoded packet
        while (avcodec_receive_packet(codec_context, pkt) == 0) {
            fwrite(pkt->data, 1, pkt->size, output_file);
            av_packet_unref(pkt);
        }
    }

    // Flush the encoder
    avcodec_send_frame(codec_context, NULL);
    while (avcodec_receive_packet(codec_context, pkt) == 0) {
        fwrite(pkt->data, 1, pkt->size, output_file);
        av_packet_unref(pkt);
    }

    // Clean up
    fclose(input_file);
    fclose(output_file);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_context);

    return 0;
}

第二段代码(直接编码输出MP4文件)

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>   
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}

int main(int argc, char** argv) {
    AVCodecContext* codec_context = NULL;
    const AVCodec* codec = NULL;
    AVFormatContext* format_context = NULL;
    AVStream* audio_stream = NULL;
    AVFrame* frame = NULL;
    AVPacket* pkt = NULL;
    FILE* input_file = NULL;
    int ret;
    int64_t next_pts = 0;

    // Open input file
    const char* input_filename = "D:\\audio\\b.pcm";
    const char* output_filename = "D:\\audio\\input.mp4";
    input_file = fopen(input_filename, "rb");
    if (!input_file) {
        fprintf(stderr, "Could not open input file\n");
        exit(1);
    }

    // Find the AAC encoder
    codec = avcodec_find_encoder_by_name("libfdk_aac");
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    // Allocate AVFormatContext for MP4 output
    avformat_alloc_output_context2(&format_context, NULL, NULL, output_filename);
    if (!format_context) {
        fprintf(stderr, "Could not allocate output context\n");
        exit(1);
    }

    // Add audio stream to the output file
    audio_stream = avformat_new_stream(format_context, codec);
    if (!audio_stream) {
        fprintf(stderr, "Could not allocate stream\n");
        exit(1);
    }

    codec_context = avcodec_alloc_context3(codec);
    if (!codec_context) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }
    audio_stream->codecpar->frame_size = codec_context->frame_size = 1024;

    // Set codec parameters
    codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
    codec_context->sample_rate = 44100;
    codec_context->bit_rate = 256000;
    codec_context->rc_buffer_size = codec_context->bit_rate;
    codec_context->rc_min_rate = codec_context->bit_rate;
    codec_context->rc_max_rate = codec_context->bit_rate;
    av_channel_layout_default(&codec_context->ch_layout, 2);

    // Copy settings to stream
    ret = avcodec_parameters_from_context(audio_stream->codecpar, codec_context);
    if (ret < 0) {
        fprintf(stderr, "Failed to copy codec parameters to stream\n");
        exit(1);
    }

    // Open codec
    if (avcodec_open2(codec_context, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    // Open output file
    if (!(format_context->oformat->flags & AVFMT_NOFILE)) {
        ret = avio_open(&format_context->pb, output_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'\n", output_filename);
            exit(1);
        }
    }

    // Write file header
    ret = avformat_write_header(format_context, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        exit(1);
    }

    // Initialize packet and frame
    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "Could not allocate AVPacket\n");
        exit(1);
    }

    frame = av_frame_alloc();
    frame->nb_samples = codec_context->frame_size;
    frame->format = codec_context->sample_fmt;
    frame->ch_layout = codec_context->ch_layout;

    // Allocate the data buffers
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate audio data buffers\n");
        exit(1);
    }

    // Main loop: read from the input file, encode, write to the output file
    while (fread(frame->data[0], 1, frame->linesize[0], input_file) == frame->linesize[0]) {
        frame->pts = next_pts;  // Set PTS for the frame
        next_pts += frame->nb_samples;  // Increment the next PTS
        // Send the frame to the encoder
        if (avcodec_send_frame(codec_context, frame) < 0) {
            fprintf(stderr, "Error sending frame to codec\n");
            exit(1);
        }

        // Get the encoded packet
        while (avcodec_receive_packet(codec_context, pkt) == 0) {
            pkt->pts = pkt->dts = frame->pts;
            av_packet_rescale_ts(pkt, codec_context->time_base, audio_stream->time_base);
            pkt->stream_index = audio_stream->index;
            av_interleaved_write_frame(format_context, pkt);
            av_packet_unref(pkt);
        }
    }

    // Flush the encoder
    avcodec_send_frame(codec_context, NULL);
    while (avcodec_receive_packet(codec_context, pkt) == 0) {
        pkt->pts = pkt->dts = next_pts;
        av_packet_rescale_ts(pkt, codec_context->time_base, audio_stream->time_base);
        pkt->stream_index = audio_stream->index;
        av_interleaved_write_frame(format_context, pkt);
        av_packet_unref(pkt);
        next_pts += pkt->duration;
    }

    // Write file trailer
    av_write_trailer(format_context);

    // Clean up
    fclose(input_file);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_context);
    avio_closep(&format_context->pb);
    avformat_free_context(format_context);

    return 0;
}

原因与解决方案

问题原因

  1. libfdk_aac默认编码模式:libfdk_aac默认启用VBR模式,仅设置bit_rate、rc_min_rate等参数无法强制切换到CBR,必须明确指定编码模式。
  2. 参数初始化异常:第一段代码中avcodec_open2传入了未定义的&opts变量,导致编码器无法正确读取比特率限制参数。
  3. AAC帧特性干扰检测:即使是CBR模式,AAC单帧大小会因帧头、字节对齐有微小波动,MediaInfo可能误判为VBR,需结合实际平均比特率验证。

解决步骤

  1. 修复未定义变量问题:第一段代码中删除avcodec_open2的第三个参数&opts,或正确初始化AVDictionary类型的opts变量。
  2. 强制启用CBR模式:在打开编码器前,通过av_opt_set设置编码器私有参数:
    // 设置为CBR模式(0=CBR,1-5为VBR质量等级)
    av_opt_set(codec_context->priv_data, "vbr", "0", 0);
    // 可选:指定AAC LC profile,提升兼容性
    av_opt_set(codec_context->priv_data, "profile", "aac_lc", 0);
    
  3. 优化缓冲参数:将rc_buffer_size调整为比特率的2-5倍,确保编码器有足够缓冲维持稳定比特率:
    codec_context->rc_buffer_size = codec_context->bit_rate * 2;
    
  4. 验证实际比特率:编码完成后用FFmpeg统计平均比特率,确认是否符合设定值:
    ffmpeg -i input.aac -f null -
    
    查看输出中的bitrate字段即可验证。

修改后的关键代码片段(以第一段代码为例)

在打开编码器前添加以下设置:

// 强制启用CBR模式
av_opt_set(codec_context->priv_data, "vbr", "0", 0);
// 指定AAC LC profile
av_opt_set(codec_context->priv_data, "profile", "aac_lc", 0);
// 调整缓冲大小
codec_context->rc_buffer_size = codec_context->bit_rate * 2;
// 修复avcodec_open2参数
if (avcodec_open2(codec_context, codec, NULL) < 0) {
    fprintf(stderr, "Could not open codec\n");
    exit(1);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:52:32