Skip to content

VideoWriter() support 4 channel format (ie BGRA, BGRX) with GStreamer #96

Description

@lstahlman

Mirrored from upstream: opencv#28296
Original author: @timorkal · created 2025-12-24T16:18:39Z
Upstream labels: feature, category: videoio
Selection: mirrored by mirror-issues.py


Describe the feature and motivation

After looking at this code, looks like there is not support for the VideoWriter function for 4 channel format (ie BGRA)
https://github.com/opencv/opencv/blob/4.x/modules/videoio/src/cap_gstreamer.cpp

Looks like there is a support for getting 4 channel format into OpenCV via VideoCapture but when trying to use VideoWriter I get an error:
write frame skipped - expected CV_8UC3

this is my test_pipeline.py script:

import numpy as np
import time

# Settings
DEVICE = "/dev/video0"
WIDTH = 1920
HEIGHT = 1080
FPS = 60
OUTPUT_FILE = "output_hw_test.mp4"

# ---------------------------------------------------------
# 1. CAPTURE PIPELINE (GPU De-tiling)
# ---------------------------------------------------------
# Logic:
#   v4l2src    -> UYVY (Camera)
#   vapostproc -> BGRA (GPU VRAM) - Heavy Math happens here
#   vapostproc -> BGRA (System RAM) - Hardware Download/De-tiling
#   appsink    -> Python (4-channel Numpy Array)
# ---------------------------------------------------------
cap_pipeline = (
    f"v4l2src device={DEVICE} ! "
    f"video/x-raw,width={WIDTH},height={HEIGHT},format=UYVY,framerate={FPS}/1 ! "
    "vapostproc ! "
    "video/x-raw(memory:VAMemory),format=BGRA ! "
    "vapostproc ! "
    "video/x-raw,format=BGRA ! "
    "appsink drop=true max-buffers=1"
)

# ---------------------------------------------------------
# 2. WRITER PIPELINE (Hardware Encoding)
# ---------------------------------------------------------
# Logic:
#   appsrc     -> BGRA (From Python)
#   vapostproc -> NV12 (GPU VRAM) - Upload & Format Conversion
#   vah265enc  -> H.265 (Hardware Encode)
#   filesink   -> MP4 File
# ---------------------------------------------------------
writer_pipeline = (
    "appsrc ! "
    f"video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS}/1 ! "
    "videoconvert ! "               # <--- MANDATORY: Bridges BGR to Hardware
    "video/x-raw,format=NV12 ! "    # Convert to NV12 (Best for Encoders)
    "vapostproc ! "                 # Uploads NV12 to GPU
    "vah265enc ! "                  # Hardware Encoding
    "h265parse ! "
    "mp4mux ! "
    f"filesink location={OUTPUT_FILE}"
)

print(f"Opening Capture: {DEVICE} @ {WIDTH}x{HEIGHT} {FPS}fps...")
cap = cv2.VideoCapture(cap_pipeline, cv2.CAP_GSTREAMER)

if not cap.isOpened():
    print("Error: Could not open capture pipeline.")
    exit()

print(f"Opening Writer: {OUTPUT_FILE}...")
# 0 = FourCC (Let GStreamer handle it), FPS, Resolution
out = cv2.VideoWriter(writer_pipeline, cv2.CAP_GSTREAMER, 0, float(FPS), (WIDTH, HEIGHT))

if not out.isOpened():
    print("Error: Could not open writer pipeline.")
    cap.release()
    exit()

print("Recording... Press 'q' to stop (or Ctrl+C in terminal)")

frame_count = 0
start_time = time.time()

try:
    while True:
        ret, frame = cap.read()
        
        if not ret:
            print("Error: Failed to receive frame.")
            break
            
        # -------------------------------------------------
        # CRITICAL CHECK
        # -------------------------------------------------
        # frame.shape should be (1080, 1920, 4) because we asked for BGRA
        # out.write() expects this exact shape because writer_pipeline says format=BGRA
        # -------------------------------------------------
        
        # Write the 4-channel BGRA frame directly
        out.write(frame)

        frame_count += 1
        
        # Optional: Calculate FPS every 60 frames
        if frame_count % 60 == 0:
            elapsed = time.time() - start_time
            print(f"Encoding FPS: {frame_count / elapsed:.2f}", end="\r")

        # Optional: Show Preview
        # We need to slice to BGR just for imshow (imshow doesn't always like 4 channels)
        # This view is cheap (no copy)
        # cv2.imshow("Preview", frame[:, :, :3])
        # if cv2.waitKey(1) & 0xFF == ord('q'):
        #    break

except KeyboardInterrupt:
    print("\nStopping...")

finally:
    print(f"\nClosing resources. Recorded {frame_count} frames.")
    cap.release()
    out.release()
    cv2.destroyAllWindows()

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions