nihohit
nihohit

Reputation: 578

Failing to properly initialize AVFrame for sws_scale conversion

I'm decoding video using FFMpeg, and want to edit the decoded frames using OpenGL, but in order to do that I need to convert the data in AVFrame from YUV to RGB.

In order to do that I create a new AVFrame:

AVFrame *inputFrame = av_frame_alloc();
AVFrame *outputFrame = av_frame_alloc();
av_image_alloc(outputFrame->data, outputFrame->linesize, width, height, AV_PIX_FMT_RGB24, 1);
av_image_fill_arrays(outputFrame->data, outputFrame->linesize, NULL, AV_PIX_FMT_RGB24, width, height, 1);

Create a conversion context:

struct SwsContext *img_convert_ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
                                                  width, height, AV_PIX_FMT_RGB24,
                                                  0, NULL, NULL, NULL);

And then try to convert it to RGB:

sws_scale(img_convert_ctx, (const uint8_t *const *)&inputFrame->data, inputFrame->linesize, 0, inputFrame->height, outputFrame->data, outputFrame->linesize);

But this causes an "[swscaler @ 0x123f15000] bad dst image pointers" error during run time. When I went over FFMpeg's source I found out that the reason is that outputFrame's data wasn't initialized, but I don't understand how it should be.

All existing answers or tutorials that I found (see example) seem to use deprecated APIs, and it's unclear how to use the new APIs. I'd appreciate any help.

Upvotes: 4

Views: 2083

Answers (2)

SebK
SebK

Reputation: 1019

In my case the av_image_alloc / av_image_fill_arrays did not create the frame->data pointers.

Here is how I did it, not sure if everything is correct, but it works:

            d->m_FrameCopy = av_frame_alloc();

            uint8_t* buffer = NULL;
            int numBytes;
            // Determine required buffer size and allocate buffer
            numBytes = avpicture_get_size(
                AV_PIX_FMT_RGB24, d->m_Frame->width, d->m_Frame->height);
            buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));

            avpicture_fill(
                (AVPicture*)d->m_FrameCopy,
                buffer,
                AV_PIX_FMT_RGB24,
                d->m_Frame->width,
                d->m_Frame->height);

            d->m_FrameCopy->format = AV_PIX_FMT_RGB24;
            d->m_FrameCopy->width = d->m_Frame->width;
            d->m_FrameCopy->height = d->m_Frame->height;
            d->m_FrameCopy->channels = d->m_Frame->channels;
            d->m_FrameCopy->channel_layout = d->m_Frame->channel_layout;
            d->m_FrameCopy->nb_samples = d->m_Frame->nb_samples;

Upvotes: 0

Yakov Galka
Yakov Galka

Reputation: 72529

Here's how I call sws_scale:

image buf2((buf.w + 15)/16*16, buf.h, 3);
sws_scale(sws_ctx, (const uint8_t * const *)frame->data, frame->linesize, 0, c->height, (uint8_t * const *)buf2.c, &buf2.ys);

There are two differences here:

  1. You pass &inputFrame->data but it shall be inputFrame->data without the address-of operator.

  2. You don't have to allocate a second frame structure. The sws_scale doesn't care about it. It just needs a chunk of memory of the proper size (and maybe alignment).

Upvotes: 2

Related Questions