Захват рабочего стола с помощью FFmpeg со скоростью 60 кадров в секунду с использованием кодека NVENC

У меня проблемы с записью рабочего стола на скорости 60FPS с использованием последней версии FFmpeg, скомпилированной для Windows, с кодеком NVENC. Метаданные говорят, что файл имеет 60 кадров в секунду, но когда я его воспроизводю, я ясно вижу, что это не 60 кадров в секунду.

Командная строка, которую я использую, выглядит следующим образом:

ffmpeg -y -rtbufsize 2000M -f gdigrab -framerate 60 -offset_x 0 -offset_y 0 -video_size 1920x1080 -i desktop -c:v h264_nvenc -preset:v fast -pix_fmt nv12 out.mp4

Я пытался использовать буфер в реальном времени, использовать другое устройство DirectShow, менять профиль или увеличивать битрейт, но видео всегда кажется на скорости 30 кадров в секунду.

Запись экрана с использованием NVIDIA ShadowPlay работает хорошо, поэтому я знаю, что это возможно на моей машине.

Используя FFprobe для проверки выходного файла ShadowPlay, я вижу:

Поток #0:0(und): видео: h264 (высокий) (avc1 / 0x31637661), yuv420p(tv, smpte170m/smpte170m/bt470m), 1920x1080 [SAR 1:1 DAR 16:9], 4573 кб / с, 59,38 кадров в секунду, 240 тбр, 60 тыс тбн, 120 тбк (по умолчанию)

Но если я заставлю свой вывод иметь тот же битрейт и профиль, я получу:

Поток № 0: 0 (und): Видео: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 5519 кбит / с, 60 кадров в секунду, 60 тбр, 15360 тбит, 120 TBC (по умолчанию)

я могу видеть tbr а также tbn разные, поэтому я знаю, что мой вывод дублирует кадры.

Для тестирования у всех моих записей была эта тестовая страница с частотой кадров в 60 кадров, и я четко видел различия.

Я знаю, что ShadowPlay, вероятно, делает гораздо больше под капотом, чем FFmpeg, используя тот же кодек. Я знаю, что OBS может сделать это довольно легко, но я хочу понять, что я делаю неправильно. Может быть, это какое-то ограничение FFmpeg?

Полный консольный вывод

Используя команду -v trace:

[gdigrab @ 0000000002572cc0] Capturing whole desktop as 1920x1080x32 at (0,0)
[gdigrab @ 0000000002572cc0] Cursor pos (1850,750) -> (1842,741)
[gdigrab @ 0000000002572cc0] Probe buffer size limit of 5000000 bytes reached
[gdigrab @ 0000000002572cc0] Stream #0: not enough frames to estimate rate; consider increasing probesize
[gdigrab @ 0000000002572cc0] stream 0: start_time: 1467123648.275 duration: -9223372036854.775
[gdigrab @ 0000000002572cc0] format: start_time: 1467123648.275 duration: -9223372036854.775 bitrate=3981337 kb/s
Input #0, gdigrab, from 'desktop':
  Duration: N/A, start: 1467123648.275484, bitrate: 3981337 kb/s
    Stream #0:0, 1, 1/1000000: Video: bmp, 1 reference frame, bgra, 1920x1080 (0x0), 0/1, 3981337 kb/s, 60 fps, 1000k tbr, 1000k tbn, 1000k tbc
Successfully opened the file.
Parsing a group of options: output file out.mp4.
Applying option c:v (codec name) with argument h264_nvenc.
Applying option pix_fmt (set pixel format) with argument nv12.
Successfully parsed a group of options.
Opening an output file: out.mp4.
[file @ 0000000000e3a7c0] Setting default whitelist 'file,crypto'
Successfully opened the file.
detected 8 logical cores
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'video_size' to value '1920x1080'
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'pix_fmt' to value '30'
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'time_base' to value '1/1000000'
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'pixel_aspect' to value '0/1'
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'sws_param' to value 'flags=2'
[graph 0 input from stream 0:0 @ 000000000257ec00] Setting 'frame_rate' to value '60/1'
[graph 0 input from stream 0:0 @ 000000000257ec00] w:1920 h:1080 pixfmt:bgra tb:1/1000000 fr:60/1 sar:0/1 sws_param:flags=2
[format @ 000000000257ffc0] compat: called with args=[nv12]
[format @ 000000000257ffc0] Setting 'pix_fmts' to value 'nv12'
[auto-inserted scaler 0 @ 00000000025802c0] Setting 'flags' to value 'bicubic'
[auto-inserted scaler 0 @ 00000000025802c0] w:iw h:ih flags:'bicubic' interl:0
[format @ 000000000257ffc0] auto-inserting filter 'auto-inserted scaler 0' between the filter 'Parsed_null_0' and the filter 'format'
[AVFilterGraph @ 0000000000e373c0] query_formats: 4 queried, 2 merged, 1 already done, 0 delayed
[auto-inserted scaler 0 @ 00000000025802c0] w:1920 h:1080 fmt:bgra sar:0/1 -> w:1920 h:1080 fmt:nv12 sar:0/1 flags:0x4
[h264_nvenc @ 0000000000e3ca20] Nvenc initialized successfully
[h264_nvenc @ 0000000000e3ca20] 1 CUDA capable devices found
[h264_nvenc @ 0000000000e3ca20] [ GPU #0 - < GeForce GTX 670 > has Compute SM 3.0 ]
[h264_nvenc @ 0000000000e3ca20] supports NVENC
[mp4 @ 0000000000e3b580] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.
Output #0, mp4, to 'out.mp4':
  Metadata:
    encoder         : Lavf57.40.101
    Stream #0:0, 0, 1/15360: Video: h264 (h264_nvenc) (Main), 1 reference frame ([33][0][0][0] / 0x0021), nv12, 1920x1080, 0/1, q=-1--1, 2000 kb/s, 60 fps, 15360 tbn, 60 tbc
    Metadata:
      encoder         : Lavc57.47.100 h264_nvenc
    Side data:
      cpb: bitrate max/min/avg: 0/0/2000000 buffer size: 4000000 vbv_delay: -1
Stream mapping:
  Stream #0:0 -> #0:0 (bmp (native) -> h264 (h264_nvenc))
Press [q] to stop, [?] for help
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
Clipping frame in rate conversion by 0.000008
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
[gdigrab @ 0000000002572cc0] Cursor pos (1850,750) -> (1842,741)
*** 35 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1850,750) -> (1842,741)
*** 7 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1850,649) -> (1850,649)
*** 1 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1858,535) -> (1858,535)
*** 3 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1859,454) -> (1859,454)
*** 2 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1865,384) -> (1865,384)
*** 2 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1846,348) -> (1846,348)
*** 3 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1770,347) -> (1770,347)
*** 2 dup!
[gdigrab @ 0000000002572cc0] Cursor pos (1545,388) -> (1545,388)
*** 4 dup!
frame=   69 fps=0.0 q=35.0 size=     184kB time=00:00:00.63 bitrate=2384.0kbits/[gdigrab @ 0000000002572cc0] Cursor pos (1523,389) -> (1519,378)

