FFmpeg Opus прерывистый звук ОБНОВЛЕННОЕ ОПИСАНИЕ - PullRequest
5 голосов
/ 08 мая 2020

Я использую FFmpeg и пытаюсь кодировать и декодировать необработанный звук PCM в Opus, используя встроенный код «opus» FFmpeg c. Мои входные образцы - это необработанный PCM 8000 Гц 16-битный моно в формате AV_SAMPLE_FMT_S16. Поскольку для Opus требуется формат сэмплов AV_SAMPLE_FMT_FLTP и частота дискретизации только 48000 Гц, поэтому я перед кодированием делаю повторную выборку сэмплов. SwrContext, я использую первый экземпляр ResamplerAudio для передискретизации необработанного входного аудиосигнала PCM перед кодированием, а второй - для ресэмплинга декодированного аудио, чтобы получить его формат и частоту дискретизации, такие же, как исходные значения входного необработанного аудио.

Класс ResamplerAudio имеет функцию, которая инициализирует его член SwrContext следующим образом:

void ResamplerAudio::init(AVCodecContext *codecContext, int inSampleRate, int outSampleRate, AVSampleFormat inSampleFmt, AVSampleFormat outSampleFmt)
{
    swrContext = swr_alloc();
    if (!swrContext)
    {
        LOGE(TAG, "[init] Couldn't allocate swr context");
        return;
    }

    av_opt_set_int(swrContext, "in_channel_layout", (int64_t) codecContext->channel_layout, 0);
    av_opt_set_int(swrContext, "out_channel_layout", (int64_t) codecContext->channel_layout,  0);

    av_opt_set_int(swrContext, "in_channel_count", codecContext->channels, 0);
    av_opt_set_int(swrContext, "out_channel_count", codecContext->channels, 0);

    av_opt_set_int(swrContext, "in_sample_rate", inSampleRate, 0);
    av_opt_set_int(swrContext, "out_sample_rate", outSampleRate, 0);

    av_opt_set_sample_fmt(swrContext, "in_sample_fmt", inSampleFmt, 0);
    av_opt_set_sample_fmt(swrContext, "out_sample_fmt", outSampleFmt,  0);

    int ret = swr_init(swrContext);
    if (ret < 0)
    {
        LOGE(TAG, "[init] swr_init error: %s", av_err2str(ret));
        return;
    }

    LOGD(TAG, "[init] success codecContext->channel_layout: %d; inSampleRate: %d; outSampleRate: %d; inSampleFmt: %d; outSampleFmt: %d", (int) codecContext->channel_layout, inSampleRate, outSampleRate, inSampleFmt, outSampleFmt);
}

И я вызываю функцию ResamplerAudio::init для первого экземпляра ResamplerAudio (этот экземпляр выполняет ресампинг исходного входного аудио PCM перед кодирование, и я назвал его resamplerEncoder) со следующими аргументами:

resamplerEncoder->init(contextEncoder, 8000, 48000, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP);

Второй экземпляр ResamplerAudio (этот экземпляр выполняет ресампинг после декодирования звука из Opus, и я назвал его resamplerDecoder). со следующими аргументами:

resamplerDecoder->init(contextDecoder, 48000, 8000, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16);

Функция ResamplerAudio, которая выполняет повторную выборку, выглядит следующим образом:

std::vector<uint8_t> ResamplerAudio::convert(uint8_t **inData, int inSamplesCount, int outChannels, int outFormat)
{
    std::vector<uint8_t> result;
    uint8_t *dstData = NULL;
    const int dstNbSamples = swr_get_out_samples(swrContext, inSamplesCount);
    av_samples_alloc(&dstData, NULL, outChannels, dstNbSamples, AVSampleFormat(outFormat), 1);
    int resampledSize = swr_convert(swrContext, &dstData, dstNbSamples, (const uint8_t **)inData, inSamplesCount);
    int dstBufSize = av_samples_get_buffer_size(NULL, outChannels, resampledSize, AVSampleFormat(outFormat), 1);

    if (dstBufSize <= 0) return result;

    std::copy(&dstData[0], &dstData[dstBufSize], std::back_inserter(result));

    return result;
}

И я вызываю функцию ResamplerAudio::convert перед кодированием со следующими аргументами:

// data - an array of raw pcm audio
// dataLength - the length of data array
// getSamplesCount() - function that calculates samples count
// frameEncode - AVFrame that using for encode audio
std::vector<uint8_t> resampledData = resamplerEncoder->convert(&data, getSamplesCount(dataLength, frameEncode->channels, AV_SAMPLE_FMT_S16), frameEncode->channels, frameEncode->format);

