Fix player freezing and rapid cycling issues
- Switch from VLC to PIL for image display to avoid VLC display errors - Add transition protection to prevent rapid media cycling - Remove conflicting next_media_loop calls - Improve error handling and logging for better debugging - Fix VLC configuration for better Raspberry Pi compatibility
This commit is contained in:
@@ -16,6 +16,7 @@ class SimpleTkPlayer:
|
||||
self.current_index = 0
|
||||
self.paused = False
|
||||
self.pause_timer = None
|
||||
self.is_transitioning = False # Flag to prevent rapid cycling
|
||||
self.label = tk.Label(root, bg='black')
|
||||
self.label.pack(fill=tk.BOTH, expand=True)
|
||||
self.create_controls()
|
||||
@@ -162,7 +163,6 @@ class SimpleTkPlayer:
|
||||
|
||||
def after_intro(self):
|
||||
self.show_current_media()
|
||||
self.root.after(100, self.next_media_loop)
|
||||
|
||||
def show_video(self, file_path, on_end=None, duration=None):
|
||||
try:
|
||||
@@ -176,10 +176,20 @@ class SimpleTkPlayer:
|
||||
self.video_canvas.pack(fill=tk.BOTH, expand=True)
|
||||
self.root.attributes('-fullscreen', True)
|
||||
self.root.update_idletasks()
|
||||
self.vlc_instance = vlc.Instance('--vout=x11')
|
||||
# Configure VLC for Raspberry Pi with better compatibility options
|
||||
vlc_args = [
|
||||
'--no-xlib',
|
||||
'--vout=gl',
|
||||
'--aout=alsa',
|
||||
'--intf=dummy',
|
||||
'--quiet',
|
||||
'--no-video-title-show',
|
||||
'--no-stats',
|
||||
'--disable-screensaver'
|
||||
]
|
||||
self.vlc_instance = vlc.Instance(vlc_args)
|
||||
self.vlc_player = self.vlc_instance.media_player_new()
|
||||
self.vlc_player.set_mrl(file_path)
|
||||
self.vlc_player.set_fullscreen(True)
|
||||
self.vlc_player.set_xwindow(self.video_canvas.winfo_id())
|
||||
self.vlc_player.play()
|
||||
# Watchdog timer: fallback if video doesn't end
|
||||
@@ -241,12 +251,73 @@ class SimpleTkPlayer:
|
||||
if ext.endswith(('.mp4', '.avi', '.mov', '.mkv')):
|
||||
self.show_video(file_path, on_end=self.next_media, duration=duration)
|
||||
elif ext.endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
|
||||
self.show_image_via_vlc(file_path, duration if duration is not None else 10, on_end=self.next_media)
|
||||
# Use PIL for images instead of VLC to avoid display issues
|
||||
self.show_image_via_pil(file_path, duration if duration is not None else 10, on_end=self.next_media)
|
||||
try:
|
||||
self.show_image_via_vlc(file_path, duration if duration is not None else 10, on_end=self.next_media)
|
||||
except Exception as e:
|
||||
print(f"[PLAYER] VLC image display failed: {e}. Trying PIL fallback.")
|
||||
self.show_image_via_pil(file_path, duration if duration is not None else 10, on_end=self.next_media)
|
||||
else:
|
||||
print(f"[PLAYER] Unsupported file type: {media['file_name']}")
|
||||
self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow')
|
||||
self.root.after(2000, self.next_media)
|
||||
|
||||
def show_image_via_pil(self, file_path, duration, on_end=None):
|
||||
"""Fallback image display using PIL and Tkinter"""
|
||||
try:
|
||||
print(f"[PLAYER] Displaying image via PIL: {file_path}")
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
# Stop any VLC player that might be running
|
||||
if hasattr(self, 'vlc_player') and self.vlc_player:
|
||||
self.vlc_player.stop()
|
||||
|
||||
# Hide video canvas if exists and show label
|
||||
if hasattr(self, 'video_canvas'):
|
||||
self.video_canvas.pack_forget()
|
||||
self.label.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Load and resize image
|
||||
img = Image.open(file_path)
|
||||
screen_width = self.root.winfo_screenwidth()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
|
||||
# Calculate scaling to fit screen while maintaining aspect ratio
|
||||
img_ratio = img.width / img.height
|
||||
screen_ratio = screen_width / screen_height
|
||||
|
||||
if img_ratio > screen_ratio:
|
||||
# Image is wider than screen ratio
|
||||
new_width = screen_width
|
||||
new_height = int(screen_width / img_ratio)
|
||||
else:
|
||||
# Image is taller than screen ratio
|
||||
new_height = screen_height
|
||||
new_width = int(screen_height * img_ratio)
|
||||
|
||||
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
self.photo = ImageTk.PhotoImage(img)
|
||||
|
||||
self.label.config(image=self.photo, text="", bg='black')
|
||||
print(f"[PLAYER] Image displayed successfully, will show for {duration} seconds")
|
||||
|
||||
# Schedule next media after duration
|
||||
def finish_image():
|
||||
print(f"[PLAYER] Image duration completed for {os.path.basename(file_path)}")
|
||||
self.label.config(image="", text="")
|
||||
if hasattr(self, 'photo'):
|
||||
del self.photo # Free memory
|
||||
if on_end:
|
||||
on_end()
|
||||
|
||||
self.image_timer = self.root.after(int(duration * 1000), finish_image)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[PLAYER] PIL image display failed: {e}. Skipping.")
|
||||
self.label.config(text=f"Image Error: {os.path.basename(file_path)}", fg='red', font=('Arial', 24))
|
||||
self.root.after(2000, lambda: on_end() if on_end else None)
|
||||
|
||||
def reload_playlist_and_continue(self):
|
||||
print("[PLAYER] Attempting to reload playlist...")
|
||||
new_playlist = load_latest_playlist()
|
||||
@@ -259,9 +330,36 @@ class SimpleTkPlayer:
|
||||
print("[PLAYER] Still no playlist. Will retry.")
|
||||
self.root.after(10000, self.reload_playlist_and_continue)
|
||||
|
||||
def validate_image_file(self, file_path):
|
||||
"""Validate if image file is not corrupted"""
|
||||
try:
|
||||
import imghdr
|
||||
# Check if file has valid image header
|
||||
img_type = imghdr.what(file_path)
|
||||
if img_type is None:
|
||||
return False
|
||||
|
||||
# Additional check for file size (empty files)
|
||||
file_size = os.path.getsize(file_path)
|
||||
if file_size < 100: # Too small to be a valid image
|
||||
return False
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[PLAYER] Error validating image {file_path}: {e}")
|
||||
return False
|
||||
|
||||
def show_image_via_vlc(self, file_path, duration, on_end=None):
|
||||
try:
|
||||
print(f"[PLAYER] Attempting to show image: {file_path}")
|
||||
|
||||
# Validate file before attempting to display
|
||||
if not self.validate_image_file(file_path):
|
||||
print(f"[PLAYER] Invalid or corrupted image file: {file_path}. Skipping.")
|
||||
if on_end:
|
||||
on_end()
|
||||
return
|
||||
|
||||
if hasattr(self, 'vlc_player') and self.vlc_player:
|
||||
self.vlc_player.stop()
|
||||
if not hasattr(self, 'video_canvas'):
|
||||
@@ -271,10 +369,20 @@ class SimpleTkPlayer:
|
||||
self.video_canvas.pack(fill=tk.BOTH, expand=True)
|
||||
self.root.attributes('-fullscreen', True)
|
||||
self.root.update_idletasks()
|
||||
self.vlc_instance = vlc.Instance('--vout=x11')
|
||||
# Configure VLC for Raspberry Pi with better compatibility options
|
||||
vlc_args = [
|
||||
'--no-xlib',
|
||||
'--vout=gl',
|
||||
'--aout=dummy',
|
||||
'--intf=dummy',
|
||||
'--quiet',
|
||||
'--no-video-title-show',
|
||||
'--no-stats',
|
||||
'--disable-screensaver'
|
||||
]
|
||||
self.vlc_instance = vlc.Instance(vlc_args)
|
||||
self.vlc_player = self.vlc_instance.media_player_new()
|
||||
self.vlc_player.set_mrl(file_path)
|
||||
self.vlc_player.set_fullscreen(True)
|
||||
self.vlc_player.set_xwindow(self.video_canvas.winfo_id())
|
||||
self.vlc_player.play()
|
||||
# Watchdog timer: fallback if image doesn't advance
|
||||
@@ -301,14 +409,29 @@ class SimpleTkPlayer:
|
||||
on_end()
|
||||
|
||||
def next_media(self):
|
||||
if self.is_transitioning:
|
||||
print("[PLAYER] Already transitioning, ignoring next_media call")
|
||||
return
|
||||
|
||||
self.is_transitioning = True
|
||||
self.current_index = (self.current_index + 1) % len(self.playlist)
|
||||
print(f"[PLAYER] Moving to next media: index {self.current_index}")
|
||||
|
||||
# Clear any existing timers
|
||||
if hasattr(self, 'video_watchdog'):
|
||||
self.root.after_cancel(self.video_watchdog)
|
||||
if hasattr(self, 'image_watchdog'):
|
||||
self.root.after_cancel(self.image_watchdog)
|
||||
if hasattr(self, 'image_timer'):
|
||||
self.root.after_cancel(self.image_timer)
|
||||
|
||||
self.show_current_media()
|
||||
# Reset transition flag after a brief delay
|
||||
self.root.after(500, lambda: setattr(self, 'is_transitioning', False))
|
||||
|
||||
def next_media_loop(self):
|
||||
if not self.playlist or self.paused:
|
||||
self.root.after(1000, self.next_media_loop)
|
||||
return
|
||||
self.show_current_media()
|
||||
# This function is no longer needed as media transitions are handled by the callback system
|
||||
pass
|
||||
|
||||
def exit_app(self):
|
||||
# Signal all threads and flags to stop
|
||||
|
||||
Reference in New Issue
Block a user