You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:13:04