Покадровое воспроизведение / остановка видеофайла в tkinter

#python #video #tkinter #raspberry-pi3

#python #Видео #tkinter #raspberry-pi3

Вопрос:

Я пытаюсь запустить / остановить видеофайл в tkinter на raspberry pi, используя python 3.

Мне нужно, чтобы видео начиналось с начала каждый раз, когда инфракрасный датчик разряжен (сломан), и останавливалось, как только датчик снова становится высоким. В идеале видео должно находиться внутри tkinter canvas, чтобы я мог одновременно отображать на экране другие элементы (например, панель загрузки).

Мне удалось запустить все, кроме видео, которое запускается сразу после обнаружения датчика, но оно замораживает все остальные процессы (например, панель загрузки) и не останавливается при высоком уровне датчика.

вот упрощенная (и непроверенная) версия кода, позволяющая получить представление об общей структуре (реальный код намного длиннее):

 import tkinter as TK
import RPi.GPIO as GPIO
import os

GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.IN)

class App:
    def __init__(self, root):
        self.root = root
        self.root.config(background = 'black', cursor = 'none')
        self.background = TK.Canvas(root, width = 1024, height = 600, bg = 'black')
        self.background.pack()

        self.ext = 0
        self.trial = 0
        self.infrared()

    def infrared(self):
        if (GPIO.input(14) == False):
            self.makebar()

            if (self.ext == 0):
                self.runvideo()

        else:
            os.system("killall omxplayer.bin")
            self.ext = 0

        self.root.after(16, self.infrared)

    def runvideo(self):
        os.system("omxplayer /home/pi/Desktop/testvideo.m4v")

    def makebar():
        self.stimulus_rect = TK.Canvas(root, width = 1024, height = 50, bg= 'white')
            if self.ext < 1000
                self.ext = self.ext   10
                self.stimulus_rect.create_rectangle(0, 0, self.ext, 50, fill="red")
                self.stimulus_rect.place(x=0, y=0, anchor="new")
            else:
                self.trial = self.trial   1
                self.ext = 0

root = TK.Tk()
App(root)
root.mainloop()
  

Из того, что я смог найти в Интернете:
1) tkinter может быть связан с opencv для достижения этой цели, но не похоже, что установка opencv на raspberry pi является простой операцией;
2) В общем случае параметры, связанные с «os», похоже, обречены на неудачу в том, чего я хочу достичь.

Я не смог найти чистый способ сделать это. Сценарием моей мечты было бы загружать в canvas видеокадры один за другим и делать это с частотой 60 Гц (частота экрана). Затем я бы проверил датчик точно с той же частотой и предотвратил загрузку следующего кадра, если датчик не поврежден. В псевдокоде это выглядело бы так

 def infrared(self):
    if (GPIO.input(14) == False):
        self.makebar()

        if (self.ext == 0):
            self.runvideo()

    else:
        self.video.stop
        self.ext = 0
        self.frame = 0

    self.root.after(16, self.infrared)

def runvideo(self):
    self.frame = self.frame   1
    video.run("testvideo.m4v", self.frame)
  

Есть идеи о том, как добиться этого в tkinter на raspberry pi?

спасибо ant

Ответ №1:

После недели исследований, проб и ошибок вот как я в настоящее время достигаю того, что мне было нужно (в псевдокоде):

 #### PSEUDOCODE ####         
from subprocess import Popen  # this library is used to open the video file


class App:
    def __init__(self, root):
        self.moviestart = False

    self.movieduration = 130000
    self.movie = "/home/pi/Desktop/test.mp4"

    def infrared(self):
        if (GPIO.input(IR) == False):
            if not self.moviestart:
                self.makevideo()  # Call the function to start the video
            self.moviestart = True  # Flag that the video has started
            self.moviestop = False  # Flag that the video is currently playing
            self.root.after(self.videoduration,
                            self.stopvideo)  # manually call the stop video function 
            # to stop the video after 13 seconds (video's length). 
            # I could not find a more elegant way of doing this, unfortunately. 
        else:
            self.clear_screen()

    self.root.after(self.refreshIR, self.infrared)


def makevideo(self):
    # popen will open the movie in a window of the size I specified, 
    # so that other element from tkinter can be placed on top of the movie window
    omxc = Popen(['omxplayer', self.movie, '--win', "0 30 800 450"])


def stopvideo(self):
    self.moviestart = False  # flag that movie has been stopped       
    if (self.moviestop == False):  # check if the movie is currently playing
        try:  # this is a cheap workaround other problems I had, do not try this at home
            os.system('killall omxplayer.bin')  # this literally kills any omxplayer instance 
            # currently open
            self.moviestop = True  # flag that the movie is not playing at the moment 
        except:
            pass
  

Я надеюсь, что это может быть полезно кому-либо еще с подобными проблемами. Я обновлю ответ, если найду лучшее решение. На данный момент это работает достаточно хорошо.