JuliusCode / MP4MUSEUM

MP4MUSEUM.org Media Player
GNU General Public License v3.0
32 stars 8 forks source link

How to stop the omxplayer-sync subprocess #32

Closed Peyof closed 1 year ago

Peyof commented 1 year ago

Hello,

For the purposes of a project, I would need to interrupt (via keyboard input for example) the omxplayer-sync subprocess. I tried different methods to stop the subprocess, but it doesn't work: https://www.pythonpool.com/python-subprocess-terminate/#FAQs

Could you put me on the right track?

THANKS,

JuliusCode commented 1 year ago

Hi,

without knowing what your current approach is, there are two things i might help you with:

omxplayer would definitly need to be called via Popen, as the current way to run it will block your script from doing anything else. you need a pipe to send terminate or kill to the process. (import subprocess, signal)

the keyboard has its own library, maybe it needs to be installed via pip first

i might have time next week to check it out. could you tell me more about the context in which this would be used? i have thought of the sync mode of something that is started before the output is „public“, as it will take a minute or so for the sync to work properly- that is why the current implementation only works for one file at a time.

best, Julius

Peyof commented 1 year ago

Hello Julius,

Thank you for your reply.

Here's what I've already tried to do using your script:

`# mp4museum v6 unified - july 2023

(c) julius schmiedel - http://mp4museum.org

import time, vlc, os, glob import keyboard import RPi.GPIO as GPIO from subprocess import * import socket import signal import sys import errno

read audio device config

audiodevice = "0"

if os.path.isfile('/boot/alsa.txt'): f = open('/boot/alsa.txt', 'r') audiodevice = f.read(1)

setup GPIO pin

GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) GPIO.setup(13, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

functions to be called by event listener

with code to filter interference / static discharges

def buttonPause(channel): inputfilter = 0 for x in range(0,200): if GPIO.input(11): inputfilter = inputfilter + 1 time.sleep(.001) if (inputfilter > 50): player.pause()

def buttonNext(channel): inputfilter = 0 for x in range(0,200): if GPIO.input(13): inputfilter = inputfilter + 1 time.sleep(.001) if (inputfilter > 50): player.stop()

play media with vlc

def vlc_play(source): if("loop." in source): vlc_instance = vlc.Instance('--input-repeat=999999999 -q -A alsa --alsa-audio-device hw:' + audiodevice) else: vlc_instance = vlc.Instance('-q -A alsa --alsa-audio-device hw:'+ audiodevice) global player player = vlc_instance.media_player_new() media = vlc_instance.media_new(source) player.set_media(media) player.play() time.sleep(1) current_state = player.get_state() while current_state == 3 or current_state == 4: time.sleep(.01) current_state = player.get_state() media.release() player.release()

find a file, and if found, return its path (for sync)

def search_file(file_name):

Use glob to find files matching the pattern in both directories

file_path_media = f'/media/*/{file_name}'
file_path_boot = f'/boot/{file_name}'

matching_files = glob.glob(file_path_media) + glob.glob(file_path_boot)

if matching_files:
    # Return the first found file name
    return matching_files[0]

# Return False if the file is not found
return False

* run player **

start player twice to make sure it is working

seems weird but works

vlc_play("/home/pi/mp4museum-boot.mp4") vlc_play("/home/pi/mp4museum-boot.mp4")

please do not remove my logo screen

vlc_play("/home/pi/mp4museum.mp4")

add event listener which reacts to GPIO signal

GPIO.add_event_detect(11, GPIO.RISING, callback = buttonPause, bouncetime = 234) GPIO.add_event_detect(13, GPIO.RISING, callback = buttonNext, bouncetime = 1234)

check for sync mode instructions

enableSync = search_file("sync-leader.txt") syncFile = search_file("sync.mp4") if syncFile and enableSync: print("Sync Mode LEADER:" + syncFile) p = Popen(["omxplayer-sync", "-u", "-m", syncFile])

enableSync = search_file("sync-player.txt") syncFile = search_file("sync.mp4") if syncFile and enableSync: print("Sync Mode PLAYER:" + syncFile) p = Popen(["omxplayer-sync", "-u", "-l", syncFile])

while True: if keyboard.read_key() == "a": p.terminate()
if keyboard.read_key() == "s": p = Popen(["omxplayer-sync", "-u", "-m", syncFile]) p = Popen(["omxplayer-sync", "-u", "-l", syncFile])

the loop

while(1): for file in sorted(glob.glob(r'/media//.*')): vlc_play(file)`

