4
votes

Recently I upgraded ffmpeg from 0.9 to 1.0 (tested on Win7x64 and on iOS), and now avcodec_decode_video2 seagfaults. Long story short: the crash occurs every time the video dimensions change (eg. from 320x240 to 160x120 or vice versa).

I receive mpeg4 video stream from some proprietary source and decode it like this:

// once, during initialization:
AVCodec *codec_ = avcodec_find_decoder(CODEC_ID_MPEG4);
AVCodecContext ctx_ = avcodec_alloc_context3(codec_);
avcodec_open2(ctx_, codec_, 0);
AVPacket packet_;
av_init_packet(&packet_);
AVFrame picture_ = avcodec_alloc_frame();

// on every frame:
int got_picture;
packet_.size = size;
packet_.data = (uint8_t *)buffer;
avcodec_decode_video2(ctx_, picture_, &got_picture, &packet_);

Again, all the above had worked flawlessly until I upgraded to 1.0. Now every time the frame dimensions change - avcodec_decode_video2 crashes. Note that I don't assign width/height in AVCodecContext - neither in the beginning, nor when the stream changes - can it be the reason?

I'd appreciate any idea!

Update: setting ctx_.width and ctx_.height doesn't help.

Update2: just before the crash I get the following log messages:

mpeg4, level 24: "Found 2 unreleased buffers!". level 8: "Assertion i < avci->buffer_count failed at libavcodec/utils.c:603"

Update3 upgrading to 1.1.2 fixed this crash. The decoder is able again to cope with dimensions change on the fly.

2
I think that changing frame size "on the fly" is incorrect. The fact that it was workable formerly is an accident. You must restart the decoder each time a frame size changes. - pogorskiy
@pogorskiy it worked "by accident" for years, through many versions! And how should I restart the decoder? - Igor R.
@pogorskiy : calling avcodec_close/avcodec_open2 solved the issue! Please, make your comment an answer, and I'll accept it. - Igor R.

2 Answers

3
votes

You can try to fill the AVPacket::side_data. If you change the frame size, codec receives information from it (see libavcodec/utils.c apply_param_change function)

This structure can be filled as follows:

int my_ff_add_param_change(AVPacket *pkt, int32_t width, int32_t height)
{
    uint32_t flags = 0;
    int size = 4 * 3;
    uint8_t *data;
    if (!pkt)
        return AVERROR(EINVAL);

    flags = AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS;
    data = av_packet_new_side_data(pkt, AV_PKT_DATA_PARAM_CHANGE, size);

    if (!data)
        return AVERROR(ENOMEM);

    ((uint32_t*)data)[0] = flags;
    ((uint32_t*)data)[1] = width;
    ((uint32_t*)data)[2] = height;
    return 0;
}

You need to call this function every time the size changes.

I think this feature has appeared recently. I didn't know about it until I looked new ffmpeg sources.

UPD

As you write, the easiest method to solve the problem is to perform codec restart. Just call avcodec_close / avcodec_open2

0
votes

I just ran into same issue when my frames were changing size on the fly. However, calling avcodec_close/avcodec_open2 is superflous. A cleaner way is to just reset your AVPacket data structure before the call to avcodec_decode_video2. Here it is the code:

av_init_packet(&packet_)

The key here is that this method resets the all of the values of AVPacket to defaults. Check docs for more info.