С приходом тяжеловесных видеоформатов, таких как 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.