Python 当 运行 脚本在 RPi 3B+ 上进行多处理时出现 vlc 错误

Python vlc errors when running script with multiprocessing on RPi 3B+

我在 Raspberry Pi 3B+ 上有一个 python 脚本 (sound_play.py),它使用 vlc 播放 mp3 文件和网络广播流。 它可以单独 运行(用于测试目的),也可以作为进程中的模块由另一个脚本 (clock.py) 使用多处理启动。

当我单独 运行 sound_play.py 时,一切正常,但如果脚本作为子进程启动,当 file/the webradio 时,我会在下面收到错误消息应该玩。 我在 ALSA 和 PulseAudio 上都试过了,但行为是一样的,它自己可以工作,多处理就不行。

一些背景信息:我刚刚重新安装了 Raspbian(以解决一个不相关的问题),所以现在我有了 Bullseye。在那之前我有一些版本的 Buster,脚本运行良好,无论是在它自己还是在子进程中。我只在重新安装 Raspbian.

后才收到错误

这可能是什么原因造成的?我该如何解决?提前谢谢你。

使用 ALSA 时出错:

17:29:42 playing https://orf-live.ors-shoutcast.at/oe1-q2a
[6d691318] prefetch stream error: unimplemented query (264) in control
[025e28a8] alsa audio output error: cannot open ALSA device "default": Input/output error
[025e28a8] main audio output error: Audio output failed
[025e28a8] main audio output error: The audio device "default" could not be used:
Input/output error.
[025e28a8] main audio output error: module not functional
[71f45f68] main decoder error: failed to create audio output

使用 PulseAudio 时出错:

17:46:26 playing https://orf-live.ors-shoutcast.at/oe1-q2a
[4cd97a68] prefetch stream error: unimplemented query (264) in control
[02019030] vlcpulse audio output error: stream creation failure: Client forked
[02019030] main audio output error: module not functional
[71f4c390] main decoder error: failed to create audio output

sound_play.py

#!/usr/bin/env python3
"""
sound_play.py
play sounds at certain times
"""

#IMPORTS
import time
from datetime import datetime, timedelta
import vlc

#CONSTANTS
DEBUG = True
VOLUME = 80 #in percent
DUR_SUNRISE = 5 #min
DUR_AFTERGLOW = 5 #min
MUSIC_PATH = "./alarms/"
HOURBELL = "./hourbell.mp3"
RADIO_NAME = "Radio Österreich Eins"
RADIO_URL = "https://orf-live.ors-shoutcast.at/oe1-q2a"

#GLOBALS
config = None
temp = None
instance = vlc.Instance('--aout=alsa')
player = instance.media_player_new()

def start(source):
    media = instance.media_new(source) 
    player.set_media(media)
    player.play()
    if DEBUG: print(time.strftime("%T"), "playing " + str(source))

def stop():
    player.stop()
    if DEBUG: print(time.strftime("%T"), "stopped playing")

def main(*args):
    global DEBUG, config, temp
    if args:
        (debug, config, temp) = args
        if debug == True:
            DEBUG = True

    global instance

    radio_active = False
    radio_start = 0.0
    music_active = False
    music_start = 0.0
    hourbell_active = False
    hourbell_start = 0.0
    hourbell_last = ""
        
    vlc.libvlc_audio_set_volume(player, VOLUME)
    if not DEBUG: instance.log_unset()
    
    while True:
        if not config == None:
            #radio
            if temp["button_new"] and temp["read_button"] == "*":
                temp["button_new"] = False
                if (music_active and not radio_active) or radio_active:
                    radio_active = False
                    music_active = False
                    stop()
                    
                else:
                    start(RADIO_URL)
                    radio_active = True
                    music_active = False
                    radio_start = time.time()

            if radio_active and time.time() >= radio_start + (60 * 60):
                radio_active = False
                stop()

            #alarm
            if config["alarm_music"] and time.strftime("%H%M") == config["alarm_time"] and time.time() >= music_start + 60:           
                if config["alarm_song"] != RADIO_NAME and not music_active:
                    if not radio_active: start(MUSIC_PATH + config["alarm_song"] + ".mp3")
                    music_active = True

                elif config["alarm_song"] == RADIO_NAME:
                    if not radio_active: start(RADIO_URL)
                    radio_active = True
                    music_active = True
                    radio_start = time.time()
                
                music_start = time.time()
            
            elif music_active and not radio_active and time.time() >= music_start + 1.5:
                if time.time() >= music_start + player.get_length() / 1000 + 1.5:
                    music_active = False
                    stop()

            elif music_active and radio_active and time.time() >= music_start + (5 * 60):
                music_active = False

            #hourbell
            if time.strftime("%M") == "00" and not time.strftime("%H") == hourbell_last:
                hourbell_last = time.strftime("%H")

                if config["alarm_hourbell"] and not hourbell_active and not music_active and not radio_active \
                    and (time.time() - temp["afterglow_start"] < DUR_AFTERGLOW * 60 or temp["light_on"]):
                    start(HOURBELL)
                    hourbell_active = True
                    hourbell_start = time.time()
                    
            elif hourbell_active and time.time() >= hourbell_start + 1.5:
                if time.time() >= hourbell_start + player.get_length() / 1000 + 1.5:
                    hourbell_active = False
                    stop()

            #sunrise
            time_after_duration = time.strftime("%H%M", time.localtime((datetime.now() + timedelta(minutes=DUR_SUNRISE)).timestamp()))
            if time_after_duration == config["alarm_time"] and config["alarm_sunrise"] and not temp["sunrise_active"]: 
                temp["sunrise_active"] = True

            elif time.strftime("%H%M") >= config["alarm_time"] and temp["sunrise_active"] and not music_active:
                temp["sunrise_active"] = False

            time.sleep(0.01)
        
        else:
            source = input("source: ")
            if source == "r": start(RADIO_URL)
            else: start(MUSIC_PATH + source + ".mp3")
            input("stop")
            stop()

