Хабрахабр

FFmpeg практика аппаратного декодирования DXVA2

Привет! Эта статья продолжение моей статьи FFmpeg начало работы Visual Studio. Здесь мы приступим к аппаратному декодированию RTSP-потока FULL HD. Заранее скажу, что с данной задачей легко справится даже Intel ATOM Z8350.

264. Задача: аппаратное декодирование и запись до 4-х кадров в оперативную память для последующей параллельной обработки (четырьмя ядрами процессора) с IP-камеры RTSP h. Как итог мы получим быстродействующую систему для компьютерной обработки RTSP-потока в параллельном режиме. Обработанные кадры отображаю с помощью функций WinAPI. Далее можно подключать алгоритмы Компьютерного зрения для обработки кадров Real Time.

image

Вступление

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

DXVA 2. DirectX Video Acceleration (DXVA) — это API для использования аппаратного ускорения для ускорения обработки видео силами графических процессоров (GPU). Основная сложность аппаратного декодирования состоит в записи раскодированного кадра в ОЗУ. 0 позволяет перенаправлять на GPU большее количество операций, включая захват видео и операции обработки видео.
После написания предыдущей статьи мне было задано не мало вопросов: «почему использован именно FFmpeg?» Начну с проблематики. Даже с учетом того что кадр хранится в формате NV12 – это тоже немало 1920 x 1080 x 1. Для Full HD это 1920 х 1080 х 3 = 6 220 800 байт. Перезаписывать 75 Мбайт в секунду серьезная задача для любого процессора. 5 = 3 110 400 байт. К сожалению, не во всех библиотеках это реализовано. Для решения этой задачи Intel добавила команды SSE 4, которые позволяют переписывать данные без участия процессора. Мной были опробованы следующие библиотеки:

  1. FFmpeg
  2. VLC
  3. OpenCV

VLC – работает с IP-камерами через аппаратное декодирование (очень низкая загрузка процессора), примитивный проигрыватель потока RTSP можно построить буквально в 10 строк кода, но получение декодированных кадров в оперативную память занимает слишком много процессорного времени.

использовать библиотеку FFmpeg. OpenCV – для работы с потоком RTSP использует FFmpeg, поэтому решено работать без посредников, т.е. К тому же FFmpeg, который установлен по умолчанию, в OpenCV собран без аппаратного декодирования.

Единственный недостаток не реализована работа с WEB-камерами для версии X86 (X64 вроде позволяет работать) в Windows. FFmpeg – показала хорошие, на мой взгляд результаты, работает стабильно.

Аппаратное декодирование видео — это просто

На самом деле аппаратное декодирование с помощью библиотеки FFmpeg — не сложнее программного. Настройки проекта такие же, как и для программной реализации, блок-схема осталась без изменений.

Вывести на экран список поддерживаемых FFmpeg методов аппаратного декодирования можно

fprintf(stderr, " %s", av_hwdevice_get_type_name(type));

Первое, что нам нужно сделать — это сообщить FFmpeg с помощью какого аппаратного декодера Вы хотите декодировать видео. В моем случае Windows10 + Intel Atom Z8350 оставляют только DXVA2:

type = av_hwdevice_find_type_by_name("dxva2");

Вы же в качестве аппаратного декодера можете выбрать CUDA, D3D11VA, QSV или VAAPI (только Linux). Соответственно у вас должно быть данное аппаратное решение и FFmpeg должен быть собран с его поддержкой.

Открываем видеопоток:

avformat_open_input(&input_ctx, filename, NULL, NULL;

Получаем информацию о видеопотоке:

av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);

Выделяем память:


frame = av_frame_alloc(); // здесь хранится декодированный файл вне ОЗУ
sw_frame = av_frame_alloc(); // Сюда мы перепишем декодированный файл в ОЗУ

Данная функция переписывает декодированный файл в ОЗУ:

av_hwframe_transfer_data(sw_frame, frame, 0);

Немного о формате NV12

Итак, мы получили кадр в структуру sw_frame. Полученный кадр хранится в формате NV12. Данный формат был придуман Microsoft. Он позволяет хранить информацию о пикселе в 12 бит. Где 8 бит интенсивность, а 4 битами описывается цветность (вернее цветность сразу описывается для 4-х рядом стоящих пикселей 2х2). Причем, sw_frame.data[0] – хранится интенсивность, а в sw_frame.data[1] – хранится цветность. Для перевода из NV-12 в RGB можете воспользоваться следующей функцией:

C++ перевод из NV12 в RGB

