LINUX.ORG.RU

libav*


0

1

Использую libav* для сохранения кадров из видео.
Проблема в том, что если вызвать функцию decode несколько раз, то 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);

video.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;
    }

    // Выделяем память под YUV видео фрейм
    AVFrame *pFrame;
    pFrame = avcodec_alloc_frame();
    if (pFrame == NULL)
    {
        std::cerr << "pFrame == NULL";
        return false;
    }
    // И под RGB фрейм
    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 -version

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

какой видеокодек используется в видео-файле?

сори что влезаю, но как раз сейчас разбираюсь с libav*, надо из RGB делать YUV420 и наоборот... какие функции ты вызываешь для этого? «swscaler from yuv420p to bgra» я про это

I-Love-Microsoft ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

Кодек H.264 / AVC

Внутри libav фреймы хранит в YUV формате, поэтому туда никаких телодфижений не надо, если не специфический YUV.

Чтобы в другой формат сконвертировать

//выделяем память под фрейм
AVFrame *pFrameF = avcodec_alloc_frame();

//Определяем требуемый размер буфера и выделяем под него память
//Вместо PIX_FMT подставить необходимый формат
int numBytes = avpicture_get_size(PIX_FMT_, pCodecCtx->width, pCodecCtx->height);
uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

avpicture_fill((AVPicture *) pFrameF, buffer, PIX_FMT_, pCodecCtx->width, pCodecCtx->height);

SwsContext *swsctx = sws_getContext(
pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, PIX_FMT_,
SWS_BILINEAR, NULL, NULL, NULL);

//получаем фрем из видео
...

//трансофрмация
sws_scale(swsctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameF->data, pFrameF->linesize);

Kristi ()
Ответ на: комментарий от Kristi

если надо из RGB в YUV420 или наоборот, то тут везде где PIX_FMT_ надо ставить PIX_FMT_RGB24?

по теме: для avcodec_decode_video2 не требуется рассчитывать вручную dts pts? все берет на себя AVPacket?

I-Love-Microsoft ★★★★★ ()
Ответ на: комментарий от I-Love-Microsoft

PIX_FMT_ - это в какой формат конвертировать

pCodecCtx->pix_fmt - из какого

Про ручной расчет dts, pts ничего не встречал. Хотя я и сам изучаю libav всего несколько дней, поэтому могу и заблуждаться.

Kristi ()
Ответ на: комментарий от I-Love-Microsoft

> по теме: для avcodec_decode_video2 не требуется рассчитывать вручную dts pts? все берет на себя AVPacket?

Демуксер заполняет эти поля в пакете. Декодер их может использовать. Для декодеров есть рекомендация опираться на предельно меньшее количество входных параметров. H.264 по факту всё корректно декодирует, если подсовывать ему только данные.

const86 ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.