Nvidia DeepStream - широко известный в узких кругах инструмент для инференса на нейронных сетях и другой высокопроизводительной обработки видео-потоков в реальном времени на оборудовании от Nvidia. Важнейшими компонентами, обеспечивающими эффективность применения DeepStream являются:

  • встроенные модули аппаратного декодирования видео NVDEC из разных форматов (H264, HEVC, AV1, MPEG-1, MPEG-2, VP8, VP9);

  • встроенные модули аппаратного кодирования видео NVENC в разные форматы (H264, HEVC, AV1, MPEG-1, MPEG-2, VP8, VP9).

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

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

Сам пайплайн DeepStream работает на движке Gstreamer с дополнительными плагинами от Nvidia, обеспечивающими всякие хитрые операции (например, dewarp видео с fisheye в несколько плоских картинок) и взаимодействие с TensorRT через плагины инференса. Так же есть плагины для трекинга и других операций. Полный список доступен по этой ссылке.

В итоге, используя DeepStream, вы можете получить очень высокое быстродействие, несравнимое с OpenCV/PyTorch, TensorFlow, etc. не погружаясь в дебри CUDA-разработки.

Наша команда занимается разработкой и оптимизацией пайплайнов видео аналитики для работы на базе DeepStream. В текущем проекте мы обнаружили, что некоторые пайплайны виснут. Поскольку обработка аналитическая, на первом этапе мы просто реализовали в K8s keepalive и несколько раз в сутки рестартовали пайплайн, когда он переставал отвечать. Само по себе расследование того, где это происходит, представлялось затруднительным и не входило в приоритетные задачи.

Не так давно мы приступили к реализации новой функции, которая подразумевает реакцию на события с низкой задержкой, поэтому проблема снова стала актуальной. За пару дней мы смогли локализовать проблему и найти видео-фрагмент, который вызывает зависание пайплайна DeepStream в модуле NVDEC при декодировании видео в формате H264.

Таким образом, фактический злоумышленник имеет способ вызвать зависание пайплайна DeepStream с помощью формирования специального фрейма. В нашем случае злоумышленником была обычная камера видео-наблюдения, установленная на площадке, где проводилось тестирование. Мы смогли локализовать видео-фрагмент для стабильного воспроизведения проблемы. Поскольку DeepStream широко внедряется в умных городах, на предприятиях и производстве, подобный баг является критическим.

В нашей среде он воспроизводится в следующем окружении на GPU Quadro RTX 4000, Geforce RTX 2080:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 465.19.01    Driver Version: 465.19.01    CUDA Version: 11.3     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:02:00.0 Off |                  N/A |
| 30%   34C    P5    17W / 215W |    434MiB /  7982MiB |     15%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

Для воспроизведения бага можно воспользоваться простейшим пайплайном Gstreamer:

gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! fakesink

Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock

Пайплайн не завершается, зависает. Работает на картах серии Turing (на Ampere не проверяли, в нашей лабе их еще нет).

Обработка того же куска видео с помощью декодера на базе CPU (стандартный Gstreamer и Intel VAAPI) успешно отрабатывает:

gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! avdec_h264 ! fakesink

Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Redistribute latency...
Redistribute latency...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
Got EOS from element "pipeline0".
Execution ended after 0:00:00.220984700
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...

При запуске пайплайна в режиме Debug, можно увидеть, что зависание происходит именно в декодере:

GST_DEBUG=5 gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! fakesink

...
0:00:00.614602755 19768 0x557836592cf0 DEBUG             GST_MEMORY gstmemory.c:139:gst_memory_init: new memory 0x5578365c8470, maxsize:71 offset:0 size:64
0:00:00.614610302 19768 0x557836592cf0 DEBUG        GST_PERFORMANCE gstallocator.c:465:_sysmem_copy: memcpy 64 memory 0x5578365c8390 -> 0x5578365c8470
0:00:00.614618503 19768 0x557836592cf0 DEBUG             GST_BUFFER gstbuffer.c:2202:gst_buffer_add_meta: alloc metadata 0x7f0d6000daf8 (GstVideoMeta) of size 112
0:00:00.614625121 19768 0x557836592cf0 DEBUG              videometa gstvideometa.c:86:gst_video_meta_transform: copy video metadata
0:00:00.614631365 19768 0x557836592cf0 DEBUG             GST_MEMORY gstmemory.c:88:_gst_memory_free: free memory 0x5578365c8390
0:00:00.614640158 19768 0x557836592cf0 DEBUG               GST_PADS gstpad.c:4072:gst_pad_query:<fakesink0:sink> sent query 0x7f0d6054f590 (allocation), result 0
0:00:00.614646925 19768 0x557836592cf0 DEBUG               GST_PADS gstpad.c:4117:gst_pad_query:<fakesink0:sink> query failed
0:00:00.614653488 19768 0x557836592cf0 DEBUG               GST_PADS gstpad.c:4238:gst_pad_peer_query:<nvv4l2decoder0:src> query failed
0:00:00.614660986 19768 0x557836592cf0 DEBUG                   v4l2 gstv4l2object.c:4389:gst_v4l2_object_stop:<nvv4l2decoder0:src> stopping

Видео-фрагмент, на котором воспроизводится проблема можно скачать здесь.

По факту проблемы мы обратились к нашему партнеру, который смог воспроизвести проблему на Nvidia T4 и дал еще больше данных:

BCM:: counter - 72
BCM:: counter - 73
BCM:: counter - 74
BCM:: counter - 75
BCM:: counter - 76
Redistribute latency...
Redistribute latency...
Redistribute latency...
Redistribute latency...
ERROR: from element /GstPipeline:pipeline0/GstQTDemux:qtdemux0: Internal data stream error.
Additional debug info:
qtdemux.c(6073): gst_qtdemux_loop (): /GstPipeline:pipeline0/GstQTDemux:qtdemux0:
streaming stopped, reason not-negotiated (-4)
Execution ended after 0:00:01.682024126
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...

То есть, 76й фрейм вызывает сбой обработки, который на CPU вызывает завершение пайплайна, а при использовании NVDEC пайплайн зависает наглухо.

По факту на форуме Nvidia был открыт баг. Пока никаких дельных советом от Nvidia нет. Ждет или фикс или хотя бы инструкции о том, как можно сделать "антивирус", поставив его перед NVDEC, чтобы определять и отбрасывать зловредные фреймы. Как сказал архитектор нашего партнера: "главное, чтобы ошибка была не в чипе".