Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / Артефакты при декодирование видео через ffmpeg [решено]

Артефакты при декодирование видео через ffmpeg [решено]

GrobozavrПостоялецwww9 апр. 201811:58#0
Возникла тут непонятная проблема:
При декодировании видео оно портится. Выглядит как постепенное (или не очень) циклическое смещение картинки вправо. Эффект постоянен для конкретного видео (по крайней мере на глаз), но очень сильно меняется между ними. Иногда практически незаметно, иногда как на старом телевизоре со сбитой частотой, когда картинка за время кадра прокручивается несколько раз по горизонтали. Картинку брал сохранением фрейма в файл куском из примеров самого ffmpeg.

Причём портится не только само видео, но, похоже, и внутр. структуры. Скалер при работе с таким кадром просто падает. OpenCV тоже.
Что я такое страшное с ним сотворил?

При этом старый исходник вполне нормально работает (собственно пытался его отрефакторить). Никаких различий в логике не нашел. С примерами из сдк тоже.
Я понимаю, если бы оно вообще не работало, но этот эффект непонятен совершенно.

Декодится как-то так

+ Показать

RedCatПостоялецwww10 апр. 20184:45#1
А какие настройки sws_ctx ?
GrobozavrПостоялецwww10 апр. 20188:23#2
А что там так повлиять могло? Там же только размеры и формат пиксела задаётся. Тем более, что это скалер, а до него дело не доходит.
Испорченную картинку возвращает уже avcodec_decode_video2

Примерно так это выглядит
ломаный кадр | Артефакты при декодирование видео через ffmpeg [решено]

Правка: 10 апр. 2018 8:55

GrobozavrПостоялецwww16 апр. 201810:54#3
Похоже код из примера с сохранением кадра кривой, т.к. и в рабочем коде сохраняет то же самое. Что странно.