getSamplesCount() функция выглядит так:

getSamplesCount(int bytesCount, int channels, AVSampleFormat format)
{
    return bytesCount / av_get_bytes_per_sample(format) / channels;
}

После этого я заполните мой frameEncode сэмплами с передискретизацией:

memcpy(&frame->data[0][0], &resampledData[0], sizeof(uint8_t) * resampledDataLength);

И передайте frameEncode в кодировку, подобную этой encodeFrame(resampledDataLength):

void encodeFrame(int dataLength)
{
    /* 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;

        // encodedData - std::vector<uint8_t> that stores encoded data
        std::copy(&packetEncode->data[0], &packetEncode->data[dataLength], std::back_inserter(encodedData));
        av_packet_unref(packetEncode);
    }
}

Затем я декодирую свои закодированные сэмплы и делаю ресэмплинг до вернуть их в формате исходного образца и с частотой дискретизации, поэтому я вызываю функцию ResamplerAudio::convert для resamplerDecoder со следующими аргументами:

// frameDecode - AVFrame that holds decoded audio
std::vector<uint8_t> resampledData = resamplerDecoder->convert(frameDecode->data, frameDecode->nb_samples, frameDecode->channels, AV_SAMPLE_FMT_S16);

И звук результата прерывистый, и я также заметил, что размер декодированного массива равен больше, чем размер исходного массива с необработанным звуком pcm.

Пожалуйста, какие-нибудь идеи, что я делаю не так? *, Я сделал пересэмплинг сырого звука PCM без каких-либо процедур кодирования и декодирования. Сначала я попытался преобразовать частоту дискретизации входного звука с 8000 Гц на 48000 Гц, затем я взял повторно дискретизированные образцы из шага выше и преобразовал его частоту дискретизации с 48000 Гц на 8000 Гц, и в результате звук был идеальным и чистым, также я сделал то же самое. шаги, но я преобразовал не частоту дискретизации, а формат выборки из AV_SAMPLE_FMT_S16 в AV_SAMPLE_FMT_FLTP и наоборот, и снова звук результата идеальный и чистый, также я получил тот же результат, когда покрыл как частоту дискретизации, так и формат выборки. Итак, я предполагаю, что проблема искаженного и прерывистого звука заключается в моей процедуре кодирования или декодирования, я думаю, наиболее вероятно, в процедуре декодирования, потому что после декодирования я ВСЕГДА получаю AVFrame с 960 nb_samples, несмотря на размер входного звука .

Моя процедура декодирования выглядит так:

std::vector<uint8_t> decode(uint8_t *data, unsigned int dataLength)
{
    decodedData.clear();

    int dataSize = dataLength;

    while (dataSize > 0)
    {
        if (!frameDecode)
        {
            frameDecode = av_frame_alloc();
            if (!frameDecode)
            {
                LOGE(TAG, "[decode] Couldn't allocate the frame");
                return EMPTY_DATA;
            }
        }

        ret = av_parser_parse2(parser, contextDecoder, &packetDecode->data, &packetDecode->size, &data[0], dataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
            LOGE(TAG, "[decode] av_parser_parse2 error: %s", av_err2str(ret));
            return EMPTY_DATA;
        }

        data += ret;
        dataSize -= ret;

        doDecode();
    }
    return decodedData;
}

void doDecode()
{
    if (packetDecode->size) {
        /* send the packet with the compressed data to the decoder */
        int ret = avcodec_send_packet(contextDecoder, packetDecode);
        if (ret < 0) LOGE(TAG, "[decode] avcodec_send_packet error: %s", av_err2str(ret));

        /* read all the output frames (in general there may be any number of them */
        while (ret >= 0)
        {
            ret = avcodec_receive_frame(contextDecoder, frameDecode);
            if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) LOGE(TAG, "[decode] avcodec_receive_frame error: %s", av_err2str(ret));
            if (ret < 0) break;

            std::vector<uint8_t> resampledData = resamplerDecoder->convert(frameDecode->data, frameDecode->nb_samples, frameDecode->channels, AV_SAMPLE_FMT_S16);
            if (!resampledData.size()) continue;
            std::copy(&resampledData.data()[0], &resampledData.data()[resampledData.size()], std::back_inserter(decodedData));
        }
    }
}

UPD 30.05.2020

Я решил отказаться от использования FFmpeg в моем проекте и использовать libopus 1.3 .1 , поэтому я сделал вокруг него обертку , и он отлично работает.

...