LINUX.ORG.RU

Не получается декодирвоать h264 по кадрам

 , , ,


1

4

День добрый. Не получается читать h264 видео по кадрам. Ругается avcodec_decode_video2, ошибочка: Invalid data found when processing input

Возможно дело в том, что h264 не поддерживает AV_CODEC_CAP_TRUNCATED, а может и нет (внутри av_read_frame это должно обходиться, как я полагаю).

Код: https://github.com/RussianBruteForce/ffmpeg_opencv

Запускать:

./cv файл

cast Norgat

# скачать нерабочий вариант одним файлом:
wget http://paste.omsklug.com/6772/raw/ -o kek.cpp
# собрать&запустить
g++ kek.cpp -std=c++14 -lavutil -lavcodec -lavformat -lswscale && ./a.out file.name.here

--------------------------------------------------------------

Всё починилось после добавления этой строчки.

avcodec_parameters_to_context(dec_ctx.get(), st->codecpar);

★★★

Последнее исправление: BruteForce (всего исправлений: 3)

Самый простой способ разобраться — пересобрать ffmpeg с отладкой, найти то место, где генерируется ошибка, и выяснить, что именно это ошибку вызывает. Дальше разматывать вызовы в обратном направлении.

i-rinat ★★★★★
()
Ответ на: комментарий от RazrFalcon

Неа, не хорошо. По крайней мере я не въехал, отличий от своего кода вроде тоже не нашёл :c

BruteForce ★★★
() автор топика
Ответ на: комментарий от BruteForce

Кстати, у тебя код ждёт, что got_frame будет не 0. А ведь этот параметр не зря отдельно, а не как код ошибки. Вызов avcodec_decode_video2() может не дать готового фрейма, и это нормально.

Не помню деталей, но вот что помню, так это кадры из нескольких слайсов. В общем, нет гарантии, что каждый слайс даст тебе декодированный кадр или полукадр.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Кстати, у тебя код ждёт, что got_frame будет не 0. А ведь этот параметр не зря отдельно, а не как код ошибки. Вызов avcodec_decode_video2() может не дать готового фрейма, и это нормально.

Это первое, что я попробовал — начал игнорировать ошибку и делать av_read_frame дальше — на все фреймы ответ h264 один — not frame.

А got_packet это если есть, что декодировать. До проверки его дело не доходит, но я уверен, что он всегда будет 0 при таком говне.

BruteForce ★★★
() автор топика
Ответ на: комментарий от BruteForce

av_read_frame дальше — на все фреймы ответ h264 один — not frame.

Думаю, он ждёт NAL unit header или как их там. Они у тебя в данных есть?

i-rinat ★★★★★
()

А я знаю, в чем дело, но не скажу, потому что больно уж провокационную аватару надел топикстартер.

anonymous
()

Содержимое Video::init_stream() намекает, что потоков много. Но при декодировании ВСЕ потоки льёшь в ОДИН декодер.

У AVPacket есть stream_index. Его надо анализировать, и как-нибудь реагировать.

fopen ★★
()
Ответ на: комментарий от anonymous

Ск пздц, даже не знаю, это ты меня или я тебя затролел!

BruteForce ★★★
() автор топика
Ответ на: комментарий от fopen

Там стрим с видево под индексом ноль, но даже если я перекодирую видосик и убираю от туда аудио вообще — толку нет. Сравнения pkt->stream_index с video_ctx->index я делал (оба ноль, ну), результат тот же.

BruteForce ★★★
() автор топика
Ответ на: комментарий от BruteForce

Понятия не имею

Придётся заиметь.

H.264 пакуют по-разному, но тебе нужно оттуда доставать по одному слайсу, так как декодер на частичных данных не работает. Где-то бывает NAL, по которым определяются границы, где-то в контейнере уже порезанные данные, и там NAL нет. Если ты просишь ffmpeg найти NAL там, где его нет, он его не найдёт.

i-rinat ★★★★★
()
Ответ на: комментарий от BruteForce
		if (pkt->data) {
			pkt->size -= len;
			pkt->data += len;
		}

AVPacket тебе не принадлежит, ты его портишь. Здесь, возможно, это не критично.

Включи отладочные сообщения, покажи, что происходит.

fopen ★★
()

Во-первых, зачем тебе свой AVIOContext? Выкинь. Скорее всего, тут напортачил. Стандартый подойдет более чем.

Во-вторых, зачем ты пользуешься старым API? Т.е. avcodec_decode_video2, не те free-функции т.д. Выкинь.

В-третьих, зачем ты лезешь в структурки контекстов FFmpeg руками?

В-четвертых, ничего плохого в одном контексте кодека для нескольких стримов нет, во многих случаях, но надо чтобы по каждому фрейму каждый раз проходил полный цикл раскодирования.

Вот пример программы, которая читает покадрово:

#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
}
#endif

#define NEEDED_FORMAT AV_PIX_FMT_GRAY8