if __name__ == "__main__":
    main()

clock.py

#!/usr/bin/env python3
"""
clock.py
main script, manage module threads
"""

#IMPORTS
import os
from multiprocessing import Process, Manager
import json
import time
import RPi.GPIO as GPIO

#import time_sync
import weather_fetch
import arduino_talk
import room_measure
import hands_move
import sound_play
import gui_show

#CONSTANTS
debug = False #debug for all modules
DEBUG = False #debug for this script
NICENESS = -5
CONFIG_FILE = "./config.json"
PIN_STEPPER = {
    "hour": {
        "A": 4,
        "B": 17,
        "C": 27,
        "D": 22,
    },
    "minute": {
        "A": 5,
        "B": 6,
        "C": 13,
        "D": 19,
    }
}

def main():
    def store_clock(config):
        while True:
            with open(CONFIG_FILE, "w") as config_file:
                json.dump(config.copy(), config_file)

            if debug: print(time.strftime("%T"), "Configuration stored")
            time.sleep(20)

    global DEBUG
    
    if debug:
        DEBUG = True

    config_reset = {
        #hands_move
        "calibration": {'first': {'hour': 1926, 'minute': 1920}, 'second': {'hour': 1915, 'minute': 1920}},
        
        #gui_show
        "alarm_time": "0000",
        "alarm_sunrise": False,
        "alarm_music": False,
        "alarm_hourbell": False,
        "alarm_song": "",
        "weather_location": "Lessach",  
    }
    temp_reset = {
        #weather_fetch
        "weather_current": "",
        "weather_forecast": "",

        #arduino_talk
        "light_on": True,
        "read_button": "",
        "button_new": False,

        #room_measure
        "room_climate": "",

        #gui_show
        "sunrise_active": False,
        "afterglow_start": 0.0,
    }
    
    os.system("sudo renice -n " + str(NICENESS) + " -p " + str(os.getpid()) + ">/dev/null")
    
    print("MULTIFUNKTIONS-DESIGNUHR")
    print("Michael Matthias Jesner")
    print("WSH Gesellenstück 2020")
    print("Zum Beenden Enter drücken\n")

    if not os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, "w") as config_file:
            json.dump(config_reset, config_file)
        
        if debug: print(time.strftime("%T"), "Reset config.json")

    with open(CONFIG_FILE, "r") as config_file:
        config = Manager().dict(json.load(config_file))
        
    temp = Manager().dict(temp_reset)

    store_process = Process(target = store_clock, args = (config,))
    process = [
        #Process(target = time_sync.main, args = (debug, config, temp)),
        Process(target = weather_fetch.main, args = (debug, config, temp)),
        Process(target = arduino_talk.main, args = (debug, config, temp)),
        Process(target = room_measure.main, args = (debug, config, temp)),
        Process(target = hands_move.main, args = (debug, config, temp)),
        Process(target = sound_play.main, args = (debug, config, temp)),
        Process(target = gui_show.main, args = (debug, config, temp)),
        ]

    for x in range(len(process)):
        process[x].start()

    store_process.start()

    input("")

    store_process.terminate()
    with open(CONFIG_FILE, "w") as config_file:
        json.dump(config.copy(), config_file)

    for x in range(len(process)):
        process[x].terminate()

    GPIO.setmode(GPIO.BCM)
    for x in PIN_STEPPER:
        for y in PIN_STEPPER[x]:
            GPIO.setup(PIN_STEPPER[x][y],GPIO.OUT)
            GPIO.output(PIN_STEPPER[x][y], GPIO.LOW)

if __name__ == "__main__":
    main()

最小可重现示例:

test1.py

from multiprocessing import Process, Manager
import test2

def main():
    p = Process(target = test2.main)
    p.start()
    input()
    p.terminate()

if __name__ == "__main__":
    main()

test2.py

#!/usr/bin/env python3
import vlc
import time

VOLUME = 100
URL = "https://orf-live.ors-shoutcast.at/oe1-q2a"

instance = vlc.Instance('--aout=alsa')
player = instance.media_player_new()

vlc.libvlc_audio_set_volume(player, VOLUME)

def main():
    media = instance.media_new(URL) 
    player.set_media(media)

    while True:
        player.play()
        time.sleep(5)
        player.pause()
        time.sleep(5)

if __name__ == "__main__":
    main()

我找到了错误的原因:在 python 中导入模块运行其中的代码。 instanceplayer 因此在导入时创建,因为这两行在 main() 之外。我将它们放在 main() 中,它起作用了。 我不知道为什么在我重新安装 os 之前这没有引起同样的问题,但这已经不重要了。

工作示例:

test1.py

from multiprocessing import Process, Manager
import test2

def main():
    p = Process(target = test2.main)
    p.start()
    input()
    p.terminate()

if __name__ == "__main__":
    main()

test2.py

#!/usr/bin/env python3
import vlc
import time

VOLUME = 100
URL = "https://orf-live.ors-shoutcast.at/oe1-q2a"

def main():
    instance = vlc.Instance('--aout=alsa')
    player = instance.media_player_new()

    vlc.libvlc_audio_set_volume(player, VOLUME)

    media = instance.media_new(URL) 
    player.set_media(media)

    while True:
        player.play()
        time.sleep(5)
        player.pause()
        time.sleep(5)

if __name__ == "__main__":
    main()