Эффективное кодирование видео в Linux c Nvidia NVENC: часть 2, дополнительная



    В первой части я рассказал о кодировании видео в Linux с использованием Nvidia NVENC. Как уже упоминалось ранее, Nvidia для десктопных видеокарт ограничивает количество потоков кодирования до двух сессий на систему. Данная часть посвящена борьбе с этим ограничением.

    Окружение


    Всё описываемое происходит на машине с GTX 970 и установленным FFmpeg, в соответствии с конфигурацией, рассмотренной ранее.

    Внешние проявления


    При попытке запустить параллельно кодирование более двух потоков видео FFmpeg выдаст ошибку:
    ...
    [nvenc @ 0x3187200] OpenEncodeSessionEx failed: 0xa - invalid license key?
    ...
    Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height
    

    Чтобы постоянно и удобно воспроизводить эту ошибку, я запустил два раза перекодирование в ffmpeg и поставил процесс на паузу, послав ему SIGSTOP (Ctrl+Z в терминале):
    $ /usr/local/bin/ffmpeg -y -i input.mov -vcodec nvenc output1.mp4
    ...
    Stream mapping:
      Stream #0:1 -> #0:0 (mpeg4 (native) -> h264 (nvenc))
      Stream #0:0 -> #0:1 (aac (native) -> aac (libfdk_aac))
    Press [q] to stop, [?] for help
    frame=   81 fps= 80 q=0.0 size=    1362kB time=00:00:03.24 bitrate=3444.9kbits/s
    [1]+  Stopped                 /usr/local/bin/ffmpeg -y -i input.mov -vcodec nvenc out1.mp4
    $ /usr/local/bin/ffmpeg -y -i input.mov -vcodec nvenc output2.mp4
    ...
    Stream mapping:
      Stream #0:1 -> #0:0 (mpeg4 (native) -> h264 (nvenc))
      Stream #0:0 -> #0:1 (aac (native) -> aac (libfdk_aac))
    Press [q] to stop, [?] for help
    frame=   81 fps= 80 q=0.0 size=    1362kB time=00:00:03.24 bitrate=3444.9kbits/s
    [2]+  Stopped                 /usr/local/bin/ffmpeg -y -i input.mov -vcodec nvenc out1.mp4
    


    ltrace


    Посмотрим на это место детальнее:
    $ ltrace /usr/local/bin/ffmpeg -y -i input.mov -vcodec nvenc out3.mp4 2>&1 | less
    ...
    dlsym(0x313e360, "cuInit")                       = 0x7f93974182c0
    dlsym(0x313e360, "cuDeviceGetCount")             = 0x7f9397418760
    dlsym(0x313e360, "cuDeviceGet")                  = 0x7f93974185c0
    dlsym(0x313e360, "cuDeviceGetName")              = 0x7f93974188e0
    dlsym(0x313e360, "cuDeviceComputeCapability")    = 0x7f9397418f80
    dlsym(0x313e360, "cuCtxCreate_v2")               = 0x7f9397419940
    dlsym(0x313e360, "cuCtxPopCurrent_v2")           = 0x7f9397419df0
    dlsym(0x313e360, "cuCtxDestroy_v2")              = 0x7f9397419af0
    dlopen("libnvidia-encode.so.1", 1)               = 0x3231970
    dlsym(0x3231970, "NvEncodeAPICreateInstance")    = 0x7f93970d4370
    posix_memalign(0x7fffb429d490, 32, 640, 0x7fffb429d3f8) = 0
    memset(0x3141420, '\0', 640)                     = 0x3141420
    free(0)                                          = <void>
    pthread_mutex_lock(0x19a90e0, 8, 0xf8f340, 0x7fffb429d3f8) = 0
    __vsnprintf_chk(0x7fffb429c3b4, 1004, 1, -1)     = 20
    __vsnprintf_chk(0x7fffb429cbb4, 1004, 1, -1)     = 55
    __snprintf_chk(0x7fffb429cfa0, 1024, 1, 1024)    = 75
    strcmp("[nvenc @ 0x3187200] OpenEncodeSe"..., "\n") = 81
    __strcpy_chk(0x19a8cc0, 0x7fffb429cfa0, 1024, 0) = 0x19a8cc0
    fputs("[nvenc @ 0x3187200] ", 0x7f93a554e1c0[nvenc @ 0x3187200] )    = 1
    fputs("OpenEncodeSessionEx failed: 0xa "..., 0x7f93a554e1c0OpenEncodeSessionEx failed: 0xa - invalid license key?
    ) = 1
    pthread_mutex_unlock(0x19a90e0, 0, 0x7fffb429cbb4, -1) = 0
    ...
    

    Видно, что какая-то ошибка вывелась, но что вызывалось не видно из-за динамической загрузки библиотеки и её символов (функций).

    Исходный код FFmpeg


    Поищем это место в исходниках самого FFmpeg, чтобы взять его отправной точкой.
    ~/ffmpeg-2.7.1$ fgrep -r OpenEncodeSessionEx
    ...
    libavcodec/nvenc.c:606:    nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    libavcodec/nvenc.c:609:        av_log(avctx, AV_LOG_FATAL, "OpenEncodeSessionEx failed: 0x%x - invalid license key?\n", (int)nv_status);
    ...
    

    Всё понятно, тут и поставим брейкпоинт.

    Светлый GDB


    GNU Debugger — это основной юниксовый отладчик, предназначение которого отлаживать программы, дабы они не генерировали ошибки.

    Для ориентирования в машинном коде скомпилированного приложения и соотношения его с исходным кодом, желательно чтобы приложение было скомпилировано с отладочными символами. Они, в первую очередь, содержат информацию о соответствии машинного и исходных кодов.

    В большинстве дистритубивов пакеты содержат бинарные файлы с обрезанной отладочной информацией и для некоторых пакетов отладочная информация поставляется в виде отдельного пакета. В ubuntu это, как правило, пакеты с суффиксом -dbg. В centos нужно подключить репозиторий с отладочными символами и воспользоваться утилитой debuginfo-install из состава yum-utils, которая установит отладочные символы для пакета и его зависимостей.

    В нашем же случае с самосборным FFmpeg, необрезанный бинарь доступен в его сборочной директории под именем ffmpeg_g. Мы можем запустить его под отладчиком и сразу поставить брейкпоинт на нужную строчку в исходном коде.
    # gdb ffmpeg-2.7.1/ffmpeg_g
    GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
    Copyright (C) 2014 Free Software Foundation, Inc.
    ...
    Reading symbols from ffmpeg-2.7.1/ffmpeg_g...done.
    (gdb)
    

    Установим брейкпоинт на интересующее нас место:
    (gdb) break nvenc.c:606
    Breakpoint 1 at 0x44a890: file libavcodec/nvenc.c, line 606.
    

    Запускаем программу, указав аргументы запуска через аргументы команде run:
    (gdb) run -i in.mov -vcodec nvenc out3.mp4
    ...
    Breakpoint 1, nvenc_encode_init (avctx=0x1b806e0) at libavcodec/nvenc.c:606
    606         nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    (gdb) list
    601         }
    602
    603         encode_session_params.device = ctx->cu_context;
    604         encode_session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
    605
    606         nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    607         if (nv_status != NV_ENC_SUCCESS) {
    608             ctx->nvencoder = NULL;
    609             av_log(avctx, AV_LOG_FATAL, "OpenEncodeSessionEx failed: 0x%x - invalid license key?\n", (int)nv_status);
    610             res = AVERROR_EXTERNAL;
    (gdb) 
    

    Брейкпоинт сработал и мы действительно достигли нужного места в коде. Для удобства нажатием Ctrl+X, Ctrl+A можно переключить GDB в режим разделения командного экрана с экраном исходника.

    Пройдём код пошагово до возврата этой функции.
    606         nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    (gdb) step
    603         encode_session_params.device = ctx->cu_context;
    (gdb) step
    606         nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    (gdb) step
    607         if (nv_status != NV_ENC_SUCCESS) {
    

    Последнюю введённую команду, к слову, можно повторять простым нажатием Enter. Возврат функции сохраняется в локальную переменную nv_status. Посмотрим, что же в ней:
    (gdb) info locals
    ...
    nv_status = NV_ENC_ERR_OUT_OF_MEMORY
    ...
    

    Убиваем висящие ffmpeg-и, в отладчике перезапускаем программу командой run. Это запустит её с теми же аргументами. Дойдя до того же места, мы увидим:
    (gdb) info locals
    ...
    nv_status = NV_ENC_SUCCESS
    ...
    

    Таким образом, функция создания сессии кодирования возвращает NV_ENC_SUCCESS (0) в случае успеха, либо NV_ENC_ERR_OUT_OF_MEMORY (10), если пользователь уже открыл 2 сессии кодирования. Спустимся вглубь этой функции.

    Тёмный GDB


    Дойдём до вызова этой функции и спустимся вглубь неё.
    Breakpoint 1, nvenc_encode_init (avctx=0x1b806e0) at libavcodec/nvenc.c:606
    606         nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
    (gdb) layout asm
    

    Интерфейс GDB примет вид:

    Принудительно перерисовать экран, если он оказался замусорен, можно нажатием Ctrl+L.

    Указатель исполнения стоит на загрузке параметров функции перед её вызовом. Отправляемся вглубь:
    (gdb) set step-mode on
    (gdb) step
    ...
    

    Оказываемся внутри функции из /usr/lib/x86_64-linux-gnu/libnvidia-encode.so.1:
      >|0x7fffe289b010  mov    %rbp,-0x20(%rsp)                                                                                                                                       |
       |0x7fffe289b015  mov    %r12,-0x18(%rsp)                                                                                                                                       |
       |0x7fffe289b01a  mov    $0x6,%ebp                                                                                                                                              |
       |0x7fffe289b01f  mov    %rbx,-0x28(%rsp)                                                                                                                                       |
       |0x7fffe289b024  mov    %r13,-0x10(%rsp)                                                                                                                                       |
       |0x7fffe289b029  mov    %rsi,%r12                                                                                                                                              |
       |0x7fffe289b02c  mov    %r14,-0x8(%rsp)                                                                                                                                        |
       |0x7fffe289b031  sub    $0xa8,%rsp                                                                                                                                             |
       |0x7fffe289b038  test   %rdi,%rdi                                                                                                                                              |
       |0x7fffe289b03b  sete   %dl                                                                                                                                                    |
       |0x7fffe289b03e  test   %rsi,%rsi                                                                                                                                              |
       |0x7fffe289b041  sete   %al                                                                                                                                                    |
       |0x7fffe289b044  or     %al,%dl                                                                                                                                                |
       |0x7fffe289b046  jne    0x7fffe289b060                                                                                                                                         |
       |0x7fffe289b048  mov    (%rdi),%eax
    

    Так проходим пошагово всю функцию, записывая на бумажке направления условных переходов. Заходя в вызовы других функций, чтобы не углубляться, сразу выходим из них командой finish. Проделываем это 2 раза, когда все кодирующие сессии заняты и когда есть свободные.

    Следуя такой методологии, приходим к тому, что разветвление начинается с места:
       |0x7fffe289b319                                  callq  0x7fffe288d510                                                                                                         |
       |0x7fffe289b31e                                  test   %eax,%eax                                                                                                              |
       |0x7fffe289b320                                  mov    %eax,%ebp                                                                                                              |
       |0x7fffe289b322                                  jne    0x7fffe289b332                                                                                                         |
    


    Функция, принимающая решение:
      >|0x7fffe288d510  mov    %rbx,-0x20(%rsp)                                                                                                                                       |
       |0x7fffe288d515  mov    %rbp,-0x18(%rsp)                                                                                                                                       |
       |0x7fffe288d51a  mov    %rdi,%rbx                                                                                                                                              |
       |0x7fffe288d51d  mov    %r12,-0x10(%rsp)                                                                                                                                       |
       |0x7fffe288d522  mov    %r13,-0x8(%rsp)                                                                                                                                        |
       |0x7fffe288d527  sub    $0x28,%rsp                                                                                                                                             |
       |0x7fffe288d52b  test   %rsi,%rsi                                                                                                                                              |
       |0x7fffe288d52e  mov    %rsi,%r12                                                                                                                                              |
       |0x7fffe288d531  mov    %rcx,%r13                                                                                                                                              |
       |0x7fffe288d534  mov    $0x6,%ebp                                                                                                                                              |
       |0x7fffe288d539  je     0x7fffe288d54d                                                                                                                                         |
       |0x7fffe288d53b  dec    %edx                                                                                                                                                   |
       |0x7fffe288d53d  mov    $0xa,%bpl                                                                                                                                              |
       |0x7fffe288d540  je     0x7fffe288d568                                                                                                                                         |
       |0x7fffe288d542  cmpb   $0x1,0x10(%rbx)                                                                                                                                        |
       |0x7fffe288d546  je     0x7fffe288d5a3                                                                                                                                         |
       |0x7fffe288d548  mov    $0x2,%ebp                                                                                                                                              |
       |0x7fffe288d54d  mov    %ebp,%eax                                                                                                                                              |
       |0x7fffe288d54f  mov    0x8(%rsp),%rbx                                                                                                                                         |
       |0x7fffe288d554  mov    0x10(%rsp),%rbp                                                                                                                                        |
       |0x7fffe288d559  mov    0x18(%rsp),%r12                                                                                                                                        |
       |0x7fffe288d55e  mov    0x20(%rsp),%r13                                                                                                                                        |
       |0x7fffe288d563  add    $0x28,%rsp                                                                                                                                             |
       |0x7fffe288d567  retq                                                                                                                                                          |
    

    Эта функция что-то проверяет в памяти, если успешно, — то делает что-то и возвращает 0, если нет, — то 10. Ровно такой код ошибки из результата этой функции возвращает сама nvEncOpenEncodeSessionEx() в случае неудачи. Попробуем игнорировать возврат этой функции, как если бы она вернула 0.

    Останавливаемся после callq 0x7fffe288d510 и перед test %eax,%eax. Обнуляем регистр с результатом функции и продолжаем свободное выполнение программы:
    (gdb) set $eax = 0
    (gdb) continue
    

    Перекодировка началась! И даже производит правильные результаты. Значит необходимо, чтобы в коде всегда было так. Зафиксируем это в самой libnvidia-encode.so.1

    Нужно понять где это место находится в физическом файле на диске. Узнаем какому смещению в файле соответствует виртуальный адрес в коде библиотеки, загруженной в память.
    (gdb) info proc mappings
    process 1692
    Mapped address spaces:
    
              Start Addr           End Addr       Size     Offset objfile
    ...
          0x7fffe2887000     0x7fffe28a8000    0x21000        0x0 /usr/lib/x86_64-linux-gnu/libnvidia-encode.so.346.46
    ...
    

    Нас интересует окрестность адреса 0x7fffe289b31e, она попадает в этот регион. Тогда смещение в файле равно: адрес — начальный адрес + смещение сегмента.
    (gdb) print/x 0x7fffe289b31e - 0x7fffe2887000 + 0x0
    $7 = 0x1431e
    


    Biew


    Осталось пропатчить сам файл. Я не нашёл пока ничего лучше, чем biew (был переименован в beye). Предварительно сделав резервную копию, исправим файл:
    biew /usr/lib/x86_64-linux-gnu/libnvidia-encode.so.346.46
    

    В нём: F2 -> Disassembler, F5 -> 1431e
    Получим такую картину:


    Выглядит точь-в-точь как искомый код, значит мы попали верно. Нам необходимо, чтобы в регистре eax оказался 0, а условный переход не произошёл никогда.

    Нажатие F4 включает режим редактирования. В biew нет такого удобного режима как в hiew, в котором можно непосредственно вводить инструкцию, а редактор её ассемблирует. Поэтому придётся манипулировать опкодами численно. Записываем, к примеру, так:


    Байт по смещению 0x1431e со значения 0x85 меняем на 0x29. Инструкция «test eax, eax» превращается в «sub eax, eax». Два байта по смещениям 0x14322 и 0x14323 заменяем на 0x90 — это широко известный опкод nop.

    Итог


    Полученное решение вполне хорошо работает. Применив стандартные инструменты, можно достичь многого и расширить границы возможного.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 20

      –5
      А покажите, пожалуйста, пример кодирования H.265, и с какой скоростью оно это делает. Очень интересно.
      И, кстати, надеюсь, что в конце сентября покажу уже сервис по распределенному кодированию видео, о концепции которого писал год назад. Сейчас, я считаю, самое время: людям нужно кодировать и в h.265, и в vp8, и в vp9, да еще и 4K-видео вот-вот пойдет потоком, а тот же ZenCoder — один из самых популярных сервисов по кодированию видео ­— возьмет с вас более $2 (!!!) за кодирование 3-минутного видеоролика в 4K в H.265. Мой же сервис будет не только позволять устанавливать все параметры кодирования для любого видеокодека, и использовать встроенные фильтры ffmpeg, но и будет заметно дешевле. Кодирование подобного видео обойдется примерно в 30 центов.
        +4
        87 fps для 1080p

        Оригинал
        H.265
          0
          Решил для сравнения закодировать то же видео с помощью x264 (на процессоре i7-3770K). Подобрал параметры, чтобы получилась такая же скорость кодирования и размер файла (preset=ultrafast, crf=24.5).

          Получившееся видео. При сравнении на глаз и там и там артефакты, какое видео лучше — непонятно.

            0
            Но, согласитесь, довольно достойный результат для H.265 с такой скоростью. Я несколько удивлен даже.
        0
        Хм, а может это не лучшая идея просто игнорировать возвращенное значение? Ведь функция все-таки зачем-то вызывается. Мне кажется, стоило проверить, что на самом деле не так…
        Кроме того, я не вижу, как функция явным образом меняет eax, да и код функции приведен не весь (куда ведет je 0x7fffe288d568?)
          0
          Код не полностью приведён, это правда, меняется ниже. Совпадение размера окна ассемлерного кода в отладчике и инструкции ret вводит в заблуждение.

          Правда в том, что eax меняется вообще в другом модуле.
          www.dropbox.com/s/9by7mkr9lh4ajg7/%D0%A1%D0%BA%D1%80%D0%B8%D0%BD%D1%88%D0%BE%D1%82%202015-07-15%2001.39.13.png?dl=0

          То есть, когда первая загадочная функция вызывается, она что-то делает, получает через dlsym указатель на другую библиотечную функцию и прыгает по нему. Не вызывает, а просто прыгает. Возврат на пропатченную мною инструкцию происходит уже из другого модуля. Мне кажется, в коде это выглядит как хвостовая оптимизация, когда функция всегда возвращает результат другой функции. Вызов опускается, заменяется на прыжок и управление возвращается уже вызвавшему первую функцию. Мне кажется, там ничего ценного, раз она безусловно возвращается и результат не проверяется.
          –9
          Из условий лицензии:
          Licensee agrees not to disassemble, decompile or reverse engineer the Object Code
            +1
            а наплевать на эту лицензию в России. Она нарушает российское законодательство и в этой части ничтожна
            +1
            UPD: Всё указанное выше справедливо и для новых драйверов 367.35. Смещение в файле получается 0x163bd.
              +1
              https://github.com/keylase/nvidia-patch
                0
                а есть мысли насчет реализации дров и работы с 1070?
                Там после 3-го потока падает:
                pthread_mutex_unlock(0x3681dc0, 2, 0x10cbbc0, 1) = 0
                pthread_mutex_lock(0x3681dc0, 1, 0, 1) = 0
                posix_memalign(0x7ffebf75a6f0, 32, 488, 0) = 0
                memset(0x3682700, '\0', 488) = 0x3682700
                free(0) = dlopen(«libcuda.so.1», 1) = 0x3682910
                dlsym(0x3682910, «cuInit») = 0x7fe3dd42b500
                dlsym(0x3682910, «cuDeviceGetCount») = 0x7fe3dd42b9a0
                dlsym(0x3682910, «cuDeviceGet») = 0x7fe3dd42b800
                dlsym(0x3682910, «cuDeviceGetName») = 0x7fe3dd42bb20
                dlsym(0x3682910, «cuDeviceComputeCapability») = 0x7fe3dd42c1c0
                dlsym(0x3682910, «cuCtxCreate_v2») = 0x7fe3dd42cb80
                dlsym(0x3682910, «cuCtxPopCurrent_v2») = 0x7fe3dd42d030
                dlsym(0x3682910, «cuCtxDestroy_v2») = 0x7fe3dd42cd30
                dlopen(«libnvidia-encode.so.1», 1) = 0x3683280
                dlsym(0x3683280, «NvEncodeAPIGetMaxSupportedVersio»...) = 0x7fe3dcde2cb0
                dlsym(0x3683280, «NvEncodeAPICreateInstance») = 0x7fe3dcde6410
                posix_memalign(0x7ffebf75a5f8, 32, 32, 1) = 0
                memcmp(0x3dc3440, 0x344779c, 16, 0) = 0
                free(0x3dc3440) = posix_memalign(0x7ffebf759090, 32, 24, 0x3dc4f50) = 0
                memset(0x3dc3440, '\0', 24) = 0x3dc3440
                realloc(0, 16) = 0x3dc3b30
                posix_memalign(0x7ffebf75a5e0, 32, 0xc800, 31) = 0
                memset(0x3e9d1c0, '\0', 51200) = 0x3e9d1c0
                posix_memalign(0x7ffebf75a5d8, 32, 256, 0x3ea99c0) = 0
                posix_memalign(0x7ffebf75a5c0, 32, 40, 0x7fe3e791f760) = 0
                memset(0x3dc35e0, '\0', 40) = 0x3dc35e0
                posix_memalign(0x7ffebf75a5d8, 32, 256, 0x7fe3e791f760) = 0
                posix_memalign(0x7ffebf75a5c0, 32, 40, 0x7fe3e791f760) = 0
                memset(0x3dc37c0, '\0', 40) = 0x3dc37c0
                posix_memalign(0x7ffebf75a5d8, 32, 256, 0x7fe3e791f760) = 0
                posix_memalign(0x7ffebf75a5c0, 32, 40, 0x7fe3e791f760) = 0
                memset(0x3dc39a0, '\0', 40) = 0x3dc39a0
                pthread_mutex_lock(0x1c94ac0, 16, 0x130298a, 0x7ffebf759ed8) = 0
                __vsnprintf_chk(0x7ffebf758e94, 1004, 1, -1) = 25
                __vsnprintf_chk(0x7ffebf759694, 1004, 1, -1) = 49
                __snprintf_chk(0x7ffebf759a80, 1024, 1, 1024) = 74
                strcmp("[h264_nvenc @ 0x340bde0] CreateB"..., "\n") = 81
                __strcpy_chk(0x1c946a0, 0x7ffebf759a80, 1024, 0) = 0x1c946a0
                __fprintf_chk(0x7fe3e79201c0, 1, 0x132c96d, 1[h264_nvenc @ 0x340bde0] ) = 36
                __fprintf_chk(0x7fe3e79201c0, 1, 0x132c96d, 1CreateBitstreamBuffer failed: out of memory (10)
                ) = 60
                pthread_mutex_unlock(0x1c94ac0, 0, 0x7ffebf759694, 0x7fe3e764c72d) = 0
                free(0x3dc3480) = free(0x3dc35e0) = free(0x3dc3840) = free(0x3dc39a0) = free(0x3dc3660) = free(0x3dc37c0) = — SIGSEGV (Segmentation fault) — +++ killed by SIGSEGV +++
                  0
                  Здравствуйте! У меня нету оборудования — ответил в ЛС.
                    +1
                    Причина оказалась в лимите числа буферов у видеокарты. Решили ограничить аппетиты ffmpeg по числу буферов вот таким патчем:
                    diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c
                    index a3a2ef5..1691d15 100644
                    --- a/libavcodec/nvenc.c
                    +++ b/libavcodec/nvenc.c
                    @@ -1157,7 +1157,7 @@ static av_cold int nvenc_setup_surfaces(AVCodecContext *avctx)
                         NvencContext *ctx = avctx->priv_data;
                         int i, res;
                         int num_mbs = ((avctx->width + 15) >> 4) * ((avctx->height + 15) >> 4);
                    -    ctx->nb_surfaces = FFMAX((num_mbs >= 8160) ? 32 : 48,
                    +    ctx->nb_surfaces = FFMAX((num_mbs >= 8160) ? 8 : 12,
                                                  ctx->nb_surfaces);
                         ctx->async_depth = FFMIN(ctx->async_depth, ctx->nb_surfaces - 1);
                     
                    diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c
                    index 6914046b..7f050af 100644
                    --- a/libavcodec/nvenc_h264.c
                    +++ b/libavcodec/nvenc_h264.c
                    @@ -80,7 +80,7 @@ static const AVOption options[] = {
                         { "vbr_2pass",    "Multi-pass variable bitrate mode",   0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_2_PASS_VBR },           0, 0, VE, "rc" },
                         { "rc-lookahead", "Number of frames to look ahead for rate-control",
                                                                                 OFFSET(rc_lookahead), AV_OPT_TYPE_INT,   { .i64 = -1 }, -1, INT_MAX, VE },
                    -    { "surfaces",     "Number of concurrent surfaces",      OFFSET(nb_surfaces),  AV_OPT_TYPE_INT,   { .i64 = 32 },  0, INT_MAX, VE },
                    +    { "surfaces",     "Number of concurrent surfaces",      OFFSET(nb_surfaces),  AV_OPT_TYPE_INT,   { .i64 = 8 },  0, INT_MAX, VE },
                         { "cbr",          "Use cbr encoding mode",              OFFSET(cbr),          AV_OPT_TYPE_BOOL,  { .i64 = 0 },   0, 1, VE },
                         { "2pass",        "Use 2pass encoding mode",            OFFSET(twopass),      AV_OPT_TYPE_BOOL,  { .i64 = -1 }, -1, 1, VE },
                         { "gpu",          "Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.",
                    
                      0
                      Патч уже не нужен, начиная с этого коммита в ffmpeg. Ожидаем релиза, содержащего этот коммит.
                        0
                        Соответственно, теперь работает ограничение через параметр surfaces командной строки.
                  0
                  На GP107GL Quadro P400 запуск патча убрал ограничение на количество одновременно запущенных ffmpeg'ов (ловите pull request на github'e для поддержки текущей версии и CentOS).

                  Однако скорость работы линейно уменьшается с увеличением числа ffmpeg'ов, как будто обработку всё равно ведут только два процессора.

                  Так и должно быть?

                  Измерял так: gist.github.com/ilyaevseev/fc75df36fe27f1ab580e6afb285b9ab7#file-ffmpeg-nvenc-parbench-sh
                    +1
                    Если декодирование ведётся средствами процессора, то скорее всего в него и упирается. В мэйлинг-листе ffmpeg один пользователь сообщает, что достиг параллелизма 60 SD-каналов. И то, упёрся только в объём памяти GPU. Судя по опциям, которые он указал, у него и декодирование аппаратное.

                    А пуллреквесты принимает keylase.
                      +1
                      если декодирование ведется процессором, то легко можно и в шину упереться.

                      Мы кстати смогли уменьшить память относительно ффмпега — не надо разные процессы создавать.
                        0
                        А что в данном случае подразумевается под «процессором»?
                        Илья пишет что используется GP107GL Quadro P400.
                        Имеется ввиду её процессор или обычный процессор?
                          0
                          Имеется в виду ситуация, когда декодинг ведётся обычным процессором, а энкодинг на GPU.

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