JetPack의 에이전틱 비디오 파이프라인

JetPack 7.2.1의 PyNvVideoCodec 2.2와 DLPack 제로카피를 활용해 비디오 디코딩과 AI 모델 추론의 지연 시간을 최소화합니다.

무료 공개 · 최근 수정

다중 카메라 환경에서 발생하는 비디오 처리 병목

실시간 로보틱스와 엣지 컴퓨터 비전 시스템은 다수의 고해상도 카메라 스트림을 동시에 처리하면서 심각한 종단간 지연 시간(End-to-End Latency) 병목에 맞닥뜨립니다. 자율주행 모바일 로봇이나 4족 보행 로봇은 3대에서 6대 이상의 카메라로부터 초당 30프레임 이상의 H.264 또는 HEVC 압축 비디오 패킷을 받아옵니다. 이때 프레임을 풀고 신경망 모델에 넘겨 판단을 내린 뒤 디스플레이나 관제 센터로 재전송하는 전체 파이프라인이 하나의 주기 안에 완결되지 못하면, 로봇의 회피 기동이나 경로 계획이 수십 밀리초 이상 밀리는 치명적인 사고가 발생합니다.

기존의 파이썬 기반 비전 파이프라인에서 가장 흔하게 발생하는 문제는 CPU 소프트웨어 디코딩과 반복적인 메모리 복사입니다. OpenCV나 표준 FFmpeg 라이브러리로 비디오를 디코딩하면 압축 해제 연산이 CPU 코어의 연산력을 잠식하고, 디코딩된 프레임 버퍼가 호스트 메모리(RAM)와 GPU 디바이스 메모리(VRAM) 사이를 여러 차례 오가며 메모리 버스 대역폭을 낭비합니다. 복합 비전-언어 모델(VLM)이나 객체 탐지기가 작동하기도 전에 프레임 전처리 단계에서만 20ms 이상의 지연이 쌓여 실시간 제어 주기를 놓치게 됩니다.

PyNvVideoCodec 2.2와 DLPack 기반 제로카피 아키텍처

NVIDIA JetPack 7.2.1에 포함된 PyNvVideoCodec 2.2는 파이썬 런타임에서 Jetson의 하드웨어 비디오 엔진을 직접 제어할 수 있는 표준 바인딩을 제공합니다. C++ 기반의 저수준 GStreamer 플러그인을 복잡하게 작성하지 않고도 파이썬 스크립트 안에서 전용 디코더(NVDEC)와 인코더(NVENC) 회로를 온전히 구동할 수 있습니다.

이 라이브러리의 핵심 가치는 GPU 하드웨어 회로에서 디코딩된 비디오 프레임을 추가 복사 없이 텐서 연산기로 직접 넘기는 하드웨어 코덱 가속(Hardware Codec Acceleration) 메커니즘에 있습니다. NVDEC가 압축 비디오 비트스트림을 해제하면, DLPack 프로토콜과 CUDA 디바이스 버퍼를 통해 GPU에 상주하는 프레임을 PyTorch 텐서나 TensorRT 런타임으로 직접 넘깁니다.

이 방식은 호스트 RAM을 거치지 않는 제로카피 메모리 공유(Zero-copy Memory Sharing)를 실현하여 호스트와 디바이스 간 불필요한 메모리 복사를 크게 줄입니다. 복사 오버헤드를 줄임에 따라 초고해상도 4K 스트림이나 다채널 1080p 환경에서도 메모리 대역폭 포화 현상 없이 안정적인 프레임 레이트를 유지하는 데 유리합니다.

다음 데이터 흐름도는 카메라 영상이 하드웨어 디코더를 거쳐 제로카피 텐서로 변환되고, AI 추론과 하드웨어 인코더로 전달되는 전체 파이프라인의 연계 과정을 보여 줍니다.

카메라 입력이 하드웨어 디코더와 DLPack 제로카피를 거쳐 신경망 추론과 인코더로 전달되는 파이프라인

그림: 하드웨어 가속 디코더와 DLPack 제로카피 기반의 비디오 파이프라인 흐름

