willprice / python-omxplayer-wrapper

:tv: Control OMXPlayer, the Raspberry Pi media player, from Python
http://python-omxplayer-wrapper.readthedocs.io
GNU Lesser General Public License v3.0
253 stars 71 forks source link

Better way? A threaded Play and Stop button for a GUI. #68

Closed embgit closed 6 years ago

embgit commented 7 years ago

I got the Wrapper to work concurrently with multiple buttons in the Kivy GUI, but with a concern in B. below about video lag time and my coding method. This code should work with any other type of GUI whose buttons would be calling the methods below.

First, with no thread in A. below, the Play button will work, but the mouse and rest of the GUI becomes disabled until the video finishes. Hence, the Stop button will not work. I'm guessing this would be true with any other GUI besides Kivy.

A. Methods for my Play and Stop buttons.

def play(self, obj):
    omxplayer = OMXPlayer(playfile)
    omxplayer.set_video_pos(0, 0, 1280, 720)
    omxplaytime = omxplayer.duration()
    omxplayer.play()
    sleep(omxplaytime)
    omxplayer.quit()

def stop(self, obj):
    omxplayer.stop()
    omxplayer.quit()

Result: Stop button or any other button will not work.

B. Using a thread allows multiple buttons to work including Stop and future Pause, Step, Forward, and Reverse. The Stop button will trigger the thread's terminate() method and will set self.running = False and kill the loop.

def play(self, obj):
    print('Playing...')
    self.play_thread = PlayVideo1()
    self.play_thread_start_join = Thread(target=self.play_thread.run,)
    self.play_thread_start_join.start()

def stop(self, obj):
    print('Play Stopped...')
    self.play_thread.terminate()
    # wait for actual termination to close up recording screen 
    self.play_thread_start_join.join()

# Play video thread for Omxplayer Wrapper
class PlayVideo1:
    def __init__(self):
        self.running = True # activated by Start button

    def terminate(self): # activated by Stop button
        self.running = False
        print('Terminating thread...')

    def run(self):
        omxplayer = OMXPlayer(playfile)
        omxplayer.set_video_pos(0, 0, 1280, 720)
        time_remaining = omxplayer.duration()
        omxplayer_sleep = 1 # one second sleep sessions below for video to run in loop
        omxplayer.play()
        while self.running:
            time_remaining = time_remaining - omxplayer_sleep
            if time_remaining > 0:
                sleep(omxplayer_sleep)
            print('Can Control = '+str(omxplayer.can_control())) 
            # If can_control() = True, pressing Kivy Stop button will make self.running = False and allow
            # me to take control of this loop and terminate the thread.

        omxplayer.quit()

Result: I have control of the loop as seen with can_control() where it will print 'Can Control = True'. The one second sleep time allows a reasonable response time for the Stop button to work.

Concern: The loop plays a one second video segment at a time and then deducts the time remaining of the video. There does not seem to be a time lag between each looping segment, but as I add more features with code I may get more possibility for lag time.

Question: Is there a better way to coding this?

C. The last example below was my attempt to simplify the process and eliminate the possibility of lag time in the loop of the thread.

        while self.running and sleep(time_remaining): # put sleep here
            print('Can Control = '+str(omxplayer.can_control())) 
            # Program does not reach can_control() above.

        omxplayer.quit()

Result: The video will play in its entirety and the Stop cannot terminate it. I'm guessing this is because it cannot run all of the code including Stop (it sets self.running = False) and the can_control() inside the loop. 'Can Control = True' does not print, in fact nothing prints at all.

Happy New Year!

Thanks for your advice in advance.

Mark

willprice commented 7 years ago

Hey Mark,

Have you had a look at the omxplayer options:

    --win 'x1 y1 x2 y2'     Set position of video window
    --win x1,y1,x2,y2       Set position of video window
    --crop 'x1 y1 x2 y2'    Set crop area for input video
    --crop x1,y1,x2,y2      Set crop area for input video

Seems like you might be able to leave part of the screen visible with your buttons showing to allow control, unless omxplayer takes over mouse input (I've not tried anything like this so I wouldn't know).

There's also --layer n for setting the rendering layer, I'm unsure whether it's possible to paint your own GUI to a higher layer to sit on top of the video, I have no idea how omxplayer does this, I think it render directly to some video buffer...

(EDIT: I might have misinterpreted this, you might not have this problem, I'll leave the above just in case you're having trouble showing buttons)

I'd go with option A, but instead of sleeping after invoking play, I'd create a thread with the specified sleep then quit, keep this in a object and then your quit method can kill the thread and terminate before hand. Option B seems unnecessarily complicated to me, why not just sleep and terminate afterwards? If a user hits your quit button prior to this, shut the thread down and invoke the quit method yourself.

Good luck, I'll be interested to learn how you tackle this :)

Happy new year to you too.