datetime:2025/03/26 14:41:00
author:nzb

systemd 启动 python opencv 播放视频

常见报错

Mar 26 10:35:31 pasture-10 systemd[1]: Started VLC media player..
Mar 26 10:35:31 pasture-10 python[21660]: qt.qpa.xcb: could not connect to display :0.0
Mar 26 10:35:31 pasture-10 python[21660]: qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "/home/blues/anaconda3/envs/speech_interaction/lib/python3.8/site-packages/cv2/qt/plugins" even tho>
Mar 26 10:35:31 pasture-10 python[21660]: This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Mar 26 10:35:31 pasture-10 python[21660]: Available platform plugins are: xcb.

初步尝试

要解决通过systemd服务启动时Qt平台插件加载失败的问题,请按照以下步骤操作:

  1. 确认Qt平台插件路径: 在conda环境中,Qt平台插件通常位于${CONDA_PREFIX}/plugins/platforms。检查路径是否存在:

    ls /home/blues/anaconda3/envs/speech_interaction/plugins/platforms/libqxcb.so
    
  2. 修改systemd服务文件: 添加必要的环境变量,特别是QT_QPA_PLATFORM_PLUGIN_PATHLD_LIBRARY_PATH

    [Unit]
    Description=Test Cv media player.
    After=graphical.target
    
    [Service]
    ExecStart=bash /home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/startup.sh
    # ExecStart=/home/blues/anaconda3/envs/speech_interaction/bin/python /home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/startup.py
    Restart=on-abort
    User=blues
    Group=blues
    Environment=DISPLAY=:0
    Environment=XAUTHORITY=/home/blues/.Xauthority
    #Environment=QT_DEBUG_PLUGINS=1
    Environment=QT_QPA_PLATFORM_PLUGIN_PATH=/home/blues/anaconda3/envs/speech_interaction/plugins/platforms
    Environment=LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/home/blues/anaconda3/envs/speech_interaction/lib
    
    [Install]
    # 图形界面模式下启动
    WantedBy=graphical.target
    
  3. 确保X服务器访问权限: 允许本地用户访问X服务器:

    xhost +local:
    
  4. 重新加载并启动服务

    sudo systemctl daemon-reload
    sudo systemctl restart test_cv.service
    
  5. 验证依赖库是否安装: 安装可能缺失的库:

    sudo apt install libxcb-xinerama0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-shape0
    

关键点解释

  • QT_QPA_PLATFORM_PLUGIN_PATH直接指向Qt的平台插件目录,确保正确加载xcb插件。
  • LD_LIBRARY_PATH添加conda环境的库路径,避免动态链接库找不到。
  • X权限设置确保服务进程有权访问显示服务器。

如果问题依旧,建议:

  1. 使用journalctl -u test_cv.service -f查看完整日志
  2. 在服务中增加Environment=QT_DEBUG_PLUGINS=1以获取详细插件加载日志

继续解决问题

按照上述流程下来,问题核心是Qt无法连接到X11显示服务器,尽管已配置环境变量。请按以下步骤排查:

  • 修复X11权限问题
# 确保用户有权限访问X服务器
sudo apt install x11-xserver-utils
xhost +SI:localuser:blues  # 精确授权指定用户
  • 验证Qt插件路径
# 检查真实存在的平台插件路径
ls /home/blues/anaconda3/envs/speech_interaction/lib/python3.8/site-packages/cv2/qt/plugins/platforms
# 应该能看到libqxcb.so

# 修改service文件环境变量
Environment=QT_QPA_PLATFORM_PLUGIN_PATH=/home/blues/anaconda3/envs/speech_interaction/lib/python3.8/site-packages/cv2/qt/plugins
  • 添加OpenGL库支持
sudo apt install libgl1-mesa-glx libglib2.0-0
  • 改进systemd服务配置
[Unit]
Description=Test Cv media player.
After=graphical.target systemd-user-sessions.service
Requires=graphical.target

[Service]
Type=simple
ExecStart=/home/blues/anaconda3/envs/speech_interaction/bin/python /path/to/startup.py
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/blues/.Xauthority
Environment=QT_DEBUG_PLUGINS=1
Environment=QT_QPA_PLATFORM=xcb
User=blues
Group=blues
Restart=on-failure

[Install]
WantedBy=graphical.target
  • 验证X11连接性
# 切换到服务用户身份测试
sudo -u blues env DISPLAY=:0 xeyes
  • 检查conda环境完整性
conda activate speech_interaction
ldd /home/blues/anaconda3/envs/speech_interaction/lib/python3.8/site-packages/cv2/qt/plugins/platforms/libqxcb.so | grep "not found"
  • 关键依赖安装
