3 February

Как повысить скорость декодирования видеопотока в FFmpeg

Open sourceProgrammingC

С приходом тяжеловесных видеоформатов, таких как 4K (Ultra HD), проблема эффективности декодирования видеопотока стала достаточно актуальной. На среднем компьютере приходится принимать специальные меры для того, чтобы можно было обработать такой видеопоток в реальном масштабе времени. В статье рассказывается о возможных способах увеличения скорости декодирования видеопотоков в решениях, основанных на FFmpeg, и приводятся результаты экспериментов по измерению скорости декодирования для 4K видеопотоков, закодированных в H264 и HEVC(H265).




Оглавление


1. Три способа повышения скорости декодирования видеопотока
1.1. Подключение дополнительных рабочих потоков в стандартных декодерах
1.2. Подключение аппаратного ускорения в стандартных декодерах
1.3. Использование специальных декодеров, реализующих декодирование на графических процессорах
2. Измерение скорости декодирования
3. Замечания о QSV декодерах
Ресурсы




1. Три способа повышения скорости декодирования видеопотока


Мы будем рассматривать три способа повышения скорости декодирования видеопотока.


  1. Подключение дополнительных рабочих потоков (threads) в стандартных декодерах.
  2. Подключение аппаратного ускорения (HW Acceleration) в стандартных декодерах.
  3. Использование специальных декодеров, реализующих декодирование на графических процессорах.

Первый из них использует исключительно возможности 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 (см. [2]), доступную только в Windows, но зато реализованную для видеокарт разных производителей (Intel, Nvidia, AMD).


Конкретные декодеры не обязаны поддерживать все эти типы аппаратного ускорения (или хотя бы один из них). Для определения типов, поддерживаемых конкретным декодером, можно воспользоваться следующим кодом:


void print_hwtypes(const char* dec_name)
{
    const AVCodec* decoder = avcodec_find_decoder_by_name(dec_name);
    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 -an -f null -
ffmpeg -benchmark -threads N -i INPUT -an -f null -
ffmpeg -benchmark -c:v h264_qsv -i INPUT -an -f null -
ffmpeg -benchmark -hwaccel dxva2 -i INPUT -an -f null -
ffmpeg -benchmark -i INPUT -an -pix_fmt bgra -f null -

На выходе будет показан фактический fps, а параметр speed покажет во сколько раз он выше номинального. Если не задана опция с ключом -threads или для N указано специальное значение auto, то декодер использует максимально возможное число потоков, загрузка ЦП при этом 100%.



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.




Tags:ffmpegдекодирование видеоUltra HD
Hubs: Open source Programming C
+16
10k 77
Comments 26
Popular right now
C++ Developer. Professional
December 28, 202060,000 ₽OTUS
Программирование на языке C (Си)
December 14, 202022,990 ₽Специалист.ру
C++ Junior Developer
March 3, 202123,990 ₽Level UP
Профессия iOS-разработчик
November 30, 202075,000 ₽SkillFactory