После примерно 3–24 часов работы дисплей иногда зависает. во время воспроизведения видео. Когда это происходит, dmesg отображает следующие сообщения (эти конкретные сообщения относятся к тому времени, когда мы еще использовали drm для вывода видео. Сейчас мы используем xcb_xv, но у нас та же проблема):
Код: Выделить всё
...
...
[ 3.028998] vc4-drm gpu: [drm] fb0: vc4drmfb frame buffer device
[ 9139.538040] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9139.545459] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9139.552522] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9139.583064] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9151.090537] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9151.096344] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9191.649563] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
[ 9191.675813] vc4-drm gpu: [drm] *ERROR* Failed to allocate DLIST entry. Requested size=9. ret=-28
...
...
Для видеофайлов для воспроизведения используется VLC. В конце каждого видео мы пытаемся очистить все ресурсы, которые VLC использовал для отображения видео. Мы также попытались сохранить счетчик воспроизведения видео, который использовался для полного отключения и восстановления экземпляра проигрывателя VLC после каждых 50 воспроизведений видео. Это не решило проблему.
Вот код:
Код: Выделить всё
import os
import time
import json
import vlc
import subprocess
from pathlib import Path
import pygame
from pygame import display, image, time as pygame_time
from jplayer.utils import scene_update_flag_utils as sufu
from jplayer.utils import logging_utils as lu
from jplayer.utils import system_utils as su
from jplayer.clients.japi_client import JApiClient
logger = lu.get_logger("jplayer_app")
VIDEO_REINIT_THRESHOLD = 50
class MediaPlayer:
def __init__(self, media_directory, scenes_directory):
logger.info("Initializing MediaPlayer ...")
self.media_directory = Path(media_directory)
self.scenes_directory = Path(scenes_directory)
self.default_image_path = Path('/home/static_images/ready_for_content.jpeg')
self.supported_images = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.heic'}
self.supported_videos = {'.mp4', '.avi', '.mov'}
self.current_scenes = []
self.video_count = 0
# Get orientation
self.jclient = JApiClient(logger)
orientation = self.jclient.jplayer_info.get("orientation", "LANDSCAPE")
logger.info(f" Detected Orientation: {orientation}")
self.screen_config = su.get_screen_config(orientation)
# Initialize Pygame for image display
pygame.init()
display.init()
# Set up fullscreen display with proper orientation
display_dims = self.screen_config.get_display_dimensions()
self.screen = display.set_mode(display_dims, pygame.FULLSCREEN)
self.screen_width, self.screen_height = display_dims
# Initialize VLC
self.initialize_vlc()
logger.info("Done initializing MediaPlayer.")
def initialize_vlc(self):
"""Initialize the VLC instance and player."""
vlc_args = [
'--vout=xcb_xv',
'--no-video-title-show',
'--file-caching=2000'
]
self.vlc_instance = vlc.Instance(vlc_args)
self.vlc_player = self.vlc_instance.media_player_new()
# Tell VLC to render to the pygame window
if os.name == 'nt': # Windows
self.vlc_player.set_hwnd(pygame.display.get_wm_info()['window'])
else: # Linux/Raspberry Pi
self.vlc_player.set_xwindow(pygame.display.get_wm_info()['window'])
def reinitialize_vlc(self):
"""Reinitialize VLC after releasing resources to free up GPU memory."""
logger.info("Reinitializing VLC to reclaim GPU resources...")
self.vlc_player.release()
self.vlc_instance.release()
self.initialize_vlc()
self.video_count = 0 # Reset the counter after reinitialization
logger.info("VLC reinitialized successfully.")
def load_scenes(self):
"""Load and parse scene configuration files."""
scenes = []
for file in self.scenes_directory.glob('*.json'):
try:
with open(file, 'r') as f:
scene = json.load(f)
required_fields = {'id', 'media_file', 'order'}
if not all(field in scene for field in required_fields):
logger.info(f"Scene file {file} missing required fields")
continue
media_path = self.media_directory / scene['media_file']
if not media_path.exists():
logger.info(f"Media file not found: {media_path}")
continue
scene['media_path'] = media_path
scenes.append(scene)
except Exception as e:
logger.error(f"Error loading scene file {file}: {e}")
continue
return sorted(scenes, key=lambda x: x['order'])
def display_image(self, scene):
"""Display an image for the specified duration."""
try:
img = image.load(str(scene['media_path']))
img = pygame.transform.scale(img, (self.screen_width, self.screen_height))
if self.screen_config.get_pygame_rotation() != 0:
img = pygame.transform.rotate(img, self.screen_config.get_pygame_rotation())
self.screen.blit(img, (0, 0))
display.flip()
duration = int(scene.get('time_to_display', 7))
pygame_time.wait(duration * 1000)
return True
except Exception as e:
logger.info(f"Error displaying image {scene['media_path']}: {e}")
return False
def display_video(self, scene):
"""Play a video file using VLC."""
media = None
try:
# Stop any existing playback and force cleanup
if self.vlc_player.is_playing():
self.vlc_player.stop()
# Create new media instance
media = self.vlc_instance.media_new(str(scene['media_path']))
media.add_option(":no-audio") # Disable audio processing if not needed
media.add_option(":no-metadata-network-access") # Prevent network access
media.add_option(":no-video-title-show") # Disable title display
# Set media and immediately check if it was successful
self.vlc_player.set_media(media)
if not self.vlc_player.get_media():
logger.info(" Failed to set media")
return False
# Attempt playback
play_result = self.vlc_player.play()
if play_result == -1:
logger.info(f" Failed to start playback for scene {scene.get('id')}")
return False
time.sleep(1)
while self.vlc_player.is_playing():
logger.info(" video is playing")
pygame.event.pump()
time.sleep(0.25)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.vlc_player.stop()
if media:
media.release()
return False
# Explicitly stop playback
self.vlc_player.stop()
# Small delay to ensure cleanup
time.sleep(0.1)
# If we got here, the video played successfully
self.video_count += 1
if self.video_count >= VIDEO_REINIT_THRESHOLD:
self.reinitialize_vlc()
return True
except Exception as e:
logger.error(f"Error playing video {scene.get('id')}: {e}")
return False
finally:
# Aggressive cleanup
if self.vlc_player.is_playing():
self.vlc_player.stop()
# Force VLC to release GPU resources
self.vlc_player.set_media(None)
if media:
media.release()
media = None
def display_default_scene(self):
"""Display the default 'ready for content' scene."""
try:
if not self.default_image_path.exists():
logger.error(f"Default image not found at {self.default_image_path}")
return False
img = image.load(str(self.default_image_path))
img = pygame.transform.scale(img, (self.screen_width, self.screen_height))
if self.screen_config.get_pygame_rotation() != 0:
img = pygame.transform.rotate(img, self.screen_config.get_pygame_rotation())
self.screen.blit(img, (0, 0))
display.flip()
pygame_time.wait(7000)
return True
except Exception as e:
logger.error(f"Error displaying default image: {e}")
return False
def run(self):
"""Main loop to display media files."""
try:
while True:
if sufu.should_reload_scenes():
self.current_scenes = self.load_scenes()
logger.info("Reloaded scenes due to update flag")
sufu.reset_update_flag_to_zero()
elif not self.current_scenes:
self.current_scenes = self.load_scenes()
logger.info("Initial scene load")
if not self.current_scenes:
logger.info("No valid scenes found, displaying default scene")
self.display_default_scene()
time.sleep(15)
continue
logger.info("Iterating through scenes ...")
for scene in self.current_scenes:
logger.info(f" Displaying scene {scene.get('id')} at media path {scene.get('media_path')}")
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
suffix = scene['media_path'].suffix.lower()
if suffix in self.supported_images:
if not self.display_image(scene):
continue
elif suffix in self.supported_videos:
if not self.display_video(scene):
continue
finally:
self.vlc_player.release()
pygame.quit()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Display images and videos in a loop based on scene configurations')
parser.add_argument('media_directory', help='Directory containing media files (images and videos)')
parser.add_argument('scenes_directory', help='Directory containing scene configuration JSON files')
args = parser.parse_args()
player = MediaPlayer(args.media_directory, args.scenes_directory)
player.run()
Подробнее здесь: https://stackoverflow.com/questions/792 ... y-pi-4b-an