[Libav-user] Encoding an AVFrame with MP3 Encoder over libav

Dr. Sven Alisch svenali at t-online.de
Mon Oct 3 11:25:58 EEST 2022


Hello aisnote,

Thank you very much for your reply. My project has many files, which I want (of course) to publish in future. To much to publish these files here, but the whole encoding process I show.

First of all, the original packets uncompressed raw PCM Samples are lying in an int16_t - RingBuffer, so I have to cast them into a uint8_t* buffer with the double size. Then they will converted with swr_convert, because the Sample Format for the MP3 - Encoder is AV_SAMPLE_FMT_FLTP. This all happens in this method, called encodeAudio:

void CExportAudio::encodeAudio()
{
    if (audioSampleRate == 0)
    {
        cerr << "AudioSampleRate not set" << endl;
        return;
    }
    /* find the AAC encoder */
    _Codec = avcodec_find_encoder(AV_CODEC_ID_MP3); // _Codec is initialized
    if (!_Codec)
    {
        cerr <<  "Codec not found!" << endl;
        return;
    }

    _CodecContext = avcodec_alloc_context3(_Codec);
    _CodecContext->bit_rate = 192000;                   // Good CD Quality
    _CodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;     // Input from ALL Decoders is always FMT_FLTP
    _CodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
    _CodecContext->channels       = 2;                  // Stereo
    _CodecContext->sample_rate    = audioSampleRate;

    /* open it */
    if (avcodec_open2(_CodecContext, _Codec, NULL) < 0)
    {
        cerr << "Could not open codec!" << endl;
        return;
    }

    _AVFifo = av_audio_fifo_alloc(
        _CodecContext->sample_fmt,
        _CodecContext->channel_layout, 1);

    int ret = 0;

    FILE *f = fopen("ali-test.mp3", "wb");	 // The output testfile
    if (!f) {
        cerr <<  "Could not open file" << endl;
        return;
    }

    SwrContext *swr = NULL;
    if (!swr)
    {
        swr = swr_alloc_set_opts(NULL,
            _CodecContext->channel_layout,          // output
            _CodecContext->sample_fmt,              // output
            audioSampleRate,                        // output
            AV_CH_LAYOUT_STEREO,                    // input
            AV_SAMPLE_FMT_S16,                      // input
            audioSampleRate,                        // input
            0,
            NULL);
        int swrInit = swr_init(swr);
        if (swrInit < 0)
        {
            cerr << "swr init error swrInit " << swrInit << endl;
            return;
        }
    }

    while (_running)
    {
        AVFrame *input_frame = av_frame_alloc();
        if (!input_frame)
        {
            cerr << "Could not allocate audio frame!" << endl;
            return;
        }

        input_frame->nb_samples     = FRAME_SIZE;
        input_frame->format         = AV_SAMPLE_FMT_S16;
        input_frame->channel_layout = AV_CH_LAYOUT_STEREO;

        int size = _streamBuffer.GetRingBufferReadAvailable();
        cerr << size << " Bytes available." << endl;

        if (size > 0)
        {
            int buffer16_size = FRAME_SIZE * 2;
            int buffer_size = buffer16_size * 2;

            // 16 Bit Samples (size for 16 Bits 2 Byte)
            int16_t *data16_buffer = (int16_t*) av_malloc(buffer16_size);
            // make more space (x2 for uint8_t)
            uint8_t *data = (uint8_t*) av_malloc(buffer_size);

            int data_size = _streamBuffer.getDataFromBuffer(data16_buffer, buffer16_size); <- Here I read these bytes from the Ringbuffer
            transcode_buffer(data, data16_buffer, buffer16_size);

            int error = avcodec_fill_audio_frame(
                    input_frame,
                    2,
                    AV_SAMPLE_FMT_S16,
                    data,
                    buffer_size,
                    1);

            uint8_t** conv_data;
            conv_data = new uint8_t*[buffer_size];
            for (int i=0; i<buffer_size; i++)
            {
                conv_data[i] = new uint8_t[buffer_size];
            }

            ret = swr_convert(
                swr,
                conv_data,
                input_frame->nb_samples,
                (const uint8_t**) input_frame->data,
                input_frame->nb_samples);

            cerr << "Return of swr_convert is ... " << ret * sizeof(float) << endl;

            if (ret > 0)
            {
                if ((error = av_audio_fifo_realloc(
                    _AVFifo,
                    av_audio_fifo_size(_AVFifo) + FRAME_SIZE)) < 0)
                {
                    cerr << "Could not reallocate FIFO" << endl;
                    return;
                }

                /* Store the new samples in the FIFO buffer. */
                if (av_audio_fifo_write(_AVFifo, (void **)conv_data, FRAME_SIZE) < FRAME_SIZE)
                {
                    cerr << "Could not write data to FIFO" << endl;
                    return;
                }

		if (av_audio_fifo_size(_AVFifo) > _CodecContext->frame_size)
            	{
                	encodeFrame(f);
	        }
            }
        }
        else
        {
            cerr << "Wait for INPUT to encode ..." << endl;
            this_thread::sleep_for(chrono::seconds(3));
        }
    }
}