압축 스트림은 NVDEC를 통해 하드웨어 수준에서 디코딩된 뒤, DLPack 버퍼 교환을 거쳐 추가 복사 없이 곧바로 TensorRT 엔진과 NVENC 인코더로 연결됩니다.

백그라운드 프리페칭과 ThreadedDecoder의 동작 원리

영상 처리와 신경망 추론을 단일 스레드 루프에서 순차적으로 실행하면 하드웨어 가속기를 사용하더라도 연산 자원이 낭비되는 구간이 발생합니다. 모델이 추론을 수행하는 동안에는 디코더가 대기하고, 디코더가 다음 프레임을 읽어오는 동안에는 GPU 텐서 코어가 유휴 상태에 머물기 때문입니다.

PyNvVideoCodec의 스레드 디코더(ThreadedDecoder) 클래스는 이 문제를 해결하기 위해 백그라운드 워커 스레드와 내부 링 버퍼를 활용한 비동기 프리페칭 구조를 채택합니다. 주 실행 흐름이 현재 프레임에 대해 신경망 추론을 수행하는 동안, 백그라운드 스레드는 다음 비트스트림 패킷을 디코딩 장치에 밀어 넣어 링 버퍼에 미리 대기시켜 둡니다.

[순차 실행 구조 (설명용 가정 수치)]
|-- NVDEC 디코딩 (8ms) --|-- TensorRT 추론 (15ms) --|-- NVDEC 디코딩 (8ms) --|  => 프레임당 23ms (약 43 FPS)
 
[ThreadedDecoder 파이프라인 구조 (설명용 가정 수치)]
Worker: |-- NVDEC 프레임 1 --|-- NVDEC 프레임 2 --|-- NVDEC 프레임 3 --|
Main:   |      대기          |-- TensorRT 추론 1 -|-- TensorRT 추론 2 -|  => 프레임당 15ms (약 66 FPS)

이와 같은 이중 버퍼링 기법을 통해 디코딩 소요 시간과 신경망 추론 소요 시간이 시간 축 위에서 중첩됩니다(위 다이어그램의 8ms와 15ms는 동작 원리를 설명하기 위한 가정 수치이며 실제 지연 시간은 실행 환경에 따라 다릅니다). 결과적으로 전체 파이프라인의 처리 속도는 두 작업 시간의 단순 합산이 아니라, 둘 중 더 긴 작업 시간에 의해서만 결정되므로 전체 처리량이 향상됩니다.

[참고] DLPack 인터페이스 표준의 장점

DLPack은 서로 다른 딥러닝 프레임워크와 메모리 할당자 간에 GPU 텐서 메모리를 상호 교환하기 위한 오픈소스 헤더 표준 규격입니다. PyNvVideoCodec으로 추출한 프레임을 torch.from_dlpack() 함수에 넘기면, 데이터 메모리 재할당이나 CPU-GPU 간 데이터 전송 없이 단 몇 마이크로초 수준의 포인터 래핑만으로 온전한 텐서 객체가 생성됩니다.

jetson-videosdk 스킬을 통한 자율 파이프라인 구축

최적의 비디오 파이프라인을 구축하려면 코덱 종류(H.264, HEVC, AV1), 해상도, 프레임 레이트, 하드웨어 프리셋, 비트레이트 제어 알고리즘(CBR, VBR) 등 수많은 엔지니어링 매개변수를 조정해야 합니다. 개발자가 이 수치를 수동으로 일일이 튜닝하는 대신, 자연어 지시를 실행 가능한 레시피로 전환하는 도구가 에이전틱 비디오 스킬(Agentic Video Skill)입니다.

jetson-videosdk 스킬을 탑재한 코딩 에이전트는 다음과 같은 4단계 자율 워크플로우를 통해 목표 요구사항에 맞는 비디오 파이프라인을 자동으로 완성합니다.

  1. 기능 탐색·설정·보고: 현재 시스템의 SoC 아키텍처(Jetson AGX Thor 또는 Orin 계열)를 조회하고, 지원하는 코덱 기능과 NVDEC/NVENC 사양을 탐색하여 보고합니다.
  2. 인코더 레시피 생성: "4채널 1080p30 저지연 비디오를 처리해 추론 모델에 전달" 같은 목표 요구사항에 맞춰 인코더 설정, 버퍼 크기, DLPack 인터페이스 바인딩 레시피 코드를 구성합니다.
  3. 성능·품질 벤치마크: 구성된 비디오 파이프라인을 구동하여 실제 처리량(FPS), 지연 시간, 화질 및 하드웨어 코덱 가속기 점유율을 벤치마크합니다.
  4. 코덱 워크플로 검증: 설정된 파이프라인이 목표 지연 시간(예: 33ms 이하)이나 요구 품질을 충족하는지 워크플로 전체의 신뢰성을 검증하고 매개변수를 미세 조정합니다.

