【问题标题】:How to put audio data in AVFrame for encode如何将音频数据放入 AVFrame 进行编码
【发布时间】:2020-04-15 08:51:28
【问题描述】:

我尝试将原始 PCM 声音编码为 G711A 和 G711U,然后对其进行解码,使用此编解码器一切正常,因为我可以为 AVCodecContext frame_size 选择任何值进行编码,但对于 Opus 编解码器,AVCodecContext frame_size 等于 120 ,所以如果我理解正确,如果我的输入数据数组大小大于 120,那么我需要进行某种缓冲并将我的输入数据分成几个部分,然后依次将其放入 AVFrame->data 并将 AVFrame 传递给编码。

结果我得到一个非常糟糕的声音,我不仅在使用 Opus 编解码器时得到这个结果,而且在 G711 中,如果我将它的 AVCodecContext frame_size 设置为某个小于我输入数据大小的值。

所以我的问题是:如果输入数据的大小大于 AVCodecContext frame_size,那么编码输入数据的正确方法是什么?我是否需要将输入数据拆分为

此时我的代码如下所示:

void encode(uint8_t *data, unsigned int length)
{
    int rawOffset = 0;
    int rawDelta = 0;
    int rawSamplesCount = frameEncode->nb_samples <= length ? frameEncode->nb_samples : length;

    while (rawSamplesCount > 0)
    {
        memcpy(frameEncode->data[0], &data[rawOffset], sizeof(uint8_t) * rawSamplesCount);

        encodeFrame();

        rawOffset += rawSamplesCount;
        rawDelta = length - rawOffset;
        rawSamplesCount = rawDelta > frameEncode->nb_samples ? frameEncode->nb_samples : rawDelta;
    }

    av_frame_unref(frameEncode);
}

void encodeFrame()
{
    /* send the frame for encoding */
    int ret = avcodec_send_frame(contextEncoder, frameEncode);
    if (ret < 0)
    {
        LOGE(TAG, "[encodeFrame] avcodec_send_frame error: %s", av_err2str(ret));
        return;
    }

    /* read all the available output packets (in general there may be any number of them) */
    while (ret >= 0)
    {
        ret = avcodec_receive_packet(contextEncoder, packetEncode);
        if (ret < 0 && ret != AVERROR(EAGAIN)) LOGE(TAG, "[encodeFrame] error in avcodec_receive_packet: %s", av_err2str(ret));
        if (ret < 0) break;
        std::pair<uint8_t*, unsigned int> p = std::pair<uint8_t*, unsigned int>();
        p.first = (uint8_t *)(malloc(sizeof(uint8_t) * packetEncode->size));
        memcpy(p.first, packetEncode->data, (size_t)packetEncode->size);
        p.second = (unsigned int)(packetEncode->size);

        listEncode.push_back(p); // place encoded data into list to finally create one array of encoded data from it
    }
    av_packet_unref(packetEncode);
}

您可以看到我将输入数据分成几个部分,然后将其放入 frame->data 中,然后将帧传递给编码,但我不确定这是正确的方法。

UPD:我注意到,当我使用 G711 时,如果我将 AVCodecContext frame_size 设置为 160,并且我的输入数据大小为 160 或 320,一切正常,但如果输入数据大小为 640,那么我会听到糟糕的嗡嗡声。

【问题讨论】:

    标签: c++ audio encoding ffmpeg


    【解决方案1】:

    你说了这么多,“所以如果我理解正确,如果我的输入数据数组大小大于 120,那么我需要做一些缓冲并将我的输入数据分成几个部分,然后依次放入AVFrame->data 并将 AVFrame 传递给编码。”

    这就是你需要的。 BUFF样本,每次发送固定数量进行编码。

    【讨论】:

    • 好的,感谢您的回答,但为什么我的代码声音不好?我在哪里做错了?如您所见,我使用 frame->nb_samples 值来拆分我的输入数据,所以我认为 nb_samples 值等于该帧可以具有的音频数据的最大字节数是否正确?例如如果 frame->nb_samples == 120 那么我可以放入 frame->data 不超过 120 字节的音频数据?
    • “那么我认为 nb_samples 值等于帧可以具有的音频数据的最大字节数是否正确?” 不一定。这取决于编解码器,如果您直接发送它,有时取决于设备。通常,当与视频同步编码时,音频 nb_samples 将等于 frame_rate。那是;让我们假设 48KHz 音频和 60Hz 视频帧速率,这使得每帧 48000/60 = 960 个样本。
    • 我想你误解了我的意思,我的意思是我认为一个 nb_sample 等于一个字节的音频数据,反之亦然是正确的吗?
    • 没有。在这种情况下,计算是这样的:假设 16Bit 2 Channels 音频。所以每个样本的字节数等于 2 * 2 = 4 个字节,nb_sample 应该是 1。
    猜你喜欢
    • 2015-02-10
    • 2017-01-12
    • 1970-01-01
    • 2019-01-09
    • 2012-05-14
    • 2019-10-28
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    相关资源
    最近更新 更多