The encodeFrame - method:
void CExportAudio::encodeFrame(FILE *f)
{
    int error = 0;

    /* Temporary storage of the output samples of the frame written to the file. */
    AVFrame *output_frame = av_frame_alloc();
    /* Create a new frame to store the audio samples. */
    if (!output_frame)
    {
        cerr << "Could not allocate output frame" << endl;
        return;
    }
    /* Use the maximum number of possible samples per frame.
     * If there is less than the maximum possible frame size in the FIFO
     * buffer use this number. Otherwise, use the maximum possible frame size. */
    const int frame_size = FFMIN(av_audio_fifo_size(_AVFifo), _CodecContext->frame_size);
    output_frame->nb_samples = frame_size;
    output_frame->format = _CodecContext->sample_fmt;
    output_frame->sample_rate = _CodecContext->sample_rate;
    output_frame->channel_layout = _CodecContext->channel_layout;

    /* Allocate the samples of the created frame. This call will make
     * sure that the audio frame can hold as many samples as specified. */
    /* error = av_frame_get_buffer(output_frame, 0);
    if (error < 0)
    {
        cerr << "Could not allocate output frame samples: " << av_err2str(error) << endl;
        av_frame_free(&output_frame);
        return;
    }

    /* packet for holding encoded output */
    AVPacket *output_packet = av_packet_alloc();
    if (!output_packet)
    {
        cerr << "could not allocate the packet!" << endl;
        return;
    }

    /* Read as many samples from the FIFO buffer as required to fill the frame.
     * The samples are stored in the frame temporarily. */
    int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size);
    if (ret < frame_size)
    {
        cerr << "Could not read data from FIFO" << endl;
        av_frame_free(&output_frame);
        return;
    }

    output_frame->pts = _pts;
    _pts += output_frame->nb_samples;

    ret = avcodec_send_frame(_CodecContext, output_frame);
    if (ret < 0)
    {
        cerr << "Error sending the frame to the encoder! Answer: " << ret << endl;
    }

    /* read all the available output packets (in general there may be any
     * number of them */
    while (ret >= 0)
    {
        ret = avcodec_receive_packet(_CodecContext, output_packet);

        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            cerr << "Error encoding audio frame ... need more DATA ..." << endl;
            return;
        }
        else if (ret < 0)
        {
            cerr << "Error encoding audio frame!" << endl;
            return;
        }

        fwrite(output_packet->data, 1, output_packet->size, f);

        av_packet_unref(output_packet);
    }
#endif
}

And:

I translate the int16_t - (2 Byte-Structure) into uint8_t - Bytes with transcode method:

void CExportAudio::transcode_buffer(uint8_t* uint8_buf, int16_t* buf, int size16)
{
    for (int i = 0; i < size16; i++)
    {
        uint8_buf[i*2] = (uint8_t) (0x00FF & buf[i]);
        uint8_buf[i*2 + 1] = (uint8_t) (buf[i] >> 8);
    }
}