For the purposes of an exhibition in Paris, I need to synchronize 4 raspberry pi players so that the videos are played and broadcast, each on their screen, one after the other. So far, no problem. The 4 videos will be of the same length, with video blacks to manage their proper sequencing.

But, using a fifth raspberry pi equipped with 5 buttons, I need to be able to interrupt the synchronized video playback to immediately start playing a video on the corresponding screen. The fifth button will restart synchronized playback. The whole system will be networked.

My idea was therefore to modify the script so as to be able to interrupt the omxplayer-sync subprocess and launch playback (via vlc) of the corresponding video (without the video blacks). I think I can manage communication between the fifth raspberry and the others (I've done this kind of thing before), but for the moment I can't manage to interrupt the omxplayer-sync subprocess, for lack of knowledge on the subject.

Thanks for your help Best, Pierre

JuliusCode commented 1 year ago

hey,

i substituted the start of omxplayer with this:

    process = subprocess.Popen(["omxplayer-sync", "-u", "-l", syncFile])

it worked, i have yet to stop it again ;)

best, Julius.

Peyof commented 1 year ago

Thank you very much Julius, One more thing, how do you stop the subprocess? Because I think I made the same substitution as yours without succeeding in stopping the process. Best and many thanks

Peyof commented 1 year ago

I'm sorry, I didn't quite understand what you said: "i have yet to stop it again ;)"

Peyof commented 1 year ago

Hello Julius, Here's where I'm at:

# mp4museum v6 unified - july 2023

# (c) julius schmiedel - http://mp4museum.org

import time, vlc, os, glob
import RPi.GPIO as GPIO
import subprocess
from multiprocessing import active_children
import signal
import keyboard
import sys

# read audio device config
audiodevice = "0"

if os.path.isfile('/boot/alsa.txt'):
    f = open('/boot/alsa.txt', 'r')
    audiodevice = f.read(1)

