Proper way of changing the media ?

This forum is about all development around libVLC.
GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Proper way of changing the media ?

Postby GuitarExtended » 28 Mar 2013 11:08

Hi,
I use libVLC with the python bindings. Thanks to the help of someone in this forum I managed to get it to play a webradio. It requires playing the playlist first, waiting for it to finish, and then playing the first subitem.
This works fine right now.
The problem I have is when I want to change the media dynamically. I've made a function which creates a new media with the new source and plays it. When I call this function in a very simple Python script with a new url it works fine. But when I try in a bigger Python application (involving a CherryPy webserver and a couple of small threads), it crashes Python without any error message.

So I suppose that i'm not handling the change of media the proper way. Do I need to wate for something to load ? Should I terminate the first media somehow before creating a new one ?
I have no clue what is wrong because I get no errors at all. Is there at least a way I could increase libVLC's verbosity, or make an error log ?

I include the simple script here, but I can't include my bigger python application. I use the same functions (VLC_play() and the callback end_reached()) in the big application.

Code: Select all

import vlc import time url="http://tvradio.ert.gr/radio/liveradio/asx/net.asx" global flag global m global i global p flag = 0 def end_reached(self): global flag flag = 1 print("End reached!") def VLC_play(file): global m global p global i global flag m=i.media_new(file) # Create new media p.set_media(m) # Set URL as the player's media m.release() p.play() # play it while flag == 0: # Wait until the end of the first media has been reached... time.sleep(0.5) print('Loading playlist...') flag = 0 sub_list = m.subitems() # .. and get the sub itmes in the playlist sub_list.lock() sub = sub_list.item_at_index(0) # Get the first sub item sub_list.unlock() sub_list.release() p.set_media(sub) # Set it as the new media in the player p.play() # and play it sub.release() a = 0 while p.is_playing()==0: # This is just a counter that runs until the stream is actually being played time.sleep(0.5) a += 1 print(a) i=vlc.Instance() #Create VLC instance p=i.media_player_new() # Create new media player event_manager = p.event_manager() # Attach event to player (next 3 lines) event=vlc.EventType() event_manager.event_attach(event.MediaPlayerEndReached, end_reached) VLC_play(url)

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 03 Apr 2013 20:58

This topic doesn't stir much interest it seems, but just for future reference in case somebody runs into this kind of problem, I solved this problem by assigning the event_manager() to a global variable. It seems like Python's garbage collection got rid of the event manager after entering the main loop. There's probably a much more elegant way of dealing with this, but at least it solved the problem for me.

OlivierAubert
Developer
Developer
Posts: 92
Joined: 08 Mar 2007 15:43

Re: Proper way of changing the media ?

Postby OlivierAubert » 11 Apr 2013 10:46

Nice that you found a solution, and posted it. Some comments about your code:

- to avoid cluttering your code (and the whole interpreter) with global variables, consider defining your own class (StreamPlayer) and add the necessary variables as instance variables for this class, e.g.

Code: Select all

class StreamPlayer(object): def __init__(self): self.flag = 0 self.event_manager = None ...
In this way, you will encapsulate your data, and avoid strange interactions.

- you do not need to instanciate an EventType to get the attribute values, the constants are defined as class attributes:

Code: Select all

event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, end_reached)
- instead of doing active waiting (time.sleep, etc), you should consider using an existing mainloop (glib mainloop, Qt mainloop, cherrypy mainloop, twisted, etc). Since the libvlc API is not reentrant (you should not call libvlc function from a libvlc event handler), you can trigger mainloop-specific events from the libvlc event handlers and execute the event in methods that will be called from the mainloop.

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 11 Apr 2013 11:41

Hi Olivier,
Thank you very much for your reply. After running into more problems (stuck in waiting loop, etc.) I realized that I indeed needed to create a class. I haven't had time to change my code yet, so your advice is very timely and most welcome !

Cheers.

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 02 May 2013 16:12

Hi,
I've tried what Olivier suggested, but i'm stuck with a seemingly obscure problem. I use the "endreached" event handler to load sub-elements in a playlist. I can trigger the event indefinitely (by trying many different playlist one after the other) as long as the method called doesn't call "play()". Otherwise, the event is triggered once, and cannot be triggered anymore. I know that all the playlists I've tried have sub-elements. Here's my code :

Code: Select all

