Why is my VLC Media Player application leaking GPU memory on Raspberry Pi 4B+ and 5?

We have a Python-based media player application that loops through media files and displays them on a screen.

After running anywhere from ~3-24 hours, the display will sometimes freeze while playing a video. When this happens, dmesg shows the following messages (these particular messages are from when we were still using drm for the video output. We now use xcb_xv , but we have the same problem):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>...
...
[ 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
...
...
</code>
<code>... ... [ 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 ... ... </code>
...
...
[    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
...
...

This indicates that GPU memory has been leaked and there is no longer enough available to continue playing the videos. The device (Raspberry Pi) continues to function otherwise.

For video files, VLC is used for playback. At the end of each video, we attempt to clean up all resources VLC used to display the video. We have also tried keeping a video play counter, which we used to completely tear down and rebuild the VLC player instance after every 50 video plays. This did not resolve the issue.

Here is the code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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()
</code>
<code>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() </code>
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()

We’ve tried to clean up resources as aggressively as we know how/are able to. Unfortunately, we’re still experiencing the leak. We’ve also tried increasing GPU memory which did not resolve the issue.

New contributor

Zach Burke is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Why is my VLC Media Player application leaking GPU memory on Raspberry Pi 4B+ and 5?

We have a Python-based media player application that loops through media files and displays them on a screen.

After running anywhere from ~3-24 hours, the display will sometimes freeze while playing a video. When this happens, dmesg shows the following messages (these particular messages are from when we were still using drm for the video output. We now use xcb_xv , but we have the same problem):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>...
...
[ 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
...
...
</code>
<code>... ... [ 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 ... ... </code>
...
...
[    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
...
...

This indicates that GPU memory has been leaked and there is no longer enough available to continue playing the videos. The device (Raspberry Pi) continues to function otherwise.

For video files, VLC is used for playback. At the end of each video, we attempt to clean up all resources VLC used to display the video. We have also tried keeping a video play counter, which we used to completely tear down and rebuild the VLC player instance after every 50 video plays. This did not resolve the issue.

Here is the code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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()
</code>
<code>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() </code>
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()

We’ve tried to clean up resources as aggressively as we know how/are able to. Unfortunately, we’re still experiencing the leak. We’ve also tried increasing GPU memory which did not resolve the issue.

New contributor

Zach Burke is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật