Skip to main content

Overview

v4l2h265dec is a hardware-accelerated video decoder that uses the Video4Linux2 (V4L2) stateful decoder API to offload H.265 (HEVC) video decoding to the Qualcomm Video Processing Unit (VPU). This plugin is provided and maintained by the GStreamer community. This document focuses on its usage in conjunction with Qualcomm-specific QIM SDK GStreamer plugins, along with relevant use cases and internal architectural considerations. v4l2h265dec is typically used to decode H.265 streams provided by:
  • File sources
  • RTSP sources
  • HLS/HTTP streaming sources
The element is responsible only for decoding video frames. It does not demux or parse streams on its own — those operations must be handled by peer GStreamer elements upstream (e.g., qtdemux and h265parse).

Example Pipeline

1

Download Required Files

h265_1080p.mp4 file is used here as an example.
2

Copy files to device

# Replace $HOME to the appropriate device path before running the commands.
# For QLI:    /root
# For Ubuntu: /home/ubuntu
# Modify this based on your platform and ensure files are copied to the correct location on the device.
# Run from your host machine — replace <user> and <device-ip>

ssh <user>@<device-ip> "mkdir -p $HOME/{media}"
scp h265_1080p.mp4 <user>@<device-ip>:$HOME/media/
3

Connect to device

# Run from your host machine — replace <user> and <device-ip>
ssh <user>@<device-ip>
4

Set environment variables

Run below command on your device
export SRC_VIDEO_NAME=h265_1080p.mp4
5

Run the pipeline

gst-launch-1.0 -e \
  filesrc location=$HOME/media/$SRC_VIDEO_NAME ! \
  qtdemux ! queue ! h265parse ! \
  v4l2h265dec capture-io-mode=4 output-io-mode=4 ! \
  video/x-raw,format=NV12 ! queue ! \
  waylandsink fullscreen=true sync=false

Key Responsibilities

v4l2h265dec is responsible for:
  • Hardware acceleration — offloads H.265/HEVC decoding to the dedicated VPU
  • V4L2 state management — manages the V4L2 stateful decoder state machine (device open, buffer allocation, stream on/off)
  • Buffer I/O management — handles buffer exchange between GStreamer and the V4L2 driver using DMABuf, MMAP, or UserPtr modes
  • Format negotiation — negotiates raw output formats supported by the hardware, including UBWC-compressed formats (NV12_Q08C, NV12_Q10LE32C) for reduced memory bandwidth
  • Multi-stream support — supports multiple concurrent decoder instances (subject to hardware resource limits)
  • Error handling — supports decoder error controls such as max-errors and corrupted-frame discard behavior

Hierarchy

GObject
   GstObject
      GstElement
         GstVideoDecoder
            GstV4l2VideoDec
               v4l2h265dec

Pad Templates

sink

Capabilities
video/x-h265stream-format: byte-stream
alignment: au
profiles: { main, main-still-picture, main-10 }
levels: 1–6.2
Availability: Always
Direction: sink

src

Capabilities
video/x-rawformat: { NV12_Q08C, NV12_Q10LE32C, NV12, NV21 }
width: [1, 32768]
height: [1, 32768]
Availability: Always
Direction: source
The exact set of profiles and levels reported depends on what the underlying V4L2 driver enumerates via V4L2_CID_MPEG_VIDEO_HEVC_PROFILE and V4L2_CID_MPEG_VIDEO_HEVC_LEVEL.

Element Properties

PropertyDescription
automatic-request-sync-point-flagsFlags used when automatically requesting sync points.

Type: Flags
Default: 0x3 (corrupt-output+discard-input)
Flags: readable/writable
automatic-request-sync-pointsAutomatically requests sync points such as keyframes or IDR frames when useful, for example after decoding errors.

Type: Boolean
Default: false
Flags: readable/writable
capture-io-modeI/O mode for the capture queue (src pad). Controls how decoded frame buffers are allocated and transferred.

Type: Enum
Default: 0, "auto"
Flags: readable/writable
deviceThe V4L2 device node path. Set automatically at registration time and read-only after element creation.

Type: String
Default: "/dev/video32"
Flags: readable
device-fdFile descriptor of the opened V4L2 device. A value of -1 indicates that the device is not open.

Type: Integer
Default: -1
Flags: readable
device-nameHuman-readable name of the V4L2 device as reported by the driver via VIDIOC_QUERYCAP.

Type: String
Default: NULL
Flags: readable
discard-corrupted-framesWhen enabled, frames marked as corrupted by the driver are dropped instead of passed downstream.

Type: Boolean
Default: false
Flags: readable/writable
extra-controlsExtra V4L2 controls (CIDs) to set on the device, specified as a GstStructure. Applied via VIDIOC_S_EXT_CTRLS.

Type: GstStructure
Default: NULL
Flags: readable/writable
max-errorsMaximum number of consecutive decoder errors before the element returns a flow error. A value of -1 means unlimited.

Type: Integer
Default: -1
Flags: readable/writable
min-force-key-unit-intervalMinimum interval in nanoseconds between force-keyunit requests sent upstream. A value of 0 means no minimum interval.

Type: Unsigned Integer64
Default: 0
Flags: readable/writable
output-io-modeI/O mode for the output queue (sink pad). Controls how compressed input buffers are submitted to the driver.

Type: Enum
Default: 0, "auto"
Flags: readable/writable
qosHandles Quality-of-Service events from downstream. When enabled, the decoder may drop frames to maintain real-time playback.

Type: Boolean
Default: true
Flags: readable/writable

I/O Mode Values

Both capture-io-mode and output-io-mode accept the same GstV4l2IOMode enumeration:
ValueIntegerDescription
auto0Automatically select the best I/O mode. Prefers DMABuf export for capture, MMAP for output.
rw1Use read/write system calls. Rarely used for M2M devices.
mmap2Use kernel memory-mapped buffers (V4L2_MEMORY_MMAP).
userptr3Use user-space pointer buffers (V4L2_MEMORY_USERPTR).
dmabuf4Export buffers as DMABuf file descriptors (V4L2_MEMORY_DMABUF). Enables zero-copy sharing with downstream GPU/display elements.
dmabuf-import5Import DMABuf file descriptors from an external allocator into the V4L2 queue.

Internal Architecture

v4l2h265dec operates using two V4L2 queue objects internally:
  • Output queue (V4L2_BUF_TYPE_VIDEO_OUTPUT) — receives compressed bitstream buffers from upstream
  • Capture queue (V4L2_BUF_TYPE_VIDEO_CAPTURE) — produces decoded raw video frames for downstream

State Transitions

TransitionDecoder BehaviorNotes
NULL → READYElement becomes ready. Static configuration available.Configure capture-io-mode and output-io-mode before active decoding.
READY → PAUSEDDecoder activates. V4L2 session setup begins.Caps negotiation: sink is video/x-h265, src is video/x-raw (NV12/NV12_Q08C).
PAUSED → PLAYINGContinuous decode starts.With capture-io-mode=4 and output-io-mode=4, pipeline is configured for DMABuf zero-copy.
PLAYING → PAUSEDDecoding paused at GStreamer scheduling level.No full decoder teardown. Resume continues from current position.
PLAYING/PAUSED → READYDecoder stops. V4L2 streaming stopped, buffers released.Key cleanup transition.
READY → NULLElement fully shut down. All resources released.
EOS while PLAYINGDecoder drains pending input, outputs remaining frames, then forwards EOS.
FLUSHCurrent decode interrupted. Queued buffers discarded.Common during seek or pipeline reset. Decoder waits for new IDR frame after flush.

Dynamic Resolution Change

