libav* неправильное декодирование

Используйте libav для сохранения кадров из видео.

Проблема в том, что если вызвать функцию декодирования несколько раз, то 2-й и потом не правильно обработается.

1-й раз такой вывод (все работает нормально):

[swscaler @ 0x8b48510]No accelerated colorspace conversion found from yuv420p to bgra.
good

2-й (не могу найти поток, но это то же самое):

[mp3 @ 0x8ae5800]Header missing
Last message repeated 223 times
[mp3 @ 0x8af31c0]Could not find codec parameters (Audio: mp1, 0 channels, s16)
[mp3 @ 0x8af31c0]Estimating duration from bitrate, this may be inaccurate
av_find_stream_info

Подскажите, пожалуйста, где произошла ошибка.

main.cpp

avcodec_init();
avcodec_register_all();
av_register_all();
char *data;
int size;
//fill data and size
...
decode(data, size);
decode(data, size);

видео.cpp

int f_offset = 0;
int f_length = 0;
char *f_data = 0;

int64_t seekp(void *opaque, int64_t offset, int whence)
{
    switch (whence)
    {
    case SEEK_SET:
        if (offset > f_length || offset < 0)
            return -1;
        f_offset = offset;
        return f_offset;
    case SEEK_CUR:
        if (f_offset + offset > f_length || f_offset + offset < 0)
            return -1;
        f_offset += offset;
        return f_offset;
    case SEEK_END:
        if (offset > 0 || f_length + offset < 0)
            return -1;
        f_offset = f_length + offset;
        return f_offset;
    case AVSEEK_SIZE:
        return f_length;
    }

    return -1;
}
int readp(void *opaque, uint8_t *buf, int buf_size)
{
    if (f_offset == f_length)
        return 0;

    int length = buf_size <= (f_length - f_offset) ? buf_size : (f_length - f_offset);

    memcpy(buf, f_data + f_offset, length);
    f_offset += length;

    return length;
}

bool decode(char *data, int length)
{
    f_offset = 0;
    f_length = length;
    f_data = data;

    int buffer_read_size = FF_MIN_BUFFER_SIZE;
    uchar *buffer_read = (uchar *) av_mallocz(buffer_read_size + FF_INPUT_BUFFER_PADDING_SIZE);

    AVProbeData pd;
    pd.filename = "";
    pd.buf_size = 4096 < f_length ? 4096 : f_length;
    pd.buf = (uchar *) av_mallocz(pd.buf_size + AVPROBE_PADDING_SIZE);
    memcpy(pd.buf, f_data, pd.buf_size);

    AVInputFormat *pAVInputFormat = av_probe_input_format(&pd, 1);
    if (pAVInputFormat == NULL)
    {
        std::cerr << "AVIF";
        return false;
    }
    pAVInputFormat->flags |= AVFMT_NOFILE;

    ByteIOContext ByteIOCtx;
    if (init_put_byte(&ByteIOCtx, buffer_read, buffer_read_size, 0, NULL, readp, NULL, seekp) < 0)
    {
        std::cerr << "init_put_byte";
        return false;
    }

    AVFormatContext *pFormatCtx;
    if (av_open_input_stream(&pFormatCtx, &ByteIOCtx, "", pAVInputFormat, NULL) < 0)
    {
        std::cerr << "av_open_stream";
        return false;
    }

    if (av_find_stream_info(pFormatCtx) < 0)
    {
        std::cerr << "av_find_stream_info";
        return false;
    }

    int video_stream;
    video_stream = -1;
    for (uint i = 0; i < pFormatCtx->nb_streams; ++i)
        if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
        {
            video_stream = i;
            break;
        }
    if (video_stream == -1)
    {
        std::cerr << "video_stream == -1";
        return false;
    }

    AVCodecContext *pCodecCtx;
    pCodecCtx = pFormatCtx->streams[video_stream]->codec;

    AVCodec *pCodec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL)
    {
        std::cerr << "pCodec == NULL";
        return false;
    }

    if (avcodec_open(pCodecCtx, pCodec) < 0)
    {
        std::cerr << "avcodec_open";
        return false;
    }

    AVFrame *pFrame;
    pFrame = avcodec_alloc_frame();
    if (pFrame == NULL)
    {
        std::cerr << "pFrame == NULL";
        return false;
    }
    AVFrame *pFrameRGB;
    pFrameRGB = avcodec_alloc_frame();
    if (pFrameRGB == NULL)
    {
        std::cerr << "pFrameRGB == NULL";
        return false;
    }

    int numBytes;
    numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);
    uint8_t *buffer;
    buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    if (buffer == NULL)
    {
        std::cerr << "buffer == NULL";
        return false;
    }

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    SwsContext *swsctx;
    swsctx = sws_getContext(
                pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB32,
                SWS_BILINEAR, NULL, NULL, NULL);
    if (swsctx == NULL)
    {
        std::cerr << "swsctx == NULL";
        return false;
    }

    AVPacket packet;
    while (av_read_frame(pFormatCtx, &packet) >= 0)
    {
        if (packet.stream_index == video_stream)
        {
            int frame_finished;
            avcodec_decode_video2(pCodecCtx, pFrame, &frame_finished, &packet);

            if (frame_finished)
            {
                sws_scale(swsctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

                std::cerr << "good";
                av_close_input_stream(pFormatCtx);

                return true;
            }
            else
                std::cerr << "frame_finished == 0";
        }
    }

    std::cerr << "av_read_frame < 0";
    return false;
}

