将Python脚本作为systemd服务运行时生成空视频文件的问题
问题:Systemd服务运行OpenCV录屏脚本生成空损坏文件,无预览窗口
我编写了一段Python脚本,可录制指定时长的视频并显示录制画面,预期单次录制结束后由/etc/systemd/system/videoPy.service服务自动重启。脚本手动运行时正常,能弹出预览窗口并生成正常的10秒.avi文件;但通过systemd启用并启动服务后,无预览窗口,且每10秒生成的.avi文件为空且损坏。已重装opencv-python等库仍未解决,怀疑问题与cv2.VideoWriter调用有关。
附service配置
[Unit] Description = starts video recorder After = multi-user.target [Service] Type = simple ExecStart = /usr/bin/python3 /home/fred/videoPy.py TimeoutStartSec = infinity Restart = on-failure RestartSec = 5 User = fred Group = users [Install] WantedBy = multi-user.target
附Python脚本
#!/usr/bin/env python3 import cv2 import numpy as np import time # Create a VideoCapture object. Setting to "0" opens onboard camera. Using "2" connects to usb camera. cap = cv2.VideoCapture(0) # Check if camera opened successfully if (cap.isOpened() == False): print("Unable to read camera feed") # defines video length capture_duration = 10 # Default resolutions of the frame are obtained. frame_width = int(cap.get(3)) frame_height = int(cap.get(4)) frame_per_sec = int('10') # PROBLEM MAY BE BELOW. Makes an empty .avi, and that is it. out = cv2.VideoWriter('/home/fred/videoPy_output.avi',cv2.VideoWriter_fourcc('M','J','P','G'), (frame_per_sec), (frame_width,frame_height)) # Captures video for specified amount of time start_time = time.time() while( int(time.time() - start_time) < capture_duration ): ret, frame = cap.read() if ret==True: #frame = cv2.flip(frame,0) out.write(frame) cv2.imshow('frame',frame) # Press Q on keyboard to stop recording if cv2.waitKey(1) & 0xFF == ord('q'): break else: break # When everything done, release the video capture and video write objects cap.release() out.release() # Closes all the frames cv2.destroyAllWindows()
解决方法
1. 修复图形环境与DISPLAY变量问题
systemd的multi-user.target默认不加载图形会话,OpenCV的cv2.imshow依赖图形环境,cv2.VideoWriter也可能因缺少图形上下文或DISPLAY变量未配置而异常:
- 在service配置的
[Service]段添加Environment="DISPLAY=:0"; - 执行
xhost +SI:localuser:fred(或在用户fred的~/.profile中添加该命令),赋予用户访问X服务器的权限; - 将service的
After和WantedBy从multi-user.target改为graphical.target,确保服务在图形环境启动后再运行。
2. 调整服务重启策略
原配置Restart=on-failure仅在脚本非0码退出时重启,但脚本录制完成后会正常退出(0码),导致systemd不会重启服务。将Restart=on-failure改为Restart=always,确保脚本无论正常还是异常结束都会自动重启。
3. 完善脚本的VideoWriter检查与逻辑
- 给
VideoWriter添加初始化成功检查,避免后续写入无效:
out = cv2.VideoWriter('/home/fred/videoPy_output.avi',cv2.VideoWriter_fourcc('M','J','P','G'), frame_per_sec, (frame_width,frame_height)) if not out.isOpened(): print("Failed to initialize VideoWriter") cap.release() exit(1)
- 修正脚本中的缩进错误(
while循环内的代码需缩进):
while( int(time.time() - start_time) < capture_duration ): ret, frame = cap.read() if ret==True: #frame = cv2.flip(frame,0) out.write(frame) cv2.imshow('frame',frame) # Press Q on keyboard to stop recording if cv2.waitKey(1) & 0xFF == ord('q'): break else: break
- 避免每次重启覆盖同一个文件,可按时间戳命名输出文件,比如:
output_filename = f'/home/fred/videoPy_output_{int(time.time())}.avi' out = cv2.VideoWriter(output_filename,cv2.VideoWriter_fourcc('M','J','P','G'), frame_per_sec, (frame_width,frame_height))
修改后的Service配置示例
[Unit] Description=starts video recorder After=graphical.target [Service] Type=simple ExecStart=/usr/bin/python3 /home/fred/videoPy.py TimeoutStartSec=infinity Restart=always RestartSec=5 User=fred Group=users Environment="DISPLAY=:0" [Install] WantedBy=graphical.target
内容的提问来源于stack exchange,提问作者portsample
相关产品推荐
相关产品推荐