# setup GPIO pin
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(13, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

# functions to be called by event listener
# with code to filter interference / static discharges
def buttonPause(channel):
    inputfilter = 0
    for x in range(0,200):
        if GPIO.input(11):
            inputfilter = inputfilter + 1
        time.sleep(.001)
    if (inputfilter > 50):
        player.pause()

def buttonNext(channel):
    inputfilter = 0
    for x in range(0,200):
        if GPIO.input(13):
            inputfilter = inputfilter + 1
        time.sleep(.001)
    if (inputfilter > 50):
        player.stop()

# play media with vlc
def vlc_play(source):
    if("loop." in source):
        vlc_instance = vlc.Instance('--input-repeat=999999999 -q -A alsa --alsa-audio-device hw:' + audiodevice)
    else:
        vlc_instance = vlc.Instance('-q -A alsa --alsa-audio-device hw:'+ audiodevice)
    global player
    player = vlc_instance.media_player_new()
    media = vlc_instance.media_new(source)
    player.set_media(media)
    player.play()
    time.sleep(1)
    current_state = player.get_state()
    while current_state == 3 or current_state == 4:
        time.sleep(.01)
        current_state = player.get_state()
    media.release()
    player.release()

# find a file, and if found, return its path (for sync)
def search_file(file_name):
    # Use glob to find files matching the pattern in both directories
    file_path_media = f'/media/*/{file_name}'
    file_path_boot = f'/boot/{file_name}'

    matching_files = glob.glob(file_path_media) + glob.glob(file_path_boot)

    if matching_files:
        # Return the first found file name
        return matching_files[0]

    # Return False if the file is not found
    return False

# *** run player ****

# start player twice to make sure it is working
# seems weird but works
vlc_play("/home/pi/mp4museum-boot.mp4")
vlc_play("/home/pi/mp4museum-boot.mp4")

# please do not remove my logo screen
vlc_play("/home/pi/mp4museum.mp4")

# add event listener which reacts to GPIO signal
GPIO.add_event_detect(11, GPIO.RISING, callback = buttonPause, bouncetime = 234)
GPIO.add_event_detect(13, GPIO.RISING, callback = buttonNext, bouncetime = 1234)

# check for sync mode instructions
enableSync = search_file("sync-leader.txt")
syncFile = search_file("sync.mp4")
if syncFile and enableSync:
    print("Sync Mode LEADER:" + syncFile)
    p = subprocess.Popen(["omxplayer-sync", "-u", "-m", syncFile])
    pid = p.pid
    active = active_children()
    time.sleep(20)
    os.kill(pid, signal.SIGKILL)
#    os.killpg(os.getpgid(os.getpid()), signal.SIGTERM)
#    for child in active:
#        child.kill()

#    while True:
#        if keyboard.read_key() == "a":
#            print("You Pressed Key a!")
#            pid = p.pid
#            os.kill(pid, signal.SIGKILL)
#            time.sleep(5)

enableSync = search_file("sync-player.txt")
syncFile = search_file("sync.mp4")
if syncFile and enableSync:
    print("Sync Mode PLAYER:" + syncFile)
    subprocess.run(["omxplayer-sync", "-u", "-l",  syncFile]) 

# the loop
while(1):
    for file in sorted(glob.glob(r'/media/*/*.*')):
        vlc_play(file)

I can get out of the "omxplayer-sync" subprocess (with os.kill()), and thus start the video playback by vlc, but the omxplayer playback remains active (you can hear the sound of the video played by omxplayer).

I can also interrupt omxplayer via ssh with :

sudo pkill -9 -f omxplayer-sync
sudo pkill -9 -f omxplayer
sudo pkill -9 -f omxplayer.bin

But I don't know how to integrate this into the python code.

Do you have an idea?

Best,

Peyof commented 1 year ago

Hi Julius,

I finally found a solution that works to interrupt the omxplayer-sync and omxplayer processes:

os.kill(pid, signal.SIGKILL)
os.system("sudo pkill -9 -f omxplayer-sync")
os.system("sudo pkill -9 -f omxplayer")
os.system("sudo pkill -9 -f omxplayer.bin")

Here's my test code:

import time, vlc, os, glob
import RPi.GPIO as GPIO
import subprocess
import signal
import keyboard
import sys

enableSync = search_file("sync-leader.txt")
syncFile = search_file("sync.mp4")
while True:
    if syncFile and enableSync:
        print("Sync Mode LEADER:" + syncFile)
        p = subprocess.Popen(["omxplayer-sync", "-u", "-m", syncFile])
        pid = p.pid
        time.sleep(5)
    if keyboard.read_key() == "a":
        print("You Pressed Key a!")
        os.kill(pid, signal.SIGKILL)
        os.system("sudo pkill -9 -f omxplayer-sync")
        os.system("sudo pkill -9 -f omxplayer")
        os.system("sudo pkill -9 -f omxplayer.bin")
        pvlc = subprocess.Popen(["vlc", "/media/internal/2020_02-WIFI-Priere-Final.mp4"])
        pidvlc = pvlc.pid
        time.sleep(5)   
    if keyboard.read_key() == "s":
        print("You Pressed Key s!")
        os.kill(pidvlc, signal.SIGKILL)
        p = subprocess.Popen(["omxplayer-sync", "-u", "-m", syncFile])
        time.sleep(5)

Best and many thanks

JuliusCode commented 1 year ago

thanks for your reply, i‘ll keep this for future reference