feat: add detailed logging for NV12 frames, GStreamer warnings, and render errors

This commit is contained in:
Matteo Benedetto
2026-03-24 00:26:17 +01:00
parent 0c8e2c2a11
commit bb0ac90c96
@@ -7,6 +7,7 @@ import logging
import os
import threading
import time
import traceback
from dataclasses import dataclass
from typing import Any, Callable
@@ -192,6 +193,7 @@ class GStreamerBackend(PlayerBackend):
self._texture_format: str = "BGRA"
self._resolution = ""
self._hw_decoders: list | None = None # None = not yet probed
self._frame_count = 0 # total frames decoded
def attach_window(self, window: object) -> None:
self._window = window
@@ -246,19 +248,38 @@ class GStreamerBackend(PlayerBackend):
# SDL_UpdateNVTexture avoids a full BGRA conversion on CPU.
# ctypes.create_string_buffer returns c_char_Array_N; cast to
# LP_c_ubyte which SDL_UpdateNVTexture requires.
_ubyte_p = ctypes.POINTER(ctypes.c_ubyte)
y_buf = ctypes.cast(ctypes.create_string_buffer(frame.pixels), _ubyte_p)
uv_buf = ctypes.cast(ctypes.create_string_buffer(frame.uv_pixels), _ubyte_p)
result = sdl2.SDL_UpdateNVTexture(
self._texture, None,
y_buf, frame.pitch,
uv_buf, frame.uv_pitch,
)
try:
_ubyte_p = ctypes.POINTER(ctypes.c_ubyte)
y_data = bytes(frame.pixels)
uv_data = bytes(frame.uv_pixels)
y_buf = ctypes.cast(ctypes.create_string_buffer(y_data), _ubyte_p)
uv_buf = ctypes.cast(ctypes.create_string_buffer(uv_data), _ubyte_p)
log.debug(
"SDL_UpdateNVTexture: %dx%d y_len=%d uv_len=%d pitch=%d uv_pitch=%d",
frame.width, frame.height,
len(y_data), len(uv_data),
frame.pitch, frame.uv_pitch,
)
result = sdl2.SDL_UpdateNVTexture(
self._texture, None,
y_buf, frame.pitch,
uv_buf, frame.uv_pitch,
)
except Exception:
log.error(
"NV12 texture upload exception (frame %d): %s",
self._frame_count,
traceback.format_exc(),
)
return False
else:
pixel_buffer = ctypes.create_string_buffer(frame.pixels)
result = sdl2.SDL_UpdateTexture(self._texture, None, pixel_buffer, frame.pitch)
if result != 0:
log.error("Could not upload SDL video texture: %s", sdl2.SDL_GetError())
log.error(
"Could not upload SDL video texture (frame %d fmt=%s): %s",
self._frame_count, frame.pixel_format, sdl2.SDL_GetError(),
)
return False
self._frame_dirty = False
@@ -446,6 +467,13 @@ class GStreamerBackend(PlayerBackend):
uv_pixels = raw[y_size:y_size + uv_size]
pitch = int(info.stride[0])
uv_pitch = int(info.stride[1])
if self._frame_count == 0:
buf_total = buffer.get_size()
log.info(
"First NV12 frame: %dx%d y_pitch=%d uv_pitch=%d "
"y_size=%d uv_size=%d buf_total=%d",
width, height, pitch, uv_pitch, y_size, uv_size, buf_total,
)
frame = _Frame(
width=width, height=height,
pitch=pitch, pixels=pixels,
@@ -455,13 +483,24 @@ class GStreamerBackend(PlayerBackend):
else:
pitch = int(info.stride[0]) if info.stride else width * 4
pixels = buffer.extract_dup(0, buffer.get_size())
if self._frame_count == 0:
log.info(
"First %s frame: %dx%d pitch=%d buf_total=%d",
fmt_str, width, height, pitch, buffer.get_size(),
)
frame = _Frame(width=width, height=height, pitch=pitch, pixels=pixels)
with self._frame_lock:
self._frame_count += 1
if self._frame_count <= 3 or self._frame_count % 300 == 0:
log.debug("Frame #%d fmt=%s %dx%d", self._frame_count, fmt_str, width, height)
prev_res = self._resolution
self._latest_frame = frame
self._frame_dirty = True
if resolution != self._resolution:
if resolution != prev_res:
self._resolution = resolution
if prev_res:
log.info("Resolution changed: %s -> %s", prev_res, resolution)
self._event_callback("resolution", resolution)
return self._flow_ok()
@@ -495,6 +534,7 @@ class GStreamerBackend(PlayerBackend):
message = bus.timed_pop_filtered(
100 * self._gst.MSECOND,
self._gst.MessageType.ERROR
| self._gst.MessageType.WARNING
| self._gst.MessageType.EOS
| self._gst.MessageType.BUFFERING
| self._gst.MessageType.STATE_CHANGED,
@@ -517,6 +557,14 @@ class GStreamerBackend(PlayerBackend):
self._set_playing(False, notify=True)
return
if msg_type == self._gst.MessageType.WARNING:
err, debug = message.parse_warning()
text = err.message if hasattr(err, "message") else str(err)
if debug:
text = f"{text} ({debug})"
log.warning("GStreamer playback warning: %s", text)
return
if msg_type == self._gst.MessageType.ERROR:
err, debug = message.parse_error()
text = err.message if hasattr(err, "message") else str(err)
@@ -592,6 +640,7 @@ class GStreamerBackend(PlayerBackend):
self._latest_frame = None
self._frame_dirty = False
self._resolution = ""
self._frame_count = 0
self._destroy_texture()
def _set_playing(self, value: bool, notify: bool) -> None: