flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Dynamic video playlist with OMXplayer and OSC

Wed Aug 08, 2018 1:55 pm

Hello there,

I'm trying to create a video player which will play several videos after another, depending on several video indexes sent trough an OSC message.
Example: I send a OSC message "playlist" with the following arguments playlist = [2,5,12,34,8] , then my player will play video N°2 then, when it reach the end, switch to the video N°5 and so on, until it reaches the end of the last video and pause, waiting for the next upcoming OSC message. The different video files available are loaded from a text file "playlist.txt". Note that the video playlist length can change (so I can't create a fix number of OMXPlayer instances and fill them with the video).

I've already done this whole project in Processing, but i'm trying to reproduce in Python to see if I can get some performances improvements, and also it's a good project to start a bit with Python... Oh yes, important: I'M VERY NEW TO PYTHON :D

For now i'm only focusing on getting videos to play one after another, using omxplayer-wrapper (http://python-omxplayer-wrapper.readthe ... yer.player)
My first try was:

Code: Select all

from omxplayer.player import OMXPlayer
from time import sleep

import os
import OSC


curMovie = 0
playing = True

playlist_file = open("playlist.txt")
movie = playlist_file.read().split("\n")
path = "/home/pi/Desktop/PYPlayer/"


def handler(addr, tags, data, client_address):
    print(data)
    curMovie = data
    
def player():
    global curMovie
    print("playlist lenth is", len(movie), "and movie count is ", curMovie)
    if curMovie == 0:
        player = OMXPlayer(path + movie[curMovie])
    while playing:
        if curMovie < len(movie) - 1:
            if player.position() > (player.duration() - 0.2) : //temporary solution to determine when player is about to reach the end of the video
                curMovie = curMovie + 1
                player.load(path + movie[curMovie])
                player()
         elif curMovie == len(movie)-1:
         	if player.position() > (player.duration() - 0.2) :
         	                player.pause()
        else:
            player.quit()    
            
player()
I have 2 problems with that solution:
1- The omxplayer window closes when switching between video, which i would like to avoid
2- It works for the first 2 videos I have in my folder (on a total of 5), then stops and gives me the error: 'OMXPlayer' object is not callable

I tried a second approach with 2 instances of OMXplayer, to be able to load the next video while one is playing.
Here, my player function is:

Code: Select all

def multiplayer():
    global curMovie
    if curMovie == 0:
        print("first video is playing", curMovie)
        player0 = OMXPlayer(path + movie[curMovie])
        player1 = OMXPlayer(path + movie[curMovie + 1])
        player1.pause()
        player1.hide_video()
        
    elif curMovie % 2 == 0 and curMovie != 0:
        print(" curMovie is even number   ",curMovie)
        player0.show_video()
        player1 = OMXPlayer(path + movie[curMovie + 1])
        player1.pause()
        player1.hide_video()
        
    elif curMovie % 2 == 1:
        print("curMovie is uneven number  ", curMovie)
        player1.show_video()
        player0 = OMXPlayer(path + movie[curMovie + 1])
        player0.pause()
        player0.hide_video()
                
    while playing:
        if curMovie < len(movie):
            if player0.position() > (player0.duration() - 0.2): 
                curMovie = curMovie + 1
                print("curMovie is   ",curMovie)
                multiplayer()
                
            elif player1.position() > (player1.duration() - 0.2) :
                curMovie = curMovie + 1
                print("curMovie is   ",curMovie)
                multiplayer()
        else:
            print("it's done")
            player1.quit()
            player0.quit()
With this option, my 2 first videos are playing at the same time, blinking really quick from one to another, after they're done, the program quit and return the error that

Code: Select all

player0.position() > (player0.duration() - 0.2)
couldn't run because omxplayer process is no longer alive.

I also tried to dynamically create the good number of OMXPlayer instances to call it one after another:

Code: Select all

for i in range(len(movie)):
	player[i] = OMXPlayer(path + movie[i])
	player[i].pause()
This would work in processing, but apparently not in Python:

Code: Select all

TypeError: 'function' objects does not support item assignments
I know it's going in many directions but i'm trying several options because of my lack of knowledge in Python and now I fell a bit stuck...
If anybody has any clue or example, or can point out something obviously wrong in my code, that would help me a lot

Thanks!

User avatar
paddyg
Posts: 2160
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Dynamic video playlist with OMXplayer and OSC

Wed Aug 08, 2018 9:08 pm

The thing most likely to cause confusion for the python interpreter is using the same names for different things. Python will let you do this and will attempt to work out what to do but it will almost certainly confuse you! Typical issues are naming the program or imported file with a name that clashes with something else (i.e. math.py or random.py). In your program you call your main function player() but then you set an instance of OMXPlayer to a variable with the same name. This might not be relevant to your problem but I would change it. The other thing is that you seem to be calling the function from within itself - recursive style (assuming that's the meaning of player() there) I would have thought that was a bad idea as it will load another instance of OMXPlayer etc. While you are just running one function then global variables are just about manageable but if you have recursive functions with loop forever in them incrementing the same global variable you will end up in a real mess. When you get to your last movie and to within 0.2s of the end then you will run player.pause() every time round the loop, as there is no time.sleep() anywhere in the loop it will happen thousands of times per second. Also there is no way to get past this point. Finally (assuming you provide a way of getting there) when you do player.quit() you should also set playing = False so the while loop ends too (otherwise you will execute player.quit() thousands of times per second and never stop the program running.

PS I'm sure I have seen several people asking on this forum about the window close and open between videos so you should be able to find the solution or verify that there isn't one.

PPS your error when you tried to set player to something was because of reusing the function name.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Re: Dynamic video playlist with OMXplayer and OSC

Thu Aug 09, 2018 9:30 am

thank you for the hints @paddyg , I'll make sure there's no confusion in the variable and function names i'm using

To answer first to your first PS: I know solution to avoid closing window between 2 videos, if I use 2 instances of OMXPlayer:

Code: Select all

player0 = OMXPlayer( path + movie[0])
sleep(player0.duration())
player1 = OMXPlayer( path + movie[1])
That's why I tried to create dynamically the needed number of instances by doing so:

Code: Select all

for i in range(len(movie)):
	player[i] = OMXPlayer(path + movie[i])
Is there a proper way to do so in Python? Should I create a class with an instance of OMXPlayer in it?

About calling the function within itself, i'm trying to re-run the function with the new value of curMovie (I did a mistake in my previous post that's why it was creating a new instance, but instead I want to use load() )

Code: Select all

def multiplayer():
    global curMovie
    if curMovie == 0:
        print("first video is playing", curMovie)
        player0 = OMXPlayer(path + movie[curMovie])
        player1 = OMXPlayer(path + movie[curMovie + 1])
        player1.pause()
        player1.hide_video()
        
    elif curMovie % 2 == 0 and curMovie != 0:
        print(" curMovie is even number   ",curMovie)
        player0.show_video()
        player1.load(path + movie[curMovie + 1])
        player1.pause()
        player1.hide_video()
        
    elif curMovie % 2 == 1:
        print("curMovie is uneven number  ", curMovie)
        player1.show_video()
        player0.load(path + movie[curMovie + 1])
        player0.pause()
        player0.hide_video()
                
    while playing:
        if curMovie < len(movie):
            if player0.position() > (player0.duration() - 0.2): 
                curMovie = curMovie + 1
                print("curMovie is   ",curMovie)
                multiplayer()
                
            elif player1.position() > (player1.duration() - 0.2) :
                curMovie = curMovie + 1
                print("curMovie is   ",curMovie)
                multiplayer()
        else:
            print("it's done")
            player1.quit()
            player0.quit()
In my understanding (which is apparently wrong :D ) this should do the following:
-load and play first video in player0, load, pause and hide 2nd video in player1 (Which is already not working, anybody has experience using these function of the OMXPlayer wrapper?)
-when one of the player reach the end of the video, increment curMovie and run the function again with this new value (waiting for the next player to reach the end of the video and so on...) How do you properly call a function several times? What I've read so far was saying to call the function within itself

Thanks again for the tips

User avatar
paddyg
Posts: 2160
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Dynamic video playlist with OMXplayer and OSC

Thu Aug 09, 2018 11:46 am

busy now but will look later. I would expect you to need to use the subprocess module to run OMXPlayer twice. That's slightly trickier but not too bad
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Re: Dynamic video playlist with OMXplayer and OSC

Fri Aug 10, 2018 11:28 am

Thank you I will investigate with classes on my side

flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Re: Dynamic video playlist with OMXplayer and OSC

Sat Aug 11, 2018 10:51 am

Hello,

some update from my project.
I tried to look into subprocess and tried this example viewtopic.php?t=174062 , but i runed into the same problem and cannot overcome it with my current knowledge.

However, i studied a bit classes in Python and made some progress with this code:

Code: Select all

from omxplayer.player import OMXPlayer
from time import sleep
import os
import OSC

curMovie = 0
p = []
playlist_file = open("playlist.txt")
movie = playlist_file.read().split("\n")
path = "/home/pi/Desktop/PYPlayer/"

class videoPlayer:
    def __init__(self, videoPath):
        self.videoPath = videoPath
        self.classPlayer = OMXPlayer(self.videoPath)
        sleep(0.2)
        self.classPlayer.pause()
        
   def lecture(self):
        self.classPlayer.play()
        


    def end(self):
        return self.classPlayer.is_playing()
        

    def length(self):
        return self.classPlayer.duration()

    def freeze(self):
        self.classPlayer.pause()

    def stop(self):
        self.classPlayer.quit()
        
for i in range(len(movie)):
    p.append(videoPlayer( path + movie[i]))
    print(" video number  ", i, "  is loaded")
    

for i in range(len(movie)):
    p[i].lecture()
    sleep(p[i].length())
    p[i].stop()
I'm successfully loading all the videos in different OMXplayer instances.
But besides the first on which pause as expected, all the other ones start to play and overlap frenetically after few seconds, even if I don't call them to play. I guess there's some confusion made in the interpretation of my different instances, but I don't know how to prevent that...

Any idea?

User avatar
paddyg
Posts: 2160
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Dynamic video playlist with OMXplayer and OSC

Sat Aug 11, 2018 2:32 pm

Hi, I've not had chance to sort this out but did have a look around last night. Using OMXPlayer (the python wrapper module) I found that I could get useful info and control when only one instance of omxplayer was instantiated but when I created a second one then they both returned the same negative value for plyr.position() and it didn't seem possible to pause one and play the other. I then tried a little using subprocess.Popen(['omxplayer', video_file... and this gave a little more control but still seemed to lack the ability to start two players and load and pause in the background then start play. The best result I found was actually to just load the next video 0.5s before the previous one was supposed to finish based on the running time and using sleep(), if they did overlap a little bit then the scrambled position() didn't matter. The loading time might depend on the size of the video which would scramble things. I will have a further look at subprocess and let you know.

Code: Select all

from omxplayer.player import OMXPlayer
import time

video_path = '/home/pi/pi3d_demos/{}'
video_list = ['video1.mp4', 'video2.mp4','video3.mp4', 'video4.mp4']
plrs = [None, None]
for i, vid in enumerate(video_list):
	ix = i % 2
	plrs[ix] = OMXPlayer(video_path.format(vid))
	time.sleep(plrs[ix].duration() - 0.5)
PS using classes is sometimes a good idea where you want to encapsulate properties and functionality, especially if you want to pass objects between threads and avoid globals
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Re: Dynamic video playlist with OMXplayer and OSC

Sun Aug 12, 2018 1:34 pm

Thanks again for your help @paddyg
I tried your code and it reacted as you described. but even if sometimes it overlaps just what it supposed to (0.5s) most of the time it's overlapping way more (several seconds) as if duration() doesn't send back.
Then I tried to

Code: Select all

print(plrs[ix].duration())
juste before sleep(), and it returned 2 times duration of the first video, then one time the duration of the second video and 2 times the duration of the 4th one.
Basically I guess it's sometimes still loading the new video when we're calling duration(), then it's not using the good value.

Code: Select all

plrs[xi].quit()
at the end of the loop fix the problem, but then we're exiting omxplayer between each video.
I've tried to quit the previous player before asking duration of the current one:

Code: Select all

plrs[ix] = OMXPlayer( path + vid)
if i != 0:
	plrs[(ix + 1) % 2].quit()
print(plrs[ix].duration())
but it's crashing after the second video, as if there's still a confusion between the instances of OMXPlayer

flashbacker
Posts: 6
Joined: Thu May 12, 2016 3:46 pm

Re: Dynamic video playlist with OMXplayer and OSC

Wed Aug 15, 2018 12:30 pm

Ok, finally solved the last bit of problem! :)
thanks a lot for your help through this @paddyg

It needed to be declared in omxplayer arguments that we were calling a new instance

Code: Select all

from omxplayer.player import OMXPlayer
from time import sleep

playlist_file = open("playlist.txt")
movie = playlist_file.read().split("\n")
path = "/home/pi/Desktop/PYPlayer/"



plrs = [None, None]
bus = ["org.mpris.MediaPlayer2.omxplayer2" ,"org.mpris.MediaPlayer2.omxplayer3",]

for i, vid in enumerate(movie):
    ix = i % 2
    
    plrs[ix] = OMXPlayer(path + vid, args=['--layer', str((ix+1)%2)], dbus_name=bus[ix])
    print(plrs[ix].duration())
    sleep(plrs[ix].duration() - 0.65)

Little surprise here, i'm using omxplayer2 and omxplayer3 instead of one, because this instance was always returning the duration of the first video... Strange behavior but it's working now, ready to move on the next problem! :)

Return to “Python”

Who is online

Users browsing this forum: No registered users and 12 guests