树莓派4B+与5上VLC媒体播放器GPU内存泄漏导致播放冻结的问题求助
树莓派4B+与5上VLC媒体播放器GPU内存泄漏导致播放冻结的问题求助
大家好,我现在碰到一个棘手的问题,想请社区的大佬们帮忙分析下:
我们开发了一个基于Python的媒体播放器应用,功能是循环遍历媒体文件并在屏幕上展示。但在树莓派4B+和5上运行时,总会出现这样的问题:运行3到24小时不等后,播放视频时显示器会突然冻结,此时查看dmesg日志会出现一系列GPU内存分配失败的错误(之前我们用drm做视频输出,现在换成了xcb_xv,但问题完全没有改善)。
以下是当时dmesg输出的错误日志:
... ... [ 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 ... ...
从这些日志能看出来,应该是GPU内存发生了泄漏,到后面没有足够的可用内存来继续播放视频了,不过此时树莓派的其他功能还是正常的。
为了解决这个问题,我们已经做了这些尝试:
- 每个视频播放结束后,我们都会尝试清理VLC使用的所有资源,包括停止播放、释放媒体实例等
- 我们还加了一个视频播放计数器,每播放50个视频就完全销毁当前的VLC实例和播放器,然后重新初始化,但这个操作并没有解决内存泄漏的问题
下面是我们的核心代码,麻烦各位帮忙看看有没有哪里的资源释放做得不到位,或者有其他可以优化的地方:
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 for playing media scenes""" # 注:原代码中run方法未完全实现,此处保留现有结构
备注:内容来源于stack exchange,提问作者Zach Burke
相关产品推荐
相关产品推荐

