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


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




    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 26

      –10

      Я в питоне не особо разбираюсь, но статью оценил. Спасибо, может пригодиться.

        +8
        Фрагменты кода не на питоне, а на C.
        +1
        Для максимального уменьшения задержки от приема до показа, если такая задача стоит, нужно работать с видеопамятью от запихивания туда сжатых данных до показа на контексте gpu, при этом аппаратное ускорение преобразования цвета тоже доступно для конкретного API, libmfx от Intel, например. FFMpeg в этом плане более ограничен, нежели API вендора.
          0
          Спасибо! Понятно, что FFmpeg не предел оптимизации.
          0
          Ждём статью об ускорении кодирования видеопотока :)
            0
            Могу сразу сказать, что libx264 и libx265 грузят ЦП на 100%, то есть они без лишних просьб используют все потоки. Аппаратное ускорение они не поддерживают. Кодеры *_qsv работают. (Наверное и их аналоги от Nvidia.) Главная проблема в том, что для кодеров используют большое число параметров, которые влияют на скорость кодирования и надо перебирать все комбинации, так, что подобные таблицы могут быть огромными. Даже если использовать только пресеты (ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow), все равно будет очень много данных.
              0

              2.5 года назад при кодировании (бенчмарк) 8К VP9->H265 с тогдашним, но свежим x265: хоть процессор и нагружал сильно, но утилизация скакала от 40-95% (8 ядер, 16 потоков). Настроек точных не вспомню, но все потоки — не все потоки. Хотя может ситуация поменялась в лучшую сторону.

            0

            Статья не раскрывает ничего, ни патченых драйверов под многопоточный encoder для Nvidia, ни того, как собирать или где добывать ffmpeg с поддержкой кодирования на radeon-ах. Ни того, что аппаратное ускорение можно применять не только для encoder-ов, но, и для decoder-ов, что тоже ускоряет общее время обработки.

              +2
              Статья вообще-то полностью про декодер, если я ничего не пропустил…
                0
                Статья как раз про декодеры. Исторически сложилось так, что всю жизнь сидел на Intel. С NVIDIA также работал мало. А вот с продукцией AMD практически не имел дела. Про поддержку Radeon в ffmeg вообще не слышал, специально не искал, так как эта поддержка для меня не актуальна.
                +3
                Дочитал до i5 8400 и сразу понял вашу проблему. При упоминании HWAccel стоит сразу же оговориться, что оно несёт собой просто тонну проблем и ограничений по входным данным. Размер кадра, битрейт, FPS, уровень и профиль кодека, которым кодировались данные. Все это жёстко ограничивает возможности встроенного в чип ВК аппаратного декодера (ASIC) по количеству поддерживаемых форматов. На днях тестировал декодер 5700xt, где завезли поддержку аж всего и аж в 8k сразу. Во всех тестах r5 3600x оказался быстрее в декодировании, просто в разы. Сегодня, когда zen 2 продается по 5 рублей за кило, лучше даже и не вспоминать про аппаратный декодер.
                Он не придумывался для ускорения декодирования видео, он придумывался в качестве очередной энергосберегайки и как замена слабомощным процессорам, а все остальное просто маркетинговый булшит. Да несомненно, в той же 5700xt завезли и аппаратный кодер, но это случилось только «вчера» и ни где никакой поддержки ещё нет, кроме драйвера AMD, который будет кодировать видео с экрана на лету, не напрягая ВК и процессор особо, в ответ на shadow play от зелёных. Вот как-то так пока обстоят дела на сегодняшний и получается, что для этих задач и проще и удобнее использовать процессор. Особенно, когда AMD ударилась в ядра. А у аппаратного декодера всегда есть потолок, например по fps, выше которого он физически не прыгнет. Алсо, когда в последний раз интересовался cuda декодером, он был скорее мертв, чем жив судя по его возможностям. По работе мне надо уметь держать воспроизведение видео любых форматов любого разрешения, в том числе и нестандартных, например 4096х4096, 3600х3600, фишай, а уж 360 видео в равнопромежуточной проекции имеет кучу нестандартных соотношений сторон. Так что к теме имею прямое отношение.
                  +1
                  Зависит от задачи. Бывает, требуется делать видеостену на 16 FullHD камер на миниатюрной железке с одним процессором, вот в таких случаях без аппаратного декодера не обойтись
                    0

                    В таком случае у Вас, помимо проблемы скорости декодирования, встанет проблема избегания пересылки данных из vram в ram и обратно.

                      0
                      В сэмплах к Intel mfx показано как работать только с видеопамятью без копирования в обычную. Железка же должна подбираться так, чтобы успевать декодировать все потоки, само собой.
                    0
                    Спасибо! Очень интересная информация. Исторически сложилось так, что я всю жизнь сидел на Intel (да и сейчас сижу). Было бы очень интересно пощупать AMD, но в обозримом будущем наверное не получится (хоть они и 5 рублей за кило).
                      0
                      Сегодня, когда zen 2 продается по 5 рублей за кило, лучше даже и не вспоминать про аппаратный декодер

                      Про аппаратный декодер придется вспомнить, если у вас компания занимается стримингом и имеет собственные сервера для транскодинга. В свое время мы использовали серверные процессоры Intel для этой цели. Если использовать программный декодер, то уменьшается количество каналов, которые можно транскодировать одновременно в разные качества. Наиболее оптимальный вариант — когда транскодинг полностью аппаратный и не происходит копирование в системную память после декодера, а поверхность сразу отправляется в энкодер. На одном таком сервере за счет встроенного GPU удавалось ускорить процесс в 5-6 раз, как и количество каналов. Это вообще критично может быть, учитывая стоимость одного сервера.
                        0
                        А у аппаратного декодера всегда есть потолок, например по fps, выше которого он физически не прыгнет

                        Этот потолок часто ограничен только пропускной способностью памяти и бывает 200-500 fps в зависимости от разрешения картинки. Если сервер обрабатывает много потоков это может быть критично.
                          0
                          Киношку по названию можно найти на YouTube:

                          Video: h264 (High) (avc1 / 0x31637661), yuv420p, 3840x2160 [SAR 1:1 DAR 16:9], 18005 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc (default)
                          R5 3600x:
                          ffmpeg -threads auto -i "f:\Films\COSTA RICA IN 4K 30fps (ULTRA HD) w Freefly Movi.mp4" -f null - -benchmark
                          frame= 5111 fps=482 q=-0.0 size=N/A time=00:02:50.94 bitrate=N/A speed=16.1x

                          Radeon 5700xt:
                          ffmpeg -hwaccel dxva2 -threads 1 -i "f:\Films\COSTA RICA IN 4K 30fps (ULTRA HD) w Freefly Movi.mp4" -f null - -benchmark
                          frame= 1267 fps=148 q=-0.0 size=N/A time=00:00:42.28 bitrate=N/A speed=4.94x


                          А ведь это даже не серверный процессор, да и ffmpeg не последний… Хотелось бы увидеть результат qsv… А вот у радеона в спеках числится как раз поддержка декодирования 4k AVC на 150fps максимально. И при декодировании этого ролика ASIC загружен на 100%. Диспетчер задач — GPU — Video Encode (ей богу не знаю почему они засунули этот показатель в encode). Но это его потолок. На карточках NV всегда отображается в графике Video Decode.

                          ps Киношку перезалили вроде как только в 60fps теперь доступна…
                            0
                            Video: h264 (High) (avc1 / 0x31637661), yuv420p, 3840x2160 [SAR 1:1 DAR 16:9], 13993 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)

                            i7-8700
                            ffmpeg -threads auto -i costa-rica_h264.mp4 -f null - -benchmark
                            frame= 9246 fps=245 q=-0.0 Lsize=N/A time=00:05:08.50 bitrate=N/A speed=8.18x


                            Geforce RTX 2080:
                            ffmpeg -hwaccel vdpau -threads 1 -i costa-rica_h264.mp4 -f null - -benchmark
                            frame= 9246 fps=198 q=-0.0 Lsize=N/A time=00:05:08.50 bitrate=N/A speed=6.59x


                            У меня, правда битрейт меньше исходника, я сам жал в h264
                              0
                              А ffmpeg -hwaccel qsv… что говорит?
                              trac.ffmpeg.org/wiki/Hardware/QuickSync Тут вроде как установка и сборка… Если оно еще не на перманентной основе вшито в ffmpeg.

                              ps При выходе за пределы возможностей аппаратного декодера на 5700xt в данный момент, кстати, падает драйвер. Ну скажем видео 8k не 24 фпс максимум, а 30. А должен сваливаться в софтварный декод. Чего-то там не доделали пока индусы из AMD…
                                0
                                А ffmpeg -hwaccel qsv… что говорит?

                                В линуксе проблематично использовать одновременно встроенное видео и дискретную видеокарту, попробую дома.
                              0
                              И при декодировании этого ролика ASIC загружен на 100%

                              ASIC ускоряет, как правило, такие операции как арифметическое кодирование и iDCT, остальное, например, Motion estimation, в зависимости от реализации, может выполняться на блоках общего назначения или даже CPU.

                              Хотелось бы увидеть результат qsv

                              У меня получался результат в 6-10 раз быстрее (Haswell, Broadwell, SkyLake), но для получения такого результата нужно использовать непосредственно Intel Media SDK без ffmpeg
                                0
                                Запустил свой ролик (тот же, что и данные в статье)
                                Stream #0:0: Video: h264 (High), yuv420p(progressive), 3840x2160 [SAR 1:1 DAR 16:9], 30.30 fps, 29.97 tbr, 1k tbn, 59.94 tbc (default)
                                ffmpeg -c:v h264_qsv -i INPUT -f null — -benchmark
                                на выходе
                                frame= 2241 fps=129 q=-0.0 Lsize=N/A time=00:01:14.77 bitrate=N/A speed=4.3x
                                Загрузка ЦП — 35%, графический процессор — 80%
                            +1

                            Сам недавно кстати эксперементировал с ffmpeg на ноутбуке с двумя видеокартами (uhd630+1050ti), жал видео в h264. Лучший по скорости результат показал h264_nvenc, чуть медленнее но качественнее сжал h264_qsv, самый медленный но самый качественный результат дал libx264, но проц напрягся под сотку, от чего грелся и шумел. Самое любопытное то, что мне удалось запустить две копии ffmpeg и жать одновременно двумя аппаратными кодеками qsv и nvenc два разных видеофайла (в диспетчере задач четко было видно загруженность обоих видеокарт). Смысла в этом конечно мало, разве что сериал так пожать получиться быстрее, но вот увы, жать один видеофайл двумя аппаратными видеокодеками нельзя. Больше всех удивил qsv — жмет быстро, но при этом проц почти не нагружен и по температурам все хорошо, можно поставить например жать сериал и в этот момент полноценно пользоваться ноутбуком.

                              0
                              Спасибо за интересную информацию. Ну вообще-то параллельная работа двух разных видеокарт достаточно ожидаема, было бы очень странно, если б они мешали друг другу. А такой режим может оказаться актуальным, если надо параллельно обрабатывать несколько 4K потоков.
                                +1
                                Я однажды поставил себе задачу: нужно сжать было сериал, чтобы смотреть его на телефоне. Цена вопроса: сжать чтобы мало занимал места, но при этом не сильно чтобы в ущерб качеству. Перепробовав тонну кодировщиков, в том числе и онлайн, я остановился именно на ffmpeg, но пожелав для себя сделать работу с ним максимально комфортной, я воспользовался весьма приятным GUI под названием WinFF.
                                Начал настраивать под себя пресеты, в итоге сделал свой пресет:
                                -vcodec h264_qsv -global_quality 25 -preset veryslow -profile high -vf format=nv12 -b:a 64k -ac 2

                                Качество картинки остается весьма приятная глазу на телефоне, звук жмется до стерео, в AAC с битрейтом 64к, что дает малый размер, качества звука вполне хватает для просмотра на телефоне. Все это сжиматься весьма на приличной скорости.
                                Что касаеться nvenc, то пресет немного разниться:
                                -vcodec h264_nvenc -global_quality 25 -preset slow -profile high -vf format=nv12 -b:a 64k -ac 2

                                Почему то nvenc + ffmpeg не могут жать с пресетом veryslow, из за чего страдает размер и качество, можно лишь минимум slow, звук жмется аналогично. Это собственно одна из причин выбора именно в пользу QSV

                            Only users with full accounts can post comments. Log in, please.