Fix app crashes: optimize Kivy window backend and SDL drivers

- Commented out forced pygame backend (causes issues with display initialization)
- Added SDL_VIDEODRIVER and SDL_AUDIODRIVER fallback chains (wayland,x11,dummy)
- Limited KIVY_INPUTPROVIDERS to wayland,x11 (avoids problematic input providers)
- Reduced FFMPEG_THREADS from 4 to 2 (conserves Raspberry Pi resources)
- Reduced LIBPLAYER_BUFFER from 2MB to 1MB (saves memory)
- Fixed asyncio event loop deprecation warning (use try/except for get_running_loop)
- Better exception handling for cursor hiding

These changes fix the app crashing after 30 seconds due to graphics provider issues.
This commit is contained in:
Kiwy Player
2026-01-17 21:35:30 +02:00
parent d1382af517
commit 120c889143
5 changed files with 253 additions and 56 deletions

View File

@@ -15,13 +15,17 @@ from concurrent.futures import ThreadPoolExecutor
os.environ['KIVY_VIDEO'] = 'ffpyplayer' # Use ffpyplayer as video provider
os.environ['FFPYPLAYER_CODECS'] = 'h264,h265,vp9,vp8' # Support common codecs
os.environ['SDL_VIDEO_ALLOW_SCREENSAVER'] = '0' # Prevent screen saver
os.environ['SDL_VIDEODRIVER'] = 'wayland,x11,dummy' # Prefer Wayland, fallback to X11, then dummy
os.environ['SDL_AUDIODRIVER'] = 'alsa,pulse,dummy' # Prefer ALSA, fallback to pulse, then dummy
# Video playback optimizations
os.environ['KIVY_WINDOW'] = 'pygame' # Use pygame backend for better performance
# Note: pygame backend requires X11/Wayland context; let Kivy auto-detect for better compatibility
# os.environ['KIVY_WINDOW'] = 'pygame' # Use pygame backend for better performance
os.environ['KIVY_AUDIO'] = 'ffpyplayer' # Use ffpyplayer for audio
os.environ['KIVY_GL_BACKEND'] = 'gl' # Use OpenGL backend
os.environ['FFMPEG_THREADS'] = '4' # Use 4 threads for ffmpeg decoding
os.environ['LIBPLAYER_BUFFER'] = '2048000' # 2MB buffer for smooth playback
os.environ['KIVY_INPUTPROVIDERS'] = 'wayland,x11' # Only use Wayland and X11 input providers, skip problematic ones
os.environ['FFMPEG_THREADS'] = '2' # Use 2 threads for ffmpeg decoding (Raspberry Pi has limited resources)
os.environ['LIBPLAYER_BUFFER'] = '1048576' # 1MB buffer (reduced from 2MB to save memory)
os.environ['SDL_AUDIODRIVER'] = 'alsa' # Use ALSA for better audio on Pi
# Configure Kivy BEFORE importing any Kivy modules
@@ -35,7 +39,7 @@ Config.set('graphics', 'window_state', 'maximized') # Maximize window
# Video and audio performance settings
Config.set('graphics', 'multisampling', '0') # Disable multisampling for better performance
Config.set('graphics', 'fast_rgba', '1') # Enable fast RGBA for better performance
Config.set('audio', 'channels', '2') # Stereo audio
# Note: 'audio' section is not available in default Kivy config - it's handled by ffpyplayer
Config.set('kivy', 'log_level', 'warning') # Reduce logging overhead
from kivy.app import App
@@ -486,8 +490,12 @@ class CustomVKeyboard(VKeyboard):
Logger.info("CustomVKeyboard: Wrapped in container with close button")
# Set the custom keyboard factory
Window.set_vkeyboard_class(CustomVKeyboard)
# Set the custom keyboard factory (only if Window is properly initialized)
if Window is not None:
try:
Window.set_vkeyboard_class(CustomVKeyboard)
except Exception as e:
Logger.warning(f"CustomVKeyboard: Could not set custom keyboard: {e}")
class ExitPasswordPopup(Popup):
def __init__(self, player_instance, was_paused=False, **kwargs):
@@ -958,7 +966,7 @@ class SignagePlayer(Widget):
# Wayland-specific commands
# Method 1: Use wlopm (Wayland output power management)
os.system('wlopm --on \* 2>/dev/null || true')
os.system('wlopm --on \\* 2>/dev/null || true')
# Method 2: Use wlr-randr for wlroots compositors
os.system('wlr-randr --output HDMI-A-1 --on 2>/dev/null || true')
@@ -1837,32 +1845,39 @@ class SignagePlayerApp(App):
else:
Logger.info(f"SignagePlayerApp: Using auto resolution (no constraint)")
# Force fullscreen and borderless
Window.fullscreen = True
Window.borderless = True
Logger.info(f"SignagePlayerApp: Screen size: {Window.size}")
Logger.info(f"SignagePlayerApp: Available screen size: {Window.system_size if hasattr(Window, 'system_size') else 'N/A'}")
# Hide cursor after 3 seconds of inactivity
Clock.schedule_once(self.hide_cursor, 3)
# Force fullscreen and borderless (only if Window is available)
if Window is not None:
Window.fullscreen = True
Window.borderless = True
Logger.info(f"SignagePlayerApp: Screen size: {Window.size}")
Logger.info(f"SignagePlayerApp: Available screen size: {Window.system_size if hasattr(Window, 'system_size') else 'N/A'}")
# Hide cursor after 3 seconds of inactivity
Clock.schedule_once(self.hide_cursor, 3)
else:
Logger.critical("SignagePlayerApp: Window is None - display server not available")
return SignagePlayer()
def hide_cursor(self, dt):
"""Hide the mouse cursor"""
try:
Window.show_cursor = False
if Window is not None:
Window.show_cursor = False
except:
pass # Some platforms don't support cursor hiding
def on_start(self):
# Setup asyncio event loop for Kivy integration
try:
loop = asyncio.get_event_loop()
Logger.info("SignagePlayerApp: Asyncio event loop integrated with Kivy")
except RuntimeError:
# Create new event loop if none exists
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
Logger.info("SignagePlayerApp: New asyncio event loop created")
# Use get_running_loop in Python 3.7+ to avoid deprecation warning
try:
loop = asyncio.get_running_loop()
except RuntimeError:
# No running loop, create a new one
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
Logger.info("SignagePlayerApp: Asyncio event loop initialized")
except Exception as e:
Logger.warning(f"SignagePlayerApp: Could not setup asyncio loop: {e}")
# Schedule periodic async task processing
Clock.schedule_interval(self._process_async_tasks, 0.1) # Process every 100ms