sudo apt install -y libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0
  • 查看完整日志定位问题
journalctl -u test_cv.service -n 100 --no-pager
  • 最终验证步骤:

    • 重新加载服务配置
sudo systemctl daemon-reload
sudo systemctl restart test_cv.service
  • 观察实时日志
journalctl -u test_cv.service -f

主要问题在于X11服务器权限和Qt插件路径配置。通过精确指定插件路径、增加X11访问权限、补充图形库依赖,应能解决无法连接显示服务器的问题。如果系统存在多用户登录情况,建议使用虚拟显示方案更稳定。

示例代码

  • test_cv.service
[Unit]
Description=VLC media player.
After=graphical.target systemd-user-sessions.service
Requires=graphical.target

[Service]
Type=simple
ExecStart=bash /home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/startup.sh
#ExecStart=/home/blues/anaconda3/envs/speech_interaction/bin/python /home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/startup.py
Environment=DISPLAY=:1
#Environment=XAUTHORITY=/home/blues/.Xauthority
# 获取详细插件加载日志
#Environment=QT_DEBUG_PLUGINS=1
#Environment=QT_QPA_PLATFORM=xcb
User=blues
Group=blues
#Restart=on-failure

[Install]
WantedBy=graphical.target
  • demo_cv.py

import time
import os

import cv2


def _emotion_play(filepath):
    # load the corresponding emoji video and play it on the screen
    while True:
        # self.logger.debug(f"*****displaying the emotion video******* {filepath}")
        if os.path.exists(filepath):
            window_name = "window"
            cap = cv2.VideoCapture(filepath)
            cv2.namedWindow(window_name, cv2.WND_PROP_FULLSCREEN)
            cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
            tik = time.time()
            while cap.isOpened():
                ret, frame = cap.read()
                if not ret:
                    break
                # Display the frame
                cv2.imshow(window_name, frame)
                # Press 'q' to exit the video playback
                if cv2.waitKey(30) & 0xFF == ord("q"):
                    break
            tok = time.time()
            print(f"{filepath.split(os.path.sep)[-1]} emotion video duration: {tok - tik}")
            # Release the video capture object and close all OpenCV windows
            # cap.release()
            # cv2.destroyAllWindows()
        else:
            print(f"The selected emotion file cannot be found: {filepath}")
        time.sleep(0.005)


# 示例使用
if __name__ == "__main__":
    filepath = "/home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/anger10.mp4"
    print(os.environ)
    _emotion_play(filepath)
  • demo_vlc.py
import vlc
import time
import threading


class VideoPlayer:
    def __init__(self):
        self.instance = vlc.Instance('--input-repeat=999999')  # --repeat 不起作用,针对的是播放列表
        self.player = self.instance.media_player_new()
        self.player.set_fullscreen(True)  # 全屏模式
        self.player.video_set_scale(0)    # 自适应画面比例
        self.current_media = None
        self.lock = threading.Lock()

    def play(self, media_path):
        """播放或切换视频源"""
        with self.lock:
            if self.player.is_playing():
                self.player.stop()  # 停止当前播放
            self.current_media = self.instance.media_new(media_path)
            self.player.set_media(self.current_media)
            self.player.play()

    def stop(self):
        """停止播放"""
        with self.lock:
            self.player.stop()


# 示例使用
if __name__ == "__main__":
    filepath = "/home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/anger10.mp4"
    # vlc
    player = VideoPlayer()
    print(1)
    player.play(filepath)  # 初始播放
    print(2)
    # # 模拟 5 秒后动态切换视频源
    # threading.Timer(5, lambda: player.play("shyness100.mp4")).start()
    print(3)
    # 保持主线程运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        player.stop()
  • startup.sh
#!/bin/bash

/home/blues/anaconda3/envs/speech_interaction/bin/python /home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/startup.py
  • startup.py
import subprocess
import time
import os
env = os.environ.copy()
print("env: ", env)

p = subprocess.Popen(['/home/blues/anaconda3/envs/speech_interaction/bin/python', '/home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/demo_cv.py'],   env=env)
# p = subprocess.Popen(['/home/blues/anaconda3/envs/speech_interaction/bin/python', '/home/blues/vscode_projects/cyan_demos/python_demo/vlc_demo/demo_vlc.py'],   env=env)

# 保持主线程运行
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    p.terminate()
    try:
        p.wait(timeout=5)
    except subprocess.TimeoutExpired:
        p.kill()
    print("done")

results matching ""

    No results matching ""