Кто-нибудь в курсе, что такое AVFrame.linesize? По описанию это размер плоски (т.е. ширина*на кол-во компонент)
Но размер этого параметра гуляет от запуска к запуску даже в рабочем коде (на одном и том же видео) и в десяток тысяч раз больше ширины кадра и нацело на него не делится. О_О
Каким-то образом оно всё-таки работает в старом коде, но в новом намертво роняет скалер или опенцв при любой работе с кадром :(

GrobozavrПостоялецwww16 апр. 201814:06#5
Всё бы ничего, но вот его значения для одного и того же видео 5 запусков подряд. Видео 1920*1080

6217632
10870688
6217632
7921568
37085088

Оно даже нацело не делится на ширину.

Правка: 16 апр. 2018 14:07

gkv311Постоялецwww16 апр. 201816:36#6
Grobozavr
Всё бы ничего, но вот его значения для одного и того же видео 5 запусков подряд. Видео 1920*1080

6217632
10870688
6217632
7921568
37085088


Что это за цифры? linesize[0] или linesize[0], linesize[1], linesize[2]?
Какой пиксельный формат у видео потока?
Декодится как-то так

Этого кода недостаточно, чтобы чем-то помочь.
Даже кода и места инициализации swscale контекста нет.
Причём портится не только само видео, но, похоже, и внутр. структуры. Скалер при работе с таким кадром просто падает.

Очевидно, что ошибка  где-то в вашем коде (возможно, портится память), если уронить ffmpeg.exe не удаётся на том же видеофайле.

Правка: 16 апр. 2018 16:37

Ghost2Постоялецwww16 апр. 201816:42#7
Grobozavr

Как создаешь v->dec_frame?

GrobozavrПостоялецwww17 апр. 20189:58#8
>Что это за цифры? linesize[0] или linesize[0], linesize[1], linesize[2]?
Написано же - 5 запусков подряд, 5 чисел. [0] естественно, остальные по нулям.
Забыл уточнить - это числа из рабочего варианта. В нерабочем примерно так же выглядят. Вообще судя по картинке, которую дампил на диск - декодирование всё-таки работает одинаково (т.е. в рабочем она почему-то такая же кривая).

>Даже кода и места инициализации swscale контекста нет.
Про "место инициализации" не понял.

Код брал (вроде) из какого-то пример идущего в комплекте с ffmpeg

const AVPixelFormat dst_pix_fmt = AV_PIX_FMT_BGR24;

v->scaled_frame = av_frame_alloc();
std::vector<uint8_t> framebuf(avpicture_get_size(dst_pix_fmt, v->dst_width, v->dst_height));
avpicture_fill(reinterpret_cast<AVPicture*>(v->scaled_frame), framebuf.data(), dst_pix_fmt, v->dst_width, v->dst_height);

v->sws_ctx = sws_getCachedContext( nullptr, vstrm->codec->width, vstrm->codec->height, vstrm->codec->pix_fmt, 
            v->dst_width, v->dst_height, dst_pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);

ret = sws_scale(v->sws_ctx, v->dec_frame->data, v->dec_frame->linesize, 0, v->dec_frame->height, v->scaled_frame->data, v->scaled_frame->linesize);

Сейчас передрал код из рабочего примера один в один - теперь скалер не падает (хотя убей бог не пойму, в чём разница) Однако opencv картинку отображает сильно поехавшую (такое впечатление, что ссылка на данные ведёт на хрендцать килобайт раньше, чем начинается собственно картинка.) Т.е. сверху мусор, а дальше начало кадра с поехавшими цвета.

Собственно где тут можно было так ложануться я не понимаю.

v_in->frame_img[ nc ] = new cv::Mat( v_in->dst_height, v_in->dst_width, CV_8UC3, v_in->scaled_frame->data, v_in->scaled_frame->linesize[0]);
cv::imshow("input-1", *v_in->frame_img[ nc ]);

В процессе работы иногда падает уже opencv.

>Очевидно, что ошибка где-то в вашем коде (возможно, портится память), если уронить ffmpeg.exe не удаётся на том же видеофайле.
Более чем очевидно, учитывая, что ,как писал уже вначале, старый код на этом же файле работает нормально. Но каких-то принципиальных отличий в новом я найти не могу. И понять, где оно вообще может портиться тоже не получается. Слишком много промежуточных шагов, результаты которых я не могу проконтролировать.
На самом деле я практически 100% уверен, что тут где-то какая-то тупая опечатка, которую я уже вторую неделю не могу найти. :(


Ghost2
av_frame_alloc();
а дальше он сам с ним разбирается. явно память только для скалера выделяю.


upd: Непонятно. Если дампить картинку после скалера - начало у неё не сдвинуто. Т.е. тупит opencv, похоже

Правка: 17 апр. 2018 10:28

gkv311Постоялецwww17 апр. 201810:36#9
Grobozavr
аже кода и места инициализации swscale контекста нет.

Про "место инициализации" не понял.
v->sws_ctx = sws_getCachedContext( nullptr, vstrm->codec->width, vstrm->codec->height, vstrm->codec->pix_fmt, 
v->dst_width, v->dst_height, dst_pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr);

Какая версия FFmpeg? Почему размеры кадра берутся из кодека (deprecated поле и некорректно), а не из декодированного кадра (в общем случае, размер кадра может меняться в видеопотоке)?
std::vector<uint8_t> framebuf(avpicture_get_size(dst_pix_fmt, v->dst_width, v->dst_height));
avpicture_fill(reinterpret_cast<AVPicture*>(v->scaled_frame), framebuf.data(), dst_pix_fmt, v->dst_width, v->dst_height);

Надеюсь декодирование делается в той же функции (да даже если и так - всё равно код нехорошо обращается с памятью)?
Иначе framebuf разрушится и v->scaled_frame будет указывать на освобождённую память.
v_in->frame_img[ nc ] = new cv::Mat( v_in->dst_height, v_in->dst_width, CV_8UC3, v_in->scaled_frame->data, v_in->scaled_frame->linesize[0]);
cv::imshow("input-1", *v_in->frame_img[ nc ]);

Я не знаю, что берёт на вход cv::Mat, но v_in->scaled_frame->data - это указатель на массив плоскостей, как и v_in->scaled_frame->linesize; может имелось в виду v_in->scaled_frame->data[0]?

Правка: 17 апр. 2018 10:37

GrobozavrПостоялецwww17 апр. 201811:15#10
>Какая версия FFmpeg?
Январская

>Почему размеры кадра берутся из кодека (deprecated поле и некорректно), а не из декодированного кадра (в общем случае, размер кадра может меняться в видеопотоке)?
1. потому, что так было в найденном примере
2. Это инициализация, которая идёт ДО декодирования первого кадра. Там во первых нет другого источника, а во вторых значение всё равно не поменяется. Вообще про то, что размер кадра может меняться в потоке впервые слышу. Тогда надо, получается, отслеживать это в процессе и перенастраивать скалер? Собственно это неважно. В таком случае там весь мой код навернётся, т.к. такой подставы не предполагается.

>Надеюсь декодирование делается в той же функции (да даже если и так - всё равно код нехорошо обращается с памятью)?
Этот кусок был содран из найденного примера один в один. Да, тут я тупанул. 
Хотя это и не оно.  Переделал - ничего не изменилось
Я правильно понимаю, что память, которую я ему выделил отдельно потом освободит av_frame_free?

>Я не знаю, что берёт на вход cv::Mat, но v_in->scaled_frame->data - это указатель на массив плоскостей, как и v_in->scaled_frame->linesize; может имелось в виду v_in->scaled_frame->data[0]?
О дааааа! Вот как раз и она, спасибо огромное.
Там раньше был как раз framebuf->data, только он отсюда теперь недоступен.
Эту штуку я ещё бы сто лет искал. :(

gkv311Постоялецwww17 апр. 201812:06#11
Grobozavr
Вообще про то, что размер кадра может меняться в потоке впервые слышу. Тогда надо, получается, отслеживать это в процессе и перенастраивать скалер?

Да, именно так. Но это поддерживается не всеми видеокодеками, и используется редко (например, в онлайн видеотрансляциях).
Поэтому можно не заморачиваться, если не пишешь полноценный плеер.
Я правильно понимаю, что память, которую я ему выделил отдельно потом освободит av_frame_free?

Нет, он будет указывать на память, аллоцированную в std::vector.
av_frame_free освободит только память, аллоцированную через av_frame_alloc (то есть только на саму структуру).
В FFmpeg много всяких вариантов и нюансов работы с памятью - avpicture_fill() один из артефактов прошлого, который только запутывает.

Правка: 17 апр. 2018 12:07

GrobozavrПостоялецwww17 апр. 201812:22#12
В онлайне - ну да, там имеет смысл. Мне не надо, но всё равно учту на будущее.

>Нет, он будет указывать на память, аллоцированную в std::vector.
Блин, в доках написано
>Free the frame and any dynamically allocated objects in it, e.g.

Там вообще дофига всего не очевидного. Разбираться приходится методом тыка и копипаста.

/ Форум / Программирование игр / Общее

2001—2018 © GameDev.ru — Разработка игр