2 ответа

Предполагая, что ваш фильтр DirectShow использует GDI, весьма вероятно, что весь рабочий стол не может быть захвачен со скоростью 60 кадров в секунду (Windows не очень хороша в этом). FFmpeg, вероятно, удваивает некоторые изображения до 60 кадров в секунду. DXGI должен обеспечивать более высокую производительность ( https://msdn.microsoft.com/en-us/library/windows/desktop/hh404487(v=vs.85).aspx), но я не знаю, что FFmpeg имеет встроенную поддержку для этого, Еще?

В настоящее время FFmpeg поддерживает фильтр, который можно использовать для установки частоты кадров вывода через частныйвариант.

Этот новыйФильтр возвращает исключительно аппаратные кадры D3D11 для кодирования или обработки на графическом процессоре, и поэтому любая форма программной обработки потребует вызова hwdownload и , таким образом, должна быть значительно быстрее, чем старые методы захвата GDI.

См. пример ниже, который захватывает весь рабочий стол:

      ffmpeg -fflags +genpts -init_hw_device d3d11va -threads:v 1 ^
-filter_complex "ddagrab=0:framerate=60:draw_mouse=0:video_size=1920x1080" ^
-c:v h264_nvenc -rc:v vbr -tune:v ll -preset:v p2 ^
-rc-lookahead:v 0 -delay:v 0 -b:v 0 -cq:v 19 -zerolatency:v 1 ^
-y -f mp4 "out.mp4"

Вы можете распечатать варианты использования ddagrab через:

      ffmpeg -h filter=ddagrab

Пример вывода:

      Filter ddagrab
  Grab Windows Desktop images using Desktop Duplication API
    Inputs:
        none (source filter)
    Outputs:
       #0: default (video)
ddagrab AVOptions:
   output_idx        <int>        ..FV....... dda output index to capture (from 0 to INT_MAX) (default 0)
   draw_mouse        <boolean>    ..FV....... draw the mouse pointer (default true)
   framerate         <video_rate> ..FV....... set video frame rate (default "30")
   video_size        <image_size> ..FV....... set video frame size
   offset_x          <int>        ..FV....... capture area x offset (from INT_MIN to INT_MAX) (default 0)
   offset_y          <int>        ..FV....... capture area y offset (from INT_MIN to INT_MAX) (default 0)
   output_fmt        <int>        ..FV....... desired output format (from 0 to INT_MAX) (default 8bit)
     auto            0            ..FV....... let dda pick its preferred format
     8bit            87           ..FV....... only output default 8 Bit format
     bgra            87           ..FV....... only output 8 Bit BGRA
     10bit           24           ..FV....... only output default 10 Bit format
     x2bgr10         24           ..FV....... only output 10 Bit X2BGR10
     16bit           10           ..FV....... only output default 16 Bit format
     rgbaf16         10           ..FV....... only output 16 Bit RGBAF16
   allow_fallback    <boolean>    ..FV....... don't error on fallback to default 8 Bit format (default false)
   force_fmt         <boolean>    ..FV....... exclude BGRA from format list (experimental, discouraged by Microsoft) (default false)

Приведенный выше фрагмент ffmpeg уже учитывает отсутствие определенного битрейта, установленного в вашем случае выше, придерживаясь соответствующего метода управления скоростью с минимально возможной задержкой.

Дополнительные ссылки:

Пожалуйста, обратитесь к этому ответу о различных элементах управления скоростью и предустановках, доступных в конкретных реализациях FFmpeg NVENC, а также об их влиянии на пропускную способность. Ваш опыт может варьироваться в зависимости от версий драйвера и компонентов ffmpeg.

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

Другие вопросы по тегам