Thank you so much!

Regards,
Sven

> 03.10.2022, в 06:34, aisnote com <aisnote at gmail.com> написал(а):
> 
> Which parameter caused the crash?
> 
> assume  _AVFifo is initialized.
> 
>    int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size);  <- Here it crashes
> 
> can you upload all the source code including your test file? Maybe I can help to debug it.
> 
> On Sun, Oct 2, 2022 at 4:07 PM Dr. Sven Alisch <svenali at t-online.de> wrote:
> Dear developers,
> 
> My original material is a PCM stream (WAV) which I want to encode to MP3. I reformat it with swr_convert from AV_SAMPLE_FMT_S16 to AV_SAMPLE_FMT_FLTP and copy it to a AVFifo, because the frame sizes are different. In my encoding Function I read out my samples from that Fifo and want to send it to the encoder. I use a similar code like the code from tanscode_aac.c. It seems to work fine, but I can not read from Fifo and store the data in to output_frame->data. My program crashes. If I use a self created structure (a buffer I prepared of my own), than it works.
> Also strange for me is, that the linesize-parameter after the av_frame_get_buffer(output_frame, 0); is always for linesize[0] set. Why? I thought, that the FLTP (Planar) mode creates a line size for each channel?
> 
> Here my code:
> 
>     AVFrame *output_frame = av_frame_alloc();
>     if (!output_frame)
>     {
>         cerr << "Could not allocate output frame" << endl;
>         return;
>     }
>     const int frame_size = FFMIN(av_audio_fifo_size(_AVFifo), _CodecContext->frame_size);
>     output_frame->nb_samples = frame_size;
>     output_frame->format = _CodecContext->sample_fmt;
>     output_frame->sample_rate = _CodecContext->sample_rate;
>     output_frame->channel_layout = _CodecContext->channel_layout;
> 
>     error = av_frame_get_buffer(output_frame, 0);
>     if (error < 0)
>     {
>         cerr << "Could not allocate output frame samples: " << av_err2str(error) << endl;
>         av_frame_free(&output_frame);
>         return;
>     }
> 
>     AVPacket *output_packet = av_packet_alloc();
>     if (!output_packet)
>     {
>         cerr << "could not allocate the packet!" << endl;
>         return;
>     }
> 
>     int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size);  <- Here it crashes
>     if (ret < frame_size)
>     {
>         cerr << "Could not read data from FIFO" << endl;
>         av_frame_free(&output_frame);
>         return;
>     }
>     cout << ret * sizeof(float) << " readed Bytes from AV Fifo." << endl;
> 
>     output_frame->pts = _pts;
>     _pts += output_frame->nb_samples;
> 
>     ret = avcodec_send_frame(_CodecContext, output_frame);
>     if (ret < 0)
>     {
>         cerr << "Error sending the frame to the encoder! Answer: " << ret << endl;
>     }
> 
>     while (ret >= 0)
>     {
>         ret = avcodec_receive_packet(_CodecContext, output_packet);
> 
>         if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
>         {
>             cerr << "Error encoding audio frame ... need more DATA ..." << endl;
>             return;
>         }
>         else if (ret < 0)
>         {
>             cerr << "Error encoding audio frame!" << endl;
>             return;
>         }
> 
>         fwrite(output_packet->data, 1, output_packet->size, f);
> 
>         av_packet_unref(output_packet);
> 
> Thank you for help!
> 
> Regards,
> Sven
> _______________________________________________
> Libav-user mailing list
> Libav-user at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/libav-user
> 
> To unsubscribe, visit link above, or email
> libav-user-request at ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> Libav-user mailing list
> Libav-user at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/libav-user
> 
> To unsubscribe, visit link above, or email
> libav-user-request at ffmpeg.org with subject "unsubscribe".

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: Message signed with OpenPGP
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20221003/f5ce0f64/attachment.sig>


More information about the Libav-user mailing list