v4l2h265dec supports mid-stream dynamic resolution changes without requiring a pipeline restart, handled through the V4L2 source change event mechanism:
  1. At initialization, the decoder subscribes to V4L2_EVENT_SOURCE_CHANGE events on the capture queue.
  2. When the driver detects a resolution change in the bitstream, it signals the event.
  3. The decoder stops the capture queue, discards the existing buffer pool, re-negotiates format via VIDIOC_G_FMT, sets a new output state with updated dimensions, and reallocates the capture buffer pool.
This allows seamless decoding of adaptive bitrate streams and content with embedded resolution changes.

Memory and Buffer Management

DMABuf Usage

Setting capture-io-mode=4 exports decoded frame buffers as DMABuf file descriptors. These can be imported directly by waylandsink or qtivcomposer without CPU memory copies, enabling zero-copy pipelines.

Alignment Requirements

Decoded buffers follow Qualcomm hardware alignment requirements (e.g., 128-byte stride alignment).

Format Support

Standard NV12 is common. UBWC (Universal Bandwidth Compression) is supported through formats like NV12_Q08C to reduce memory bandwidth.

Codec Header Initialization

Before the first frame is decoded, the decoder sends the codec-specific header data (VPS/SPS/PPS) to the output queue. If the input caps contain codec_data (e.g., from an hvcC box in MP4), that data is sent first. Otherwise, the first input buffer itself is used as the initialization data.

Latency

The decoder computes and reports pipeline latency based on the minimum number of capture buffers required by the driver and the frame duration:
latency = min_buffers × frame_duration

Drain and Flush

  • Drain — sends V4L2_DEC_CMD_STOP to signal end-of-stream; waits for all remaining frames to be produced
  • Flush — stops both queues, resets buffer pools, and restarts streaming; used during seek operations

Usage

Ensure you have followed the prerequisites before continuing

Single RTSP Stream — Decode and Display

Demonstrates real-time RTSP stream reception, H.265 network depayloading, and hardware-accelerated decoding with DMABuf zero-copy for Wayland preview.
gst-launch-1.0 -e \
  rtspsrc location="rtsp://<user>:<password>@<ip>:<port>/<channel>" ! \
  queue ! rtpptdemux ! rtph265depay ! h265parse ! \
  v4l2h265dec output-io-mode=dmabuf capture-io-mode=dmabuf ! \
  video/x-raw,format=NV12 ! queue ! \
  waylandsink fullscreen=true

Four-Stream Side-by-Side Composition

Demonstrates decoding four input streams using four hardware decoder instances, arranging them in a 2×2 grid using qtivcomposer, and displaying the composed frame.
gst-launch-1.0 -e --gst-debug=2 \
  qtivcomposer name=mixer \
    sink_0::position="<0, 0>"     sink_0::dimensions="<960, 540>" \
    sink_1::position="<960, 0>"   sink_1::dimensions="<960, 540>" \
    sink_2::position="<0, 540>"   sink_2::dimensions="<960, 540>" \
    sink_3::position="<960, 540>" sink_3::dimensions="<960, 540>" \
  ! queue ! waylandsink fullscreen=true sync=false enable-last-sample=false \
  filesrc location=/data/input0_1080p_h265.mp4 ! \
    qtdemux ! queue ! h265parse ! v4l2h265dec output-io-mode=4 capture-io-mode=4 ! queue ! mixer. \
  filesrc location=/data/input1_1080p_h265.mp4 ! \
    qtdemux ! queue ! h265parse ! v4l2h265dec output-io-mode=4 capture-io-mode=4 ! queue ! mixer. \
  filesrc location=/data/input2_1080p_h265.mp4 ! \
    qtdemux ! queue ! h265parse ! v4l2h265dec output-io-mode=4 capture-io-mode=4 ! queue ! mixer. \
  filesrc location=/data/input3_1080p_h265.mp4 ! \
    qtdemux ! queue ! h265parse ! v4l2h265dec output-io-mode=4 capture-io-mode=4 ! queue ! mixer.