С приходом тяжеловесных видеоформатов, таких как 4K (Ultra HD), проблема эффективности декодирования видеопотока стала достаточно актуальной. На среднем компьютере приходится принимать специальные меры для того, чтобы можно было обработать такой видеопоток в реальном масштабе времени. В статье рассказывается о возможных способах увеличения скорости декодирования видеопотоков в решениях, основанных на FFmpeg, и приводятся результаты экспериментов по измерению скорости декодирования для 4K видеопотоков, закодированных в H264 и HEVC(H265).
Оглавление
1. Три способа повышения скорости декодирования видеопотока
1.1. Подключение дополнительных рабочих потоков в стандартных декодерах
1.2. Подключение аппаратного ускорения в стандартных декодерах
1.3. Использование специальных декодеров, реализующих декодирование на графических процессорах
2. Измерение скорости декодирования
3. Замечания о QSV декодерах
Ресурсы
1. Три способа повышения скорости декодирования видеопотока
Мы будем рассматривать три способа повышения скорости декодирования видеопотока.
- Подключение дополнительных рабочих потоков (threads) в стандартных декодерах.
- Подключение аппаратного ускорения (HW Acceleration) в стандартных декодерах.
- Использование специальных декодеров, реализующих декодирование на графических процессорах.
Первый из них использует исключительно возможности CPU, а два других задействуют возможности графических процессоров.
Доступные способы повышения скорости декодирования видеопотока достаточно сильно зависят от используемой операционной системы, аппаратной конфигурации компьютера и конфигурации FFmpeg. Все приведенные в статье результаты проверялись на следующей программно-аппаратной конфигурации: операционная система — Windows 10, ЦП — Intel i5 8400 2.80 ГГц (6 ядер без hyper-threading), встроенный графический процессор — Intel UHD Graphics 630, память — 16 ГБ, сборка FFmpeg 4.2.1, с zeranoe.
Для лучшего понимания архитектуры кодеков в FFmpeg можно посмотреть предыдущую статью автора, которая находится здесь.
1.1. Подключение дополнительных рабочих потоков в стандартных декодерах
Многие декодеры (но, конечно, не все) позволяют установить количество рабочих потоков, используемых для декодирования. Для этого перед вызовом avcodec_open2() член thread_count структуры AVCodecContext надо установить в требуемое значение. Другой способ — добавить опцию threads в словарь опций, передаваемый в качестве третьего аргумента в avcodec_open2().
Наиболее популярные декодеры, используемые для тяжелых форматов, (h264, hevc, vp9) поддерживают эту возможность, а вот theora нет.
Для подключения дополнительных потоков в командной строке надо использовать ключ -threads.
1.2. Подключение аппаратного ускорения в стандартных декодерах
FFmpeg позволяет для некоторых декодеров подключить аппаратное ускорение. При программировании с использованием FFmpeg API все необходимое для подключения к декодерам аппаратного ускорения находится в заголовочном файле libavutil/hwcontext.h. В этом файле определено перечисление enum AVHWDeviceType, каждый элемент которого и соответствует некоторому типу аппаратного ускорения. Какие типы аппаратного ускорения доступны в текущей сборке FFmpeg можно узнать с помощью следующего кода:
void print_hwtypes_all()
{
AVHWDeviceType hwtype = AV_HWDEVICE_TYPE_NONE;
while ((hwtype = av_hwdevice_iterate_types(hwtype)) !=
AV_HWDEVICE_TYPE_NONE)
{
printf("%s\n", av_hwdevice_get_type_name(hwtype));
}
}Для описанной выше программно-аппаратной конфигурации мы получим:
cuda
dxva2
qsv
d3d11va
Понятно, что cuda требует установки платы Nvidia и соответствущего ПО, qsv использует технологию Intel Quick Sync Video (QSV), реализованную на встроенных графических процессорах Intel (см. [1]), dxva2 и d3d11va используют технологию DirectX Video Acceleration (см. [1]), доступную только в Windows.
Конкретные декодеры не обязаны поддерживать все эти типы аппаратного ускорения (или хотя бы один из них). Для определения типов, поддерживаемых конкретным декодером, можно воспользоваться следующим кодом:
void print_hwtypes(const AVCodec *decoder)
{
for (int i = 0; ; ++i) {
const AVCodecHWConfig *config =
avcodec_get_hw_config(decoder, i);
if (config) {
if (config->methods &
AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
printf("%s\n",
av_hwdevice_get_type_name(config->device_type));
}
}
else {
break;
}
}
}Для описанной выше программно-аппаратной конфигурации декодеры h264, hevc, vp9, vc1 поддерживают следующие типы аппаратного ускорения:
dxva2
d3d11va
cuda
А вот theora вообще не поддерживает аппаратного ускорения.
Теперь совсем кратко рассмотрим процедуру подключения к декодеру аппаратного ускорения, за дополнительными деталями надо обратится к примеру hw_decode.c. Также можно посмотреть статью [3], написанную 2expres.
void init_hwdevice(AVHWDeviceType hwtype, AVCodecContext *codec_ctx)
{
AVBufferRef *dev_ctx = NULL;
int ret = av_hwdevice_ctx_create(&dev_ctx, hwtype, NULL, NULL, 0);
if (ret >= 0) {
codec_ctx->get_format = get_hw_format; // см. hw_decode.c
codec_ctx->hw_device_ctx = av_buffer_ref(dev_ctx);
// сохранить dev_ctx, чтобы освободить после декодирования
// с помощью av_buffer_unref()
}
}После декодирования данные кадра находится в некотором специальном формате, который определяется типом устройства, поэтому его надо конвертировать в один из обычных пиксельных форматов с помощью функции av_hwframe_transfer_data(). Для dxva2 и d3d11va этот формат будет NV12.
Для подключения аппаратного ускорения в командной строке надо использовать ключ -hwaccel.
1.3. Использование специальных декодеров, реализующих декодирование на графических процессорах
В состав FFmpeg входят два семейства кодеков, реализующих кодирование и декодирование на графических процессорах.
Одно семейство использует технологию Intel Quick Sync Video (QSV), реализованную на видеопроцессорах, интегрированных в процессоры Intel семейств i3, i5, i7, i9. Подробнее см. [1]. Эти кодеки имеют суффикс _qsv. В рассматриваемой сборке FFmpeg есть следующие декодеры: h264_qsv, hevc_qsv, vp8_qsv, mpeg2_qsv, vc1_qsv.
Другое семейство использует технологии NVDEC, NVENC реализованные на платах Nvidia. Декодеры имеют суффикс _cuvid. В рассматриваемой сборке FFmpeg есть следующие декодеры: h264_cuvid, hevc_cuvid, mpeg2_cuvid, vc1_cuvid, vp8_cuvid, vp9_cuvid, mjpeg_cuvid, mpeg4_cuvid.
После открытия входного потока доступ к декодеру обычно реализуется следующим образом:
AVStream *strm;
// ...
AVCodecID cid = strm->codecpar->codec_id;
const AVCodec *decoder = avcodec_find_decoder(cid);Но таким образом можно получить только декодер по умолчанию для данного идентификатора кодека. Для альтернативных декодеров нужно использовать имя декодера примерно таким образом:
const AVCodec *decoder = (cid == AV_CODEC_ID_H264)
? avcodec_find_decoder_by_name("h264_qsv")
: avcodec_find_decoder(cid);Для использования альтернативных декодеров в командной строке надо использовать ключ -c:v расположив его перед ключом -i примерно таким образом
ffmpeg -c:v h264_qsv -i INPUT ...
2. Измерение скорости декодирования
Для экспериментов по измерению скорости декодирования были выбраны два видеоролика, один закодирован в H264, другой в HEVC(H265). Размер кадра — 3840х2160 (Ultra HD), скорость — 30 к/с. Тестировались стандартные декодеры — h264, hevc и соответствующие QSV декодеры — h264_qsv, hevc_qsv. Стандартные декодеры настраивались в 4х вариантах: по умолчанию, два рабочих потока, четыре рабочих потока, аппаратное ускорение dxva2. В наших экспериментах dxva2 показал лучшие результаты, чем d3d11va, поэтому последний не участвовал в измерениях скорости декодирования. Для проведения тестов была написана программа которая извлекала пакеты из файла и декодировала их с максимально возможной скоростью, игнорируя метки времени и не выполняя рендеринг или иную обработку. Было два режима этой программы: в первом выполнялось только декодирование, в втором еще производилось конвертирование декодированного кадра в 32-битный формат BGRA с использованием библиотеки libswscale. (На выходе декодера кадр обычно имеет 12-битный планарный формат YUV420P или NV12.) Проводилось измерение времени, затраченного программой, и фиксировалось относительное время по отношению к номинальной длительности видеопотока (в процентах). Таким образом, если результат меньше 100%, то у нас есть шанс обработать видеопоток в реальном масштабе времени, если больше, то таких шансов нет. Также с помощью Диспетчера задач фиксировалась примерная загрузка ЦП и графического процессора. Использовалась 64-битная сборка FFmpeg.
| h264 | hevc | ||||||
|---|---|---|---|---|---|---|---|
| Config | # | Время | CPU | GPU | Время | CPU | GPU |
| default | 1 | 75 | 26 | 0 | 125 | 25 | 0 |
| 2 | 132 | 28 | 0 | 180 | 27 | 0 | |
| threads=2 | 1 | 47 | 42 | 0 | 74 | 42 | 0 |
| 2 | 79 | 48 | 0 | 104 | 46 | 0 | |
| threads=4 | 1 | 35 | 60 | 0 | 46 | 64 | 0 |
| 2 | 60 | 54 | 0 | 71 | 70 | 0 | |
| dxva2 | 1 | 45 | 14 | 72 | 34 | 28 | 70 |
| 2 | 107 | 28 | 35 | 99 | 30 | 36 | |
| xxxx_qsv | 1 | 25 | 34 | 80 | 25 | 34 | 72 |
| 2 | 70 | 39 | 54 | 70 | 40 | 50 | |
Большого обсуждения результаты, наверное, не требуют. Единственно на что стоит обратить внимание — это заметные затраты на преобразование в BGRA. И главное, что эти затраты сильно меняются в зависимости от тестовой конфигурации, хотя работа во всех случаях выполнялась очень близкая.
Эксперименты проводились также для 32-битной сборки FFmpeg. Результаты довольно близкие, кроме одного случая: декодер hevc в конфигурациях без аппаратного ускорения показал падение производительности в 2-3 раза. Весьма неожиданный результат.
Описанные тесты можно выполнить в командной строке. Надо использовать глобальный ключ -benchmark и установить нулевой выход. Вот несколько примеров:
ffmpeg -benchmark -i INPUT -f null - ffmpeg -benchmark -threads N -i INPUT -f null - ffmpeg -benchmark -c:v h264_qsv -i INPUT -f null - ffmpeg -benchmark -hwaccel dxva2 -i INPUT -f null - ffmpeg -benchmark -i INPUT -pix_fmt bgra -f null -
На выходе будет показан фактический fps, а параметр speed покажет во сколько раз он выше номинального.
3. Замечания о QSV декодерах
В рассматриваемой сборке FFmpeg есть следующие QSV декодеры: h264_qsv, hevc_qsv, vp8_qsv, mpeg2_qsv, vc1_qsv. Два последних оказались неработоспособными. Декодер mpeg2_qsv выдавал искаженную картинку, а vc1_qsv выдавал ошибку при передаче пакета на декодирование. Правда, эти декодеры не особо актуальны, но, все-таки, зачем выкладывать неработоспособные компоненты, не вполне понятно.
К оставшимся декодерам тоже есть претензии. В целом они работают, за исключением одного момента — они некорректно отрабатывают вызов avcodec_flush_buffers(). Ошибки нет, но после этого вызова позиционирование работает некорректно.
Ресурсы
[1] Intel Quick Sync Video.
[2] DirectX Video Acceleration.
[3] FFmpeg практика аппаратного декодирования DXVA2.
VertogPro
Я в питоне не особо разбираюсь, но статью оценил. Спасибо, может пригодиться.
dm_frox Автор
Фрагменты кода не на питоне, а на C.