Как декодировать AAC, используя avcodec_decode_audio4?

Я изменил в своем коде avcodec_decode_audio3 на avcodec_decode_audio4 и добавил обработку кадров. Но теперь я больше не могу декодировать кадры AAC.

Почему avcodec_decode_audio4 возвращает -22 (неверный аргумент)? Следуя ответу ниже, это как-то связано с параметрами в AVContext, которые нужно установить?

Мне пришлось использовать avcodec_decode_audio4, потому что я обновил свой ffmpeg, а затем получил следующую ошибку:

[NULL @ 0xb14f020] Custom get_buffer() for use withavcodec_decode_audio3() detected. 
         Overriding with avcodec_default_get_buffer
[NULL @ 0xb14f020] Please port your application to avcodec_decode_audio4()

В соответствии с ошибкой буфера в avcodec_decode_audio4() это регрессия, есть ли другое решение для этого, кроме возврата к ffmpeg < 0.8?

Декодер использует avcodec_decode_audio4:

AVCodec *codec;
AVCodecContext *avCtx;
AVFrame * decoded_frame = NULL;
uint8_t *outbuf = static_cast<uint8_t *>(malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE));
AVPacket avPacket;

main(){
    av_register_all();
    codec = avcodec_find_decoder(CODEC_ID_AAC);

    //set parameters
    avCtx = avcodec_alloc_context3(codec);
    avCtx->channels = 1;
    avCtx->sample_rate = 44100;
    avCtx->bit_rate=16;

    if (avcodec_open2(avCtx, codec, NULL) < 0) printf("Could not open codec\n");

    av_init_packet(&avPacket);

    //Main decoder loop
    while(1) 
        my_frame_decoder();
    return 0;
}

void my_frame_decoder() {
    //get data
    ...
    avPacket.size = numBytes;
    avPacket.data = inputBytes;
    int len;

    while (avPacket.size > 0) {

    int got_frame = 0;
    if (!decoded_frame) {
        if (!(decoded_frame = avcodec_alloc_frame())) {
            printf("out of memory");
            return;
        }
    } else {
        avcodec_get_frame_defaults(decoded_frame);
    }

    //-------------------->> returns always -22
    len = avcodec_decode_audio4(avCtx, decoded_frame, &got_frame, &avPacket); 

    //do something with the decoded frame
    ...
    avPacket.size -= len;
    avPacket.data += len;
    }

    return;
}

3 ответа

Решение

После нескольких часов поисков я узнал, что dec_ctx из avcodec_decode_audio4 должен быть открыт dec_codec инициализируется av_find_best_stream()

1° av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1,
                &dec_codec, 0);<br>
2° dec_ctx = m_in_aud_strm->codec;<br>
3° av_opt_set_int(dec_ctx, "refcounted_frames", 1, 0);<br>
4° avcodec_open2(dec_ctx, dec_codec, NULL)<br>
.
.
.
5° avcodec_decode_audio4(dec_ctx, pFrame, &got_frame, &pkt);

Я думаю, что проблема заключается в параметрах, установленных в контексте вашего кодека. Пожалуйста, обратитесь к https://www.ffmpeg.org/doxygen/trunk/structAVCodecContext.html для настройки параметров, который изменился с avcodec_decode_audio3 на avcodec_decode_audio4.

<!-- language: c++ -->

#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <math.h>
#include <wctype.h>
#include <wchar.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <locale.h>
#include <signal.h>
#include <limits.h>
#include <float.h>
#include <iso646.h>

#undef NDEBUG
#include <assert.h>

// Use avcodec_send_packet() and avcodec_receive_frame().

//sample code
while (av_read_frame (FormatContext, packet_ptr) >= 0)
{
    /* some code */
    if (packet_ptr->stream_index == audiostream)
    {

        assert(NULL == decoded_frame);

        decoded_frame = av_frame_alloc();

        ret = avcodec_send_packet(pCodecCtx, packet_ptr);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
        {
            av_packet_unref (packet_ptr);

            if (decoded_frame)
            {
                av_frame_unref(decoded_frame);
                decoded_frame = NULL;
            }

            continue;
        }
        else
        {
            if (0 <= ret)
                packet_ptr->size = 0;

            ret = avcodec_receive_frame(pCodecCtx, decoded_frame);
            if (ret >= 0)
                got_frame = 1;
            else
            {
                got_frame = 0;

                if (decoded_frame)
                {
                    av_frame_unref(decoded_frame);
                    decoded_frame = NULL;
                }

                av_packet_unref (packet_ptr);
                continue;
            }
        }

        if(AV_SAMPLE_FMT_FLTP == pCodecCtx->sample_fmt)//AAC sample format for Libav released 10-October-2020 (ffmpeg 4.3.1)
        {
            //now get the PCM data ready to play or save

            int nb_samples = decoded_frame->nb_samples;
            int channels = pCodecCtx->channels;

            if(channels > 2) //for this small sample only 2 channels...
            {
                channels = 2;//it will convert multichannel media files to 2 channels, remember this...more code need to be modified
            }

            int outputBufferLen = nb_samples * channels * 2;
            int size_out //the size of the PCM data as sizeof(char)
                    =outputBufferLen;

            char * buf = malloc(size_out);

            short *outputBuffer=(short *)buf;
            int in_samples = decoded_frame->nb_samples;

            int i=0;
            float * inputChannel0 = (float *)decoded_frame->extended_data[0];

            // Mono
            if (pCodecCtx->channels==1)
            {
                for (i=0; i<in_samples; i++)
                {
                    float sample = *inputChannel0++;
                    if (sample<-1.0f) sample=-1.0f;
                    else if (sample>1.0f) sample=1.0f;
                    outputBuffer[i] = (int16_t) (sample * 32767.0f);//largest positive int16_t
                }
            }
            // Stereo
            else
            {
                float * inputChannel1 = (float *)decoded_frame->extended_data[1];
                for (i=0; i < in_samples; i++)
                {

                    float sample = *inputChannel0++;
                    if (sample<-1.0f) sample=-1.0f;
                    else if (sample>1.0f) sample=1.0f;
                    float sample2 = *inputChannel1++;
                    if (sample2<-1.0f) sample2=-1.0f;
                    else if (sample2>1.0f) sample2=1.0f;

                    outputBuffer[i*2] = (int16_t) ((sample) * 32767.0f);
                    outputBuffer[i*2+1] = (int16_t) ((sample2) * 32767.0f);
                }
            }

            //use buf and size_out here then free the buf

            free(buf);

        }
    }
    av_packet_unref (packet_ptr);

    if (decoded_frame)
    {
        av_frame_unref(decoded_frame);
        decoded_frame = NULL;
    }
}
    
Hope it helps...

Нет другого решения, кроме как обойти старые версии. После тестирования различных сборок, которые работают с avcodec_decode_audio3, я подумал, что другим было бы полезно знать, что ffmpeg-0.10.14.tar.bz2 из https://ffmpeg.org/releases/ работает.

Другие вопросы по тегам