int main(int argc, char** argv)
{
    int rv = 0, vstream, width, height, outSize;
    unsigned int i;
    AVFrame *frame = NULL;
    uint8_t * out[4] = { 0 };
    int outLinesize[4] = { 0 };
    AVPacket pkt;
    AVFormatContext * fmt = NULL;
    AVCodecContext * ctx = NULL;
    AVCodec * decoder = NULL;
    AVCodecParameters * codecpar = NULL;
    struct SwsContext * sws = NULL;
    const char * filename;
    char err[512];

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        return -1;
    }
    filename = argv[1];

    // init FFmpeg
    av_register_all();
    avcodec_register_all();
    avformat_network_init();
    av_log_set_level(AV_LOG_DEBUG);

    // init packet with default values
    av_init_packet(&pkt);

    // alloc temporary frame
    frame = av_frame_alloc();
    if (!frame) { rv = AVERROR(ENOMEM); goto exit; }

    // open input file/stream
    rv = avformat_open_input(&fmt, filename, NULL, NULL);
    if (rv < 0) goto exit;

    // find streams
    rv = avformat_find_stream_info(fmt, NULL);
    if (rv < 0) goto exit;

    // log input streams
    av_dump_format(fmt, 0, filename, 0);

    // find video stream
    for (i = 0; i<fmt->nb_streams; ++i)
    {
        if (fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
            !codecpar)
        {
            vstream = i;
            codecpar = fmt->streams[i]->codecpar;
            width = codecpar->width;
            height = codecpar->height;
            break;
        }
    }
    if (!codecpar) { rv = AVERROR_STREAM_NOT_FOUND; goto exit; }

    // allocate sws context
    sws = sws_getContext(width, height, codecpar->format, width, height, NEEDED_FORMAT, SWS_POINT, NULL, NULL, NULL);
    if (!sws) { rv = AVERROR(ENOMEM); goto exit; }

    // find decoder description
    decoder = avcodec_find_decoder(codecpar->codec_id);
    if (!decoder) { rv = AVERROR_DECODER_NOT_FOUND; goto exit; }

    // alloc decoder context
    ctx = avcodec_alloc_context3(decoder);
    if (!ctx) { rv = AVERROR(ENOMEM); goto exit; }

    // copy codec parameters to context
    rv = avcodec_parameters_to_context(ctx, codecpar);
    if (rv < 0) goto exit; 

    // open decoder context
    rv = avcodec_open2(ctx, decoder, NULL);
    if (rv < 0) goto exit;

    // alloc data for output frame
    rv = av_image_get_buffer_size(NEEDED_FORMAT, width, height, 1);
    if (rv < 0) goto exit;
    outSize = rv;
    out[0] = (uint8_t*)av_malloc((size_t)outSize);
    if(!out[0]) { rv = AVERROR(ENOMEM); goto exit; }
    rv = av_image_fill_arrays(out, outLinesize, out[0], NEEDED_FORMAT, width, height, 1);
    if (rv < 0) goto exit;
    
    // frame read cycle
    while ((rv = av_read_frame(fmt, &pkt)) >= 0) // read packet from input
    {
        // ignore audio
        if (pkt.stream_index == vstream)
        {
            // send packet to decoder
            rv = avcodec_send_packet(ctx, &pkt);
            if (rv >= 0)
            {
                // receive frame from decoder
                while ((rv = avcodec_receive_frame(ctx, frame)) >= 0)
                {
                    // convert colorspace
                    rv = sws_scale(sws, frame->data, frame->linesize, 0, height, out, outLinesize);
                    if (rv >= 0)
                    {
                        // Now 'out[0]' contains first plane data
                    }
                }
            }
        }
        av_packet_unref(&pkt);
    }

    rv = 0;
exit:
    if (rv < 0)
    {
        av_strerror(rv, err, sizeof(err));;
        fprintf(stderr, "Error: %s\n", err);
    }
    if (out[0]) { av_freep(&out[0]); };
    if (frame) { av_frame_free(&frame); }
    if (ctx) { avcodec_free_context(&ctx); }
    if (fmt) { avformat_close_input(&fmt); }
    if (sws) { sws_freeContext(sws); }
    return rv < 0 ? rv : 0;
}

lovesan ★★
()

Плюс, если у тебя opencv, или что там другое, что умеет в аппаратное ускорение, то не надо руками на процессоре colorspace conversion делать, надо использовать ffmpeg-овский HWAccel, который умеет аппаратно декодировать h264, сразу в yuv текстуру на видеокарту, и там уже с ней работать потом.

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

:c

/tmp/main.c:27: error: unknown type name ‘AVCodecParameters’
  AVCodecParameters * codecpar = NULL;
  ^

Потыкаю когда просплюсь. Премного благодарен за ассист.

BruteForce ★★★
() автор топика
Последнее исправление: BruteForce (всего исправлений: 2)
Ответ на: комментарий от lovesan

Да, у меня с гита последний снапшот собран был. Опечатка была в инклуд-паф, благодарю — полез смотреть, заметил, исправил.

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