void SaveFrame(uint8_t * f1, uint8_t * f2, int iFrame) { FILE *pFile; char szFilename[32]; int x, i, j;
// char buff[1920 * 1080 * 3]; uint8_t *buff = new uint8_t(1920*3*2); int u=0, v=0, y=0; // Open file sprintf(szFilename, "frame%d.ppm", iFrame); pFile = fopen(szFilename, "wb"); if (pFile == NULL) return; // Записуем заголовок файла fprintf(pFile, "P6\n%d %d\n255\n", 1920, 1080); for (j = 0; j < 1080 / 2; j++) { for (i = 0; i < 1920; i +=2) { // 1 точка rgb y = *(f1 + j * 1920 * 2 + i); v = *(f2 + j * 1920 + i) - 128; u = *(f2 + j * 1920 + i + 1) - 128; x = round(y + 1.370705 * v); if (x < 0) x = 0; if (x > 255) x = 255;
// if (j > 34) printf("%i, ",(j * 1920 * 2 + i) * 3); buff[i * 3 + 2] = x; x = round(y - 0.698001 * v - 0.337633 * u); if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 1] = x; x = round(y + 1.732446 * u); if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3] = x; // 2 точка rgb y = *(f1 + j * 1920 * 2 + i + 1); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 5] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 4] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 3] = x; // 3 точка rgb y = *(f1 + j * 1920 * 2 + 1920 + i); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 2] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 1] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 0] = x; // 4 точка rgb y = *(f1 + j * 1920 * 2 + 1920 + i + 1); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 5] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 4] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 3] = x;
// printf("%i, ", i); } // for i fwrite(buff, 1, 1920 * 3 * 2, pFile); printf("\n %i\n", j); } // for j
// printf("Save4\n"); // Write pixel data
// fwrite(buff, 1, 1920*1080*3, pFile); // Close file printf("close\n"); fclose(pFile); printf("exit\n");
// delete buff;
// return;
}

Хотя работа с NV12 позволяет ускорить выполнение таких процедур, как размывка, Retinex и получение изображения в оттенках серого (просто отбросив цветность). В моих задачах я не перевожу формат NV12 в RGB, так как это занимает дополнительное время.

Познакомились в форматом NV12 и как его преобразовывать в привычный RGB. И так мы научились аппаратно декодировать видеофайлы и выводить их в окно.

Dll аппаратного декодирования

Кадры FFmpeg выдает через 40 мс (при 25 кадрах в секунду). Как правило, обработка кадра Full HD занимает значительно больше времени. Для этого требуется организовать многопоточность, для максимальной загрузки всех 4-х ядер процессора. Я на практике один раз запускаю 6 потоков и больше их не снимаю, что значительно упрощает работу и увеличивает надежность работы программы. Схема работы приведена на рис. 1

1 Схема построения многопоточной программы с FFmpeg image
рис.

DLL) для включения в свои проекты. Я написал свой декодер в виде *.dll (FFmpegD. С помощью нее мы напишем свой проигрыватель RTSP-потока с IP-камеры. Это позволяет сократить код-проекта, что повышает понимание кода и включать в любые языки программирования, вплоть до Ассемблера (проверено:) ).

Для начала работы с DLL нужно передать указатель массив int[13], HANDLE события поступления нового кадра, HANDLE начала обработки нового пакета данных с камеры и массив char адрес камеры.

Структура массива дана в таблице 1.

image

Перед вызовом необходимо обнулить номера кадров 1-4.

После установит событие «Поступление нового кадра». DLL выполнит все необходимые действия по инициализации FFmpeg и будет записывать указатели и номера кадров. Нужно только обрабатывать поступающие кадры и вместо номера кадра записывать 0 (это значит кадр обработан и больше не используется).

За основу взят пример ShowDib3 Charles Petzold. Ниже Вы найдете пример проигрывателя с исходным кодом.

→ Архив с проектом
→ Архив FFmpegD.dll

ИТОГ: аппаратный детектор движения FFmpeg даже на Intel Atom Z8350 декодирует h264 Full HD в реальном времени с загрузкой процессора до 20% с подключенный детектором движения.

Пример работы детектора движения на Intel ATOM Z8350. Первые 30 сек идет подсчет фона. После работает детектор движения по методу вычитания фона.

S. Так же можете декодировать и видеофайлы (сжатые h. P. 264)!!!

Ссылки:

  1. Разная полезная информация по FFmpeg
  2. Информация по использованию различных библиотек, предоставляемых FFmpeg
  3. Информация о форматах и переводе в RGB
Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть