forked from platypush/platypush
Fixed stop handler for vlc plugin.
For some reason, vlc event handlers are not re-entrant (https://github.com/oaubert/python-vlc/issues/44#issuecomment-378520074). This means that the vlc API can't be used from an event handler, and that an event handler that reacts to stop/end-of-stream by releasing the player and the vlc instance will likely get stuck and the app may eventually die with SIGSEGV. Because of this design limitation on the vlc side, the plugin has to run another thread in the main app that monitors the stop event set by the event handler and releases the resources appropriately.
This commit is contained in:
parent
d190560536
commit
833f810d4b
1 changed files with 20 additions and 1 deletions
|
@ -1,5 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
from platypush.plugins.media import PlayerState, MediaPlugin
|
from platypush.plugins.media import PlayerState, MediaPlugin
|
||||||
|
@ -47,6 +49,8 @@ class MediaVlcPlugin(MediaPlugin):
|
||||||
self._on_stop_callbacks = []
|
self._on_stop_callbacks = []
|
||||||
self._title = None
|
self._title = None
|
||||||
self._filename = None
|
self._filename = None
|
||||||
|
self._monitor_thread: Optional[threading.Thread] = None
|
||||||
|
self._on_stop_event = threading.Event()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _watched_event_types(cls):
|
def _watched_event_types(cls):
|
||||||
|
@ -64,11 +68,18 @@ class MediaVlcPlugin(MediaPlugin):
|
||||||
|
|
||||||
def _init_vlc(self, resource):
|
def _init_vlc(self, resource):
|
||||||
import vlc
|
import vlc
|
||||||
|
|
||||||
|
if self._instance:
|
||||||
|
self.logger.info('Another instance is running, waiting for it to terminate')
|
||||||
|
self._on_stop_event.wait()
|
||||||
|
|
||||||
self._reset_state()
|
self._reset_state()
|
||||||
|
|
||||||
for k, v in self._env.items():
|
for k, v in self._env.items():
|
||||||
os.environ[k] = v
|
os.environ[k] = v
|
||||||
|
|
||||||
|
self._monitor_thread = threading.Thread(target=self._player_monitor)
|
||||||
|
self._monitor_thread.start()
|
||||||
self._instance = vlc.Instance(*self._args)
|
self._instance = vlc.Instance(*self._args)
|
||||||
self._player = self._instance.media_player_new(resource)
|
self._player = self._instance.media_player_new(resource)
|
||||||
|
|
||||||
|
@ -76,16 +87,24 @@ class MediaVlcPlugin(MediaPlugin):
|
||||||
self._player.event_manager().event_attach(
|
self._player.event_manager().event_attach(
|
||||||
eventtype=evt, callback=self._event_callback())
|
eventtype=evt, callback=self._event_callback())
|
||||||
|
|
||||||
|
def _player_monitor(self):
|
||||||
|
self._on_stop_event.wait()
|
||||||
|
self.logger.info('VLC stream terminated')
|
||||||
|
self._reset_state()
|
||||||
|
|
||||||
def _reset_state(self):
|
def _reset_state(self):
|
||||||
self._latest_seek = None
|
self._latest_seek = None
|
||||||
self._title = None
|
self._title = None
|
||||||
self._filename = None
|
self._filename = None
|
||||||
|
self._on_stop_event.clear()
|
||||||
|
|
||||||
if self._player:
|
if self._player:
|
||||||
|
self.logger.info('Releasing VLC player resource')
|
||||||
self._player.release()
|
self._player.release()
|
||||||
self._player = None
|
self._player = None
|
||||||
|
|
||||||
if self._instance:
|
if self._instance:
|
||||||
|
self.logger.info('Releasing VLC instance resource')
|
||||||
self._instance.release()
|
self._instance.release()
|
||||||
self._instance = None
|
self._instance = None
|
||||||
|
|
||||||
|
@ -105,7 +124,7 @@ class MediaVlcPlugin(MediaPlugin):
|
||||||
self._post_event(MediaPauseEvent)
|
self._post_event(MediaPauseEvent)
|
||||||
elif event.type == EventType.MediaPlayerStopped or \
|
elif event.type == EventType.MediaPlayerStopped or \
|
||||||
event.type == EventType.MediaPlayerEndReached:
|
event.type == EventType.MediaPlayerEndReached:
|
||||||
self._reset_state()
|
self._on_stop_event.set()
|
||||||
self._post_event(MediaStopEvent)
|
self._post_event(MediaStopEvent)
|
||||||
for cbk in self._on_stop_callbacks:
|
for cbk in self._on_stop_callbacks:
|
||||||
cbk()
|
cbk()
|
||||||
|
|
Loading…
Reference in a new issue