class MediaPlayer(object): def __init__(self): self.flag = 0 self.VLC_i = vlc.Instance() #Create VLC instance self.VLC_p = self.VLC_i.media_player_new() # Create new media player self.VLC_m = None self.VLC_events = self.VLC_p.event_manager() # Attach event to player (next 2 lines) self.event = vlc.EventType() self.VLC_events.event_attach(self.event.MediaPlayerEndReached, end_reached) def play(self,flux): print("VLC -- Lecture!!!") self.VLC_m = self.VLC_i.media_new(flux) # Create new media print(self.VLC_m.get_mrl()) self.VLC_p.set_media(self.VLC_m) # Set URL as the player's media self.VLC_m.release() self.VLC_p.play() # play it def loadSub(self): sub_list = self.VLC_m.subitems() # .. and get the sub itmes in the playlist print(sub_list.count()) sub_list.lock() sub = sub_list.item_at_index(0) # Get the first sub item print("Sous-Element : " + str(sub.get_mrl())) sous_el = sub.get_mrl() self.play(sous_el) sub_list.unlock() sub_list.release()


The event handler calls a function outside my MediaPlayer class, in my mainloop, which in turn calls the "loadSub()" method of my class. Everything works fine if i comment out "self.play(sous_el)" (I can reach the sub-element all right). Setting the new media of the actual vlc player directly in "loadSub()" and calling its play() method doesn't make any difference.

There's something i really don't get here. Maybe it's got to do with all the lock/unlock/release stuff that i don't really understand...
Any help would be very much appreciated.

OlivierAubert
Developer
Developer
Posts: 92
Joined: 08 Mar 2007 15:43

Re: Proper way of changing the media ?

Postby OlivierAubert » 02 May 2013 16:37

You should post the code for your end_reached method.

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 03 May 2013 09:28

Hi Olivier,
end_reached() calls loadSub().

Code: Select all

def end_reached(self): print("End reached!") VLC_player.loadSub()
VLC_player is an instance of my MediaPlayer class.
My whole code looks like this :

(Declarations)
(some functions)
def end_reached()
...
class website(object)
...
class MediaPlayer(object)
...
VLC_player = MediaPlayer()
VLC_player.play(stream)
site=website()
cherrypy.quickstart(site, config="config.conf")

OlivierAubert
Developer
Developer
Posts: 92
Joined: 08 Mar 2007 15:43

Re: Proper way of changing the media ?

Postby OlivierAubert » 13 May 2013 10:44

Your end_reached method is in another class than the video player, but it is not what matters here. What matters is that no libvlc method is called from the thread of execution of the libvlc callback. In more practical terms, depending on your mainloop library, in your end_reached method you should schedule a method execution to do the actual libvlc work. With the gobject/glib framework for instance, you would do:

Code: Select all

def end_reached(event): gobject.idle_add(a_function_that_loads_sub)
so that the libvlc method (that loads subs) is called in the mainloop thread.

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 11 Jun 2013 22:07

Hi Olivier,

I'm still confused... :(

In my end_reached method I call a method of my cherrypy site (site.load_sub()), in which I call the method from my VLC object (VLC_player.loadSub()). This doesn't work.
Perhaps this is because the VLC_player and site objects are instanciated in the same thread ? This the code I have :

Code: Select all

VLC_player = MediaPlayer() VLC_player.play(getLastRadio()) site=MonSiteWeb() cherrypy.quickstart(site, config="config.conf")
I'm sorry i'm not sure I understand what you mean about the threads and the mainloop. I don't even know where my cherrypy mainloop is (I thought it was the site object, but maybe it's implicit ?).
Could my problem be solved by turning my VLC_player object into a thread ? Right now this is how I instanciate it :

Code: Select all

class MediaPlayer(object): def __init__(self): self.flag = 0 self.VLC_i = vlc.Instance() #Create VLC instance self.VLC_p = self.VLC_i.media_player_new() # Create new media player self.VLC_m = None self.VLC_events = self.VLC_p.event_manager() # Attach event to player (next 3 lines) self.event = vlc.EventType() self.VLC_events.event_attach(self.event.MediaPlayerEndReached, end_reached)
But could it be something like this instead?

Code: Select all

class VLC_player(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.daemon = True self.VLC_i = vlc.Instance() #Create VLC instance self.VLC_p = self.VLC_i.media_player_new() # Create new media player (etc.)
Thanks again for your help and patience.

GuitarExtended
Blank Cone
Blank Cone
Posts: 11
Joined: 25 Mar 2013 22:27

Re: Proper way of changing the media ?

Postby GuitarExtended » 13 Jun 2013 10:04

Hi,

Problem solved !
I didn't know I could make an explicit mainloop outside cherrypy. I have replaced

Code: Select all

cherrypy.quickstart(something)
by

Code: Select all

cherrypy.tree.mount(something) cherrypy.engine.start()

to be able to enter a mainloop i put immediately after :

Code: Select all

while 1: time.sleep(1) if flag==1: Player.loadsub() flag=0
Whenever the end of a file is reached in Player, a function is called which assigns 1 to flag. The mainloop then triggers the loadsub() method of Player.
Now I have issues with configuring cherrypy properly with the two lines above, but that's another story !

Thank you for your help !


Return to “Development around libVLC”

Who is online

Users browsing this forum: Jona and 5 guests