Skip to main content

streaming-frames

Submitting video frames to ovstream (raw CUDA BGRA8 or pre-encoded bitstreams). Use when user asks how to push frames, the video frame format, pitch alignment, or pre-encoded passthrough.

معلومات المصدر

المستودع
NVIDIA-Omniverse/ovstream
آخر نشاط في المصدر
٦ أكتوبر ٢٠٢٦ في ١٩:٢٦
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٢
التفرعات
٥

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
streaming-frames
description
Submitting video frames to ovstream (raw CUDA BGRA8 or pre-encoded bitstreams). Use when user asks how to push frames, the video frame format, pitch alignment, or pre-encoded passthrough.
license
LicenseRef-NvidiaProprietary
author
NVIDIA ovstream
tags
["ovstream","frames","cuda","pre-encoded"]
tools
["Read","Grep"]
# Streaming Frames ## Overview `stream_video` / `ovstream_stream_video` is the per-frame entry point. It accepts a `VideoFrame` / `ovstream_video_frame_t` descriptor that **discriminates** on which fields are non-zero: - **`pitch_bytes > 0`** → raw CUDA BGRA8 buffer. `buffer` points at GPU memory; the SDK reads it directly and encodes via NVENC. This is the path 95% of apps take. - **`size_bytes > 0`** → pre-encoded bitstream (H.264 / H.265 / AV1, or a `CUSTOM` GStreamer-pipeline payload on RTSP). `buffer` is a **host** pointer to the encoded payload; the SDK packetizes and forwards without re-encoding. (Unlike raw CUDA, pre-encoded paths do not accept GPU-resident buffers.) - **Both `pitch_bytes == 0` and `size_bytes == 0`** → `TENSOR` input: `buffer` is a `DLTensor*` (DLPack) and `width` / `height` are also 0; the SDK reads shape, dtype, stride, and device from the DLTensor itself. Construct via `VideoFrame.from_dlpack(obj)` in Python (any framework that exposes `__dlpack__` — Warp, PyTorch, JAX, CuPy). The server's configured `video_input` must be `TENSOR`. The exception above aside, exactly one of `pitch_bytes` / `size_bytes` must be non-zero, and the choice must match the server's configured `video_input`. ## Python > **Source:** `examples/python/basic_stream/main.py` snippet `stream-loop` Construct a `VideoFrame` once, reuse the same buffer pointer across frames, mutate the buffer contents in place between calls. ```python frame = ovstream.VideoFrame( buffer=cuda_ptr.value, # device pointer (int) width=1920, height=1080, pitch_bytes=1920 * 4, # BGRA = 4 bytes/px ) for _ in range(num_frames): # ... fill the CUDA buffer with this frame's content ... try: server.stream_video(frame) except ovstream.OvstreamError: pass # no client connected yet, etc. ``` ## C > **Source:** `examples/c/basic_stream/main.cu` snippet `stream-loop` ## Buffer lifetime contract `stream_video` **stages the frame data into server-owned memory before returning** on every backend (RTSP / WebRTC / native / SHM / CUDASHM), with one exception: `webrtc.resizable` disables StreamSDK's frame copying, so in that mode the buffer must be a persistent allocation at the maximum resolution and per-frame allocations are NOT safe (see the Resizable streams section below). Outside that mode, the caller may reuse or free `buffer` (including a stack buffer or a per-iteration `std::vector`) as soon as the call returns. Reusing a single long-lived buffer across frames is still the recommended pattern because it avoids per-frame allocation, but per-frame allocations are equally safe. Producers that want to skip the staging copy and overlap their next render with the encoder's read should pass a CUDA event via `frame.sync.wait_event` — backends host-block on it before staging, so the producer's render can chain after the wait. ## Pitch `pitch_bytes` is the stride of one row, not `width * 4`. NVENC tolerates the buffer being padded for DMA alignment — use `cudaMallocPitch` to get a pitch that NVENC likes. > **Source:** `examples/c/basic_stream/main.cu` snippet `cuda-buffer-alloc` ## Pre-encoded passthrough If you already have encoded NAL units / OBUs (e.g. from a video decoder, a recorded file, or an upstream encoder), set `video_input` in `ServerConfig` to one of `H264 / H265 / AV1` at server start, then submit frames with `size_bytes` populated instead of `pitch_bytes`. The SDK forwards the bitstream as-is. > **Source:** `examples/c/pre_encoded_stream/main.cpp` snippet `configure-pre-encoded` > > Followed by: `examples/c/pre_encoded_stream/main.cpp` snippet `pre-encoded-loop` RTSP and WebRTC both support pre-encoded H.264/H.265; WebRTC additionally supports AV1. RTSP does not currently support AV1. Neither SHM nor CUDASHM supports pre-encoded — both carry raw BGRA8 only. ## Per-frame metadata (optional) `ovstream_video_frame_t` has three optional metadata fields — `metadata` (blob pointer), `metadata_size` (bytes), and `metadata_uuid` (16-byte type identifier). Populate them on the frame descriptor you pass to the regular `stream_video` call; there are no separate `*WithMetadata` entry points. - For RTSP pre-encoded streams, the SDK injects the metadata as SEI User Data Unregistered NAL units (ITU-T H.264/H.265). - For RTSP raw-CUDA streams (encoder owns the bitstream) and on WebRTC / SHM / CUDASHM, metadata is currently ignored. Use only if you need per-frame tags that traverse the wire. ## Key Types / Functions | Python | C | |--------|---| | `ovstream.VideoFrame(buffer=..., width=..., height=..., pitch_bytes=...)` | `ovstream_video_frame_t` | | `ovstream.VideoFrame.from_dlpack(obj)` (TENSOR input) | `ovstream_video_frame_t` with `buffer` set to a `DLTensor*` | | `server.stream_video(frame)` | `ovstream_stream_video(server, &frame)` | | `ovstream.VideoInput.{CUDA, TENSOR, CUSTOM, H264, H265, AV1}` | `OVSTREAM_VIDEO_INPUT_{CUDA, TENSOR, CUSTOM, H264, H265, AV1}` | ## Resizable streams Per-frame dimensions ARE the resize mechanism — there is no separate resize call. On SHM/CUDASHM this always works: `config.width` / `height` are the maximum, per-slot headers carry each frame's actual dimensions, and consumers react to changes. On WebRTC/native it is opt-in via `webrtc.resizable` (`webrtc_resizable=True`), with the same semantics: submit frames at any size up to the maximum and the stream resolution follows — the SDK floors unaligned dimensions (width to a multiple of 8, height to even) rather than rejecting them. Allocate the framebuffer once at the maximum and render into a sub-region (the pitch stays the maximum-width pitch) — required, not just convenient: resizable mode disables StreamSDK's frame copying, so the immediate-reuse buffer lifetime contract does not apply; the buffer must be a persistent allocation and per-frame reallocation is not supported. It must also be plain device memory (`cudaMalloc` / `cudaMallocPitch`, or in Warp an array allocated under `wp.ScopedMempool(device, False)`): the encoder registers the buffer directly and cannot register CUDA memory-pool allocations (`cudaMallocAsync`), so `stream_video` rejects such frames on a resizable stream with an explicit error. RTSP is strictly fixed-size. See the `stream-messages` skill for delivering client resize requests to your render loop. ## Common Pitfalls - `pitch_bytes` and `size_bytes` are mutually exclusive. Setting both is an error. The only frame shape where both are zero is `TENSOR` input (dimensions and stride come from the DLTensor). - For BGRA8, `pitch_bytes >= width * 4`. Using `pitch_bytes = width * 4` is fine when `cudaMallocPitch` happens to align that way, but trust `cudaMallocPitch`'s returned pitch — don't hard-code. - A non-`SUCCESS` `stream_video` return is normal early in a stream's life (no client connected yet) — backends mostly accept early frames silently for pipeline warm-up, but if they do surface a failure, log at verbose and continue. - BGRA channel order, not RGBA. Any producer that emits RGBA needs to swizzle on the GPU before handing the buffer to ovstream. ovrtx's `LdrColor` is RGBA8; ovrtx 0.5 can emit a BGRA8 render var through its SPG `Swizzle` node, which is what [`examples/python/ovrtx_stream`](../../examples/python/ovrtx_stream/) does, so no application-side kernel is needed for that pairing. - Audio frames go through `stream_audio` / `ovstream_stream_audio` (16-bit PCM, WebRTC/native only). RTSP, SHM, and CUDASHM don't support audio — `stream_audio` returns `OVSTREAM_API_NOT_SUPPORTED` (Python: `OvstreamError` with `.status == ApiStatus.NOT_SUPPORTED`) on those.
عرض على GitHub