You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

avcodec_send_frame持续返回-22错误的AAC音频编码器问题求助

avcodec_send_frame持续返回-22错误的AAC音频编码器问题求助

大家好,我正在用ffmpeg/libavcodec写一个WAV转AAC的编码器,找编码器、创建上下文、填充帧数据这些步骤都走通了,但调用avcodec_send_frame()的时候一直返回-22(无效参数错误)。我已经参考ffmpeg官方的音频编码示例,把实际输入数据换成了正弦波测试,但还是没解决问题。

下面是我当前的核心代码片段:

#ifndef AACCONVERTER_H
#define AACCONVERTER_H
extern "C" {
#include <libavcodec/avcodec.h>

class AacConverter {
public:
    AacConverter() : ctx{nullptr}, codec{nullptr} {};
    AacConverter(const int sampleRate, const int channels) {
        // Set up audio encoder
        codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
        if (codec == NULL) {
            qDebug() << "Failed to find AAC encoder";
            return;
        }
        ctx = avcodec_alloc_context3(codec);
        if (!ctx) {
            qDebug() << "Failed to allocate context";
        }
        ctx->bit_rate = 128000;
        ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
        ctx->sample_rate = sampleRate;
        ctx->channel_layout = AV_CH_LAYOUT_STEREO;
        ctx->channels = av_get_channel_layout_nb_channels(ctx->channel_layout);
        ctx->profile = FF_PROFILE_AAC_MAIN;
        ctx->time_base = (AVRational){1, sampleRate};
        ctx->codec_type = AVMEDIA_TYPE_AUDIO;

        int ret = avcodec_open2(ctx, codec, nullptr);
        if (ret < 0) {
            qDebug() << "Failed to open codec:" << ret;
        }
    };

    ~AacConverter() {
        if (ctx) {
            avcodec_close(ctx);
            av_free(ctx);
            ctx = nullptr;
        }
    };

    unsigned char* encodeWav(const char* data, unsigned int length, unsigned int& bufSize) {
        frameEncode = av_frame_alloc();
        if (!frameEncode) return nullptr;
        frameEncode->nb_samples = ctx->frame_size;
        frameEncode->format = ctx->sample_fmt;
        frameEncode->channel_layout = ctx->channel_layout;

        int rawOffset = 0;
        int rawDelta = 0;
        int rawSamplesCount = frameEncode->nb_samples <= length ? frameEncode->nb_samples : length;
        char* dataPtr = (char*)data;
        int i, j, k, ret;
        uint16_t* samples = nullptr;
        float t, tincr;

        qDebug() << "AAC rawSamplesCount" << rawSamplesCount << "frameEncode->nb_samples" << frameEncode->nb_samples << "length" << length;

        /* allocate the data buffers */
        ret = av_frame_get_buffer(frameEncode, 0);
        if (ret < 0) {
            qDebug() << "Could not allocate audio data buffers:" << ret;
            return nullptr;
        }
        ret = av_frame_make_writable(frameEncode);
        if (ret < 0) {
            qDebug() << "Failed to make frame writable:" << ret;
            return nullptr;
        }

        /* encode a single tone sound */
        t = 0;
        tincr = 2 * M_PI * 440.0 / ctx->sample_rate;
        for (i = 0; i < 200; i++) {
            /* make sure the frame is writable -- makes a copy if the encoder
             * kept a reference internally */
            ret = av_frame_make_writable(frameEncode);
            if (ret < 0) exit(1);
            samples = (uint16_t*)frameEncode->data[0];
            for (j = 0; j < ctx->frame_size; j++) {
                samples[2*j] = (int)(sin(t) * 10000);
                for (k = 1; k < ctx->ch_layout.nb_channels; k++)
                    samples[2*j + k] = samples[2*j];
                t += tincr;
            }
            encodeFrame();
        }

        qDebug() << "2" << samples << frameEncode->data[0] << &dataPtr[rawOffset];
        av_frame_unref(frameEncode);
        bufSize = collectedSamples.size();
        return collectedSamples.data();
    }

    void encodeFrame() {
        qDebug() << Q_FUNC_INFO;
        /* send the frame for encoding */
        int ret = avcodec_send_frame(ctx, frameEncode);
        if (ret < 0) {
            qDebug() << "avcodec_send_frame returned" << ret;
            return;
        }
        qDebug() << ret;

        /* read all the available output packets (in general there may be any number of them) */
        while (ret >= 0) {
            ret = avcodec_receive_packet(ctx, &packetEncode);
            if (ret < 0 && ret != AVERROR(EAGAIN)) continue;
            if (ret < 0) break;

            uint8_t* data = (uint8_t *)(malloc(sizeof(uint8_t) * packetEncode.size));
            memcpy(data, packetEncode.data, (size_t)packetEncode.size);
            const auto size = (unsigned int)(packetEncode.size);
            for (unsigned int i = 0; i < size; i++) {
                collectedSamples.push_back(data[i]);
            }
            free(data);
        }
        av_packet_unref(&packetEncode);
    }

private:
    AVCodecContext *ctx;
    AVCodec *codec;
    AVPacket packetEncode;
    AVFrame* frameEncode;
    std::vector<uint8_t> collectedSamples;
};
}
#endif