ffmpeg -версия

FFmpeg 0.6.2-4:0.6.2-1ubuntu1
libavutil     50.15. 1 / 50.15. 1
libavcodec    52.72. 2 / 52.72. 2
libavformat   52.64. 2 / 52.64. 2
libavdevice   52. 2. 0 / 52. 2. 0
libavfilter    1.19. 0 /  1.19. 0
libswscale     0.11. 0 /  0.11. 0
libpostproc   51. 2. 0 / 51. 2. 0

person jjSunny    schedule 21.08.2011    source источник


Ответы (1)


Вы, вероятно, читали какой-нибудь туториал по libav и просто копируете/вставляете почти весь код в свою функцию decode(). Это действительно неправильно. Посмотрите на источник. Каждый раз, когда вы хотите декодировать какой-то кадр — неважно, аудио или видео — вы открываете контекст ввода, инициализируете кодеки и т. д. несколько раз. И каждый раз ты его никак не закрываешь\освобождаешь! Имейте в виду, что даже если вы исправите open\init и close\freeing все вещи, вы будете получать один и тот же кадр каждый раз, когда вы вызываете decode(), потому что этот подход приведет к поиску позиции файла в начале файла каждый раз, когда вы вызываете decode ().

Более того, вы вызываете av_close_input_stream() вместо av_close_input_file(), вы забыли закрыть кодек с помощью avcodec_close(), освободить выделенные изображения с помощью avpicture_free(), освободить выделенные кадры с помощью av_free(), освободить чтение пакетов с помощью av_free_packet(). Кроме того, ваши функции seekp() и readp() тоже могут ошибаться.

Еще один совет: функция func sws_getContext() теперь устарела, и вместо нее следует использовать sws_getCachedContext(). Судя по названию функции в вашем случае (множественные вызовы sws_getContext, но все равно неправильно) она будет работать быстрее.

Пожалуйста, прочитайте еще раз несколько руководств по libav. Все они кажутся устаревшими, но вы можете просто заменить устаревшие или удаленные функции на новые, которые вы можете найти в официальной документации libav doxygen. Вот несколько ссылок:

http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html
http://dranger.com/ffmpeg/ffmpeg.html

Актуальные примеры вы найдете в официальной документации API libav.

http://libav.org/doxygen/master/examples.html

Они объясняют наиболее распространенные варианты использования.

person Roman Kruglov    schedule 05.09.2011