feat: add detailed logging for NV12 frames, GStreamer warnings, and render errors
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user