Raspberry Pi 4B万圣节惊吓项目:替换Omxplayer为VLC求助
万圣节惊吓项目:替换Omxplayer/FBI为VLC播放器的解决方案
我是Python新手,正在开发一个万圣节互动项目,需求如下:
- 电视屏幕默认显示视频首帧静态画面
- PIR传感器检测到运动后,播放完整MP4惊吓视频
- 同时启动摄像头录制观众反应
- 惊吓视频播放完成后,回放录制的反应视频
- 回放结束后回到静态画面,等待下一次触发
原脚本使用已废弃的omxplayer和fbi工具,尝试替换为VLC播放器但未成功,需要技术支持。
原Scare脚本代码
#!/usr/bin/python import subprocess as sp import time import os import datetime from pirDetect import * import sys import random """ This script will play any of the videos listed in a random order. Usage: python ./scareRandom.py [VideoName] [Minutes] [VideoName] is any of video prefixes in the video_prefix list. [Minutes] is the time value in minutes of how often you want to rotate to a different video. Example usage would be : python ./scareRandom.py Male 5. After each trigger of the on_motion event the script will check and determine if the time elapsed is greater than the value you provided in argument 2 and if the elapsed time is longer than your time setting it will randomly pick a new video prefix and will recursively attempt to choose one that is NOT the current video prefix so it doesn't play the same video more than one time in sequence. To add more or different videos just add to or modify the video_prefix list below. If adding more videos or changing the defaults you will have to create a start image for each additional video. The naming structure for the start images and videos are as follows. [Prefix]ScareV.m4v (MaleScareV.m4v) or [Prefix]ScareV.mp4 (MaleScareV.mp4) [Prefix]Start.png (MaleStart.png) """ # initialize variables video_prefix = ["Male", "Female", "Child"] # This is the list of videos prefixes, you can add additional video # prefixes here. video = ["omxplayer", "filename", "-o", "both", "--win", "0 0 1280 720", "--aspect-mode", "fill", "--no-osd", "--orientation", "180", "--vol", "-600"] record = ["raspivid", "-o", "filename", "-n", "-t", "5000", "-rot", "180"] scare_file = "" current_prefix = "" new_prefix = "" image_name = "" start_time = time.time() def change_video(): global start_time global scare_file global current_prefix global new_prefix elapsed_time = time.time() - start_time print(str("\nTime since last rotation: {0}".format(datetime.timedelta(seconds=elapsed_time)))) if elapsed_time > (int(sys.argv[2]) * 60): while new_prefix == current_prefix: # make sure we don't choose the same video new_prefix = video_prefix[random.randrange(len(video_prefix))] current_prefix = new_prefix scare_file = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.m4v".format(current_prefix) start_time = time.time() show_image(current_prefix) print("\nUpdating Video to: {0}\n".format(current_prefix)) def getfilename(): return "/home/pi/Projects/Halloween/Recordings/" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S.h264") def sub_proc_wait(params): sub = sp.Popen(params) while sub.poll() is None: time.sleep(.1) def on_motion(curr_state): if curr_state: auto_file_name = getfilename() # Get a time stamped file name record[2] = auto_file_name sub_record = sp.Popen(record) # Start recording to capture their fright video[1] = scare_file sub_proc_wait(video) # Play the video to scare them video[1] = auto_file_name sub_proc_wait(video) # Play back the video we just recorded change_video() def show_image(_image_name): os.system("sudo fbi -T 1 -d /dev/fb0 -noverbose -once /home/pi/Projects/Halloween/ScareMedia/{0}Start.png".format( _image_name)) def start_up(): global scare_file global image_name image_name = arg1 scare_file = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.m4v".format(image_name) show_image(image_name) obj_detect = detector(7) obj_detect.subscribe(on_motion) obj_detect.start() os.system("sudo killall -9 fbi") if __name__ == "__main__": try: arg1 = sys.argv[1] if arg1 not in video_prefix: raise ValueError('first argument must be Male,Female or Child') if sys.argv[2].isdigit(): arg2 = int(sys.argv[2]) else: raise ValueError('Second argument must be a number') except IndexError: print("Usage: python ./scareRandom.py [VideoName] [Minutes]") sys.exit(1) except ValueError as x: print(x.message + "\nUsage: python ./scareRandom.py [VideoName] [Minutes]") sys.exit(1) start_up()
原PirDetect脚本代码
#!/usr/bin/python import RPi.GPIO as GPIO import time import os class detector(object): def __init__(self, sensor): self.callBacks = [] self.sensor = sensor self.currState = False self.prevState = False GPIO.setmode(GPIO.BOARD) GPIO.setup(self.sensor, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) def read(self): self.prevState = self.currState self.currState = GPIO.input(self.sensor) def printState(self): print( "GPIO pin {0} is {1}".format(self.sensor, "HIGH" if self.currState else "LOW")) def subscribe(self, callBack): self.callBacks.append(callBack) def callBack(self, state): for fn in self.callBacks: fn(state) def start(self): try: self.read() self.printState() while True: self.read() if self.currState != self.prevState: self.printState() self.callBack(self.currState) time.sleep(.1) except (KeyboardInterrupt, SystemExit): #Since fbi doesn't restore the console correctly when the application is exited we do a little clean up. os.system('stty sane')
解决方案:替换为VLC播放器
步骤1:安装依赖包
先在树莓派上安装VLC及Python绑定:
sudo apt update sudo apt install vlc python3-vlc
步骤2:修改Scare脚本
替换原脚本中omxplayer和fbi相关逻辑,改用VLC处理媒体播放:
#!/usr/bin/python3 import subprocess as sp import time import os import datetime from pirDetect import * import sys import random import vlc # 初始化VLC实例和播放器 instance = vlc.Instance('--no-osd', '--fullscreen', '--orientation 180', '--volume -600') player = instance.media_player_new() # 全局变量 video_prefix = ["Male", "Female", "Child"] record = ["raspivid", "-o", "filename", "-n", "-t", "5000", "-rot", "180"] scare_file = "" current_prefix = "" new_prefix = "" image_name = "" start_time = time.time() def change_video(): global start_time, scare_file, current_prefix, new_prefix elapsed_time = time.time() - start_time print(f"\nTime since last rotation: {datetime.timedelta(seconds=elapsed_time)}") if elapsed_time > (int(sys.argv[2]) * 60): while new_prefix == current_prefix: new_prefix = video_prefix[random.randrange(len(video_prefix))] current_prefix = new_prefix scare_file = f"/home/pi/Projects/Halloween/ScareMedia/{current_prefix}ScareV.m4v" start_time = time.time() show_image(current_prefix) print(f"\nUpdating Video to: {current_prefix}\n") def getfilename(): return f"/home/pi/Projects/Halloween/Recordings/{datetime.datetime.now().strftime('%Y-%m-%d_%H.%M.%S.h264')}" def play_media(file_path): # 停止当前播放并加载新媒体 if player.is_playing(): player.stop() media = instance.media_new(file_path) player.set_media(media) player.play() # 等待播放完成 while player.is_playing(): time.sleep(0.1) def on_motion(curr_state): if curr_state: auto_file_name = getfilename() record[2] = auto_file_name sub_record = sp.Popen(record) # 播放惊吓视频 play_media(scare_file) # 等待录制完成 sub_record.wait() # 回放录制内容 play_media(auto_file_name) # 回到静态画面 show_image(current_prefix) change_video() def show_image(_image_name): # 用VLC循环显示静态图片 if player.is_playing(): player.stop() image_path = f"/home/pi/Projects/Halloween/ScareMedia/{_image_name}Start.png" media = instance.media_new(image_path) media.add_option('input-repeat=-1') # 无限循环 player.set_media(media) player.play() def start_up(): global scare_file, image_name, current_prefix image_name = arg1 current_prefix = image_name scare_file = f"/home/pi/Projects/Halloween/ScareMedia/{image_name}ScareV.m4v" show_image(image_name) obj_detect = detector(7) obj_detect.subscribe(on_motion) obj_detect.start() player.stop() if __name__ == "__main__": try: arg1 = sys.argv[1] if arg1 not in video_prefix: raise ValueError('first argument must be Male,Female or Child') if sys.argv[2].isdigit(): arg2 = int(sys.argv[2]) else: raise ValueError('Second argument must be a number') except IndexError: print("Usage: python ./scareRandom.py [VideoName] [Minutes]") sys.exit(1) except ValueError as x: print(f"{x}\nUsage: python ./scareRandom.py [VideoName] [Minutes]") sys.exit(1) try: start_up() except KeyboardInterrupt: player.stop() GPIO.cleanup()
步骤3:优化PirDetect脚本的清理逻辑
添加GPIO资源清理,避免硬件占用:
#!/usr/bin/python3 import RPi.GPIO as GPIO import time import os class detector(object): def __init__(self, sensor): self.callBacks = [] self.sensor = sensor self.currState = False self.prevState = False GPIO.setmode(GPIO.BOARD) GPIO.setup(self.sensor, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) def read(self): self.prevState = self.currState self.currState = GPIO.input(self.sensor) def printState(self): print(f"GPIO pin {self.sensor} is {'HIGH' if self.currState else 'LOW'}") def subscribe(self, callBack): self.callBacks.append(callBack) def callBack(self, state): for fn in self.callBacks: fn(state) def start(self): try: self.read() self.printState() while True: self.read() if self.currState != self.prevState: self.printState() self.callBack(self.currState) time.sleep(.1) except (KeyboardInterrupt, SystemExit): GPIO.cleanup() os.system('stty sane')
关键改动说明
- 使用
python3-vlc库替代omxplayer,实现更稳定的媒体播放控制 - 用VLC的循环播放功能替代
fbi,保持屏幕显示静态首帧 - 优化播放等待逻辑,确保视频播放完成后再执行后续操作
- 添加程序退出时的资源清理(VLC进程、GPIO),避免硬件/软件资源泄漏
- 改用f-string格式化字符串,代码更简洁易读
内容的提问来源于stack exchange,提问作者Wessie
相关产品推荐
相关产品推荐