我梳理出的问题根源&修复方案

1. 采样格式与数据填充完全不匹配

你设置的编码器采样格式是AV_SAMPLE_FMT_FLTP(浮点Planar格式),但代码里却用uint16_t*(16位整型)来强制转换帧数据指针:

  • FLTP格式的每个声道数据是分开存储的:左声道在frame->data[0],右声道在frame->data[1],不是交错存储
  • FLTP的样本值范围是[-1.0, 1.0]的浮点数,不是16位整型的[-32768, 32767]

修复代码

// 替换原有的样本填充逻辑
float t = 0;
float tincr = 2 * M_PI * 440.0 / ctx->sample_rate;
// 分别获取左右声道的浮点数据指针
float* left_ch = static_cast<float*>(frameEncode->data[0]);
float* right_ch = static_cast<float*>(frameEncode->data[1]);

for (int j = 0; j < ctx->frame_size; j++) {
    float sample = sin(t) * 0.5f; // 控制幅度避免过载
    left_ch[j] = sample;
    right_ch[j] = sample; // 左右声道复用同一数据
    t += tincr;
}

2. 帧样本数不符合编码器要求

AAC编码器要求每次发送的帧样本数必须严格等于ctx->frame_size(通常是1024或2048),你代码里计算rawSamplesCount时和输入length做比较,会导致帧样本数不匹配,触发无效参数错误。

修复方式

  • 移除rawSamplesCount相关的逻辑,直接使用ctx->frame_size作为每次发送的样本数
  • 如果是处理实际WAV输入,需要先把数据按ctx->frame_size分块,不足的部分补零

3. 未初始化编码用Packet

类成员packetEncode没有提前初始化,直接传入avcodec_receive_packet()会导致野指针问题。

修复方式
在类构造函数中初始化Packet:

AacConverter(const int sampleRate, const int channels) {
    // ... 其他初始化代码 ...
    av_init_packet(&packetEncode);
    packetEncode.data = nullptr;
    packetEncode.size = 0;
}

4. 声道布局硬编码不合理

你直接硬编码了AV_CH_LAYOUT_STEREO,但构造函数参数里有channels,如果传入非2的声道数会导致矛盾。建议根据传入的声道数自动匹配布局:

// 替换原有的声道布局设置
ctx->channels = channels;
ctx->channel_layout = av_get_default_channel_layout(channels);
if (!ctx->channel_layout) {
    qDebug() << "Unsupported channel count:" << channels;
    return;
}

5. 重复调用av_frame_make_writable()

在循环里每次都调用av_frame_make_writable()是冗余的,只需要在第一次分配帧后,或者发送帧后需要修改数据时调用一次即可。


额外调试技巧

如果还是报错,可以用av_strerror()把错误码转成可读字符串,精准定位问题:

char err_buf[256];
av_strerror(ret, err_buf, sizeof(err_buf));
qDebug() << "avcodec_send_frame failed:" << err_buf;

按上面的方案修改后,应该就能解决avcodec_send_frame()返回-22的问题了!

火山引擎 最新活动