나아가 JetPack 7.2.1은 최근 발표된 T3000의 성능을 Jetson Thor AGX 개발 키트의 T5000 모듈에서 에뮬레이션하는 기능을 지원합니다. 개발자는 실제 차세대 실리콘 칩을 직접 보유하기 전이라도 에이전트와 에뮬레이션 도구를 활용하여 향후 하드웨어 사양에서 동작할 고성능 멀티 비디오 파이프라인의 처리 한계를 미리 검증할 수 있습니다.

PyNvVideoCodec 2.2 디코딩 및 제로카피 연동 예제

다음 파이썬 스크립트는 PyNvVideoCodec 2.2의 ThreadedDecoder를 생성하고, 백그라운드 스레드에서 디코딩된 프레임을 DLPack을 통해 PyTorch 텐서로 변환하는 실행 뼈대를 나타냅니다.

import time
import torch
from PyNvVideoCodec import ThreadedDecoder, OutputColorType
import pycuda.driver as cuda
from pycuda.autoinit import context
 
def run_low_latency_pipeline(video_source: str, batch_size: int = 3, max_batches: int = 30):
    # 1. Jetson 하드웨어 비디오 엔진을 활용하는 ThreadedDecoder 초기화
    decoder = ThreadedDecoder(
        enc_file_path=video_source,
        buffer_size=12,          # 미리 디코딩해 둘 프레임 수
        gpu_id=0,
        use_device_memory=True,  # 프레임을 GPU 메모리에 유지
        output_color_type=OutputColorType.RGBP,  # 평면 RGB(CHW), 딥러닝 모델용
    )
    metadata = decoder.get_stream_metadata()
    
    total_frames = 0
    start_time = time.perf_counter()
    batch_count = 0
    
    while batch_count < max_batches:
        # 백그라운드 스레드에서 디코딩되어 대기 중인 프레임 배치 인출 (빈 목록이면 스트림 끝)
        frames = decoder.get_batch_frames(batch_size)
        if not frames:
            break
            
        # 2. DLPack을 이용한 제로카피 GPU 텐서 변환
        tensors = [torch.from_dlpack(f) for f in frames]
        
        # 3. 인공지능 추론 모델 입력에 맞춘 가벼운 정규화 및 모의 연산
        for frame_tensor in tensors:
            normalized = frame_tensor.float() / 255.0
            _ = torch.mean(normalized)
            total_frames += 1
            
        batch_count += 1
 
    elapsed = time.perf_counter() - start_time
    fps = total_frames / elapsed if elapsed > 0 else 0
    print(f"처리 프레임: {total_frames}, 소요 시간: {elapsed:.3f}초, 처리 속도: {fps:.1f} FPS")
 
if __name__ == "__main__":
    test_stream = "input.mp4"
    run_low_latency_pipeline(test_stream)

파이프라인의 실제 처리 속도와 지연 시간은 실행 대상 Jetson 기기 사양, 영상 해상도, 비디오 코덱 및 배치 크기에 따라 달라집니다. 따라서 특정 단일 수치를 일반화하기보다 jetson-video-benchmark 스킬을 활용하거나 현장 환경에서 직접 프로파일링하여 성능을 측정해야 합니다. CPU 부하를 최소화하면서 GPU 메모리에 텐서가 상주하므로, 잔여 GPU 자원을 VLA 모델이나 멀티모달 에이전트의 실시간 추론 연산에 온전히 할당할 수 있습니다.

참고 문서

설명이 어렵거나 잘못된 부분을 발견하셨나요?

문서 수정 의견 보내기