From 106a20ad3345203830439b14f0570509c5510ed8 Mon Sep 17 00:00:00 2001 From: Miqra Developer Date: Wed, 26 Sep 2018 19:14:21 +0000 Subject: [PATCH] Implemented use of glimagesink Added looping functions Removed separate quickplayer. Quickplayer is now integrated in normal player --- .gitignore | 2 +- VERSION.py | 0 debian/postinst | 0 etc/init.d/mediaserver | 6 +- mediacore-mediaserver | 0 mediaserver/PKG_CONFIG.py | 0 mediaserver/__init__.py | 0 mediaserver/audioplayer.py | 68 ---- mediaserver/basicplayer.py | 11 +- mediaserver/client.py | 357 +++++++++++++++++++++ mediaserver/dbus_smartobject.py | 144 +++++++++ mediaserver/event.py | 0 mediaserver/mediaserver.py | 125 +++++++- mediaserver/monitoredplayer.py | 102 ++++++ mediaserver/quickplayer.py | 4 +- setup.py | 0 stdeb.cfg | 2 +- test_basicplayer.py | 15 +- test_client.py | 90 ++++++ usr-share-mediaserver/background-image.png | Bin usr-share-mediaserver/background-image.svg | 0 21 files changed, 828 insertions(+), 98 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 VERSION.py mode change 100644 => 100755 debian/postinst mode change 100644 => 100755 etc/init.d/mediaserver mode change 100644 => 100755 mediacore-mediaserver mode change 100644 => 100755 mediaserver/PKG_CONFIG.py mode change 100644 => 100755 mediaserver/__init__.py delete mode 100644 mediaserver/audioplayer.py mode change 100644 => 100755 mediaserver/basicplayer.py create mode 100755 mediaserver/client.py create mode 100755 mediaserver/dbus_smartobject.py mode change 100644 => 100755 mediaserver/event.py mode change 100644 => 100755 mediaserver/mediaserver.py create mode 100755 mediaserver/monitoredplayer.py mode change 100644 => 100755 mediaserver/quickplayer.py mode change 100644 => 100755 setup.py mode change 100644 => 100755 stdeb.cfg mode change 100644 => 100755 test_basicplayer.py create mode 100755 test_client.py mode change 100644 => 100755 usr-share-mediaserver/background-image.png mode change 100644 => 100755 usr-share-mediaserver/background-image.svg diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 0956cb0..412b8f1 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ build/ deb_dist/ -*.pyc +**/*.pyc diff --git a/VERSION.py b/VERSION.py old mode 100644 new mode 100755 diff --git a/debian/postinst b/debian/postinst old mode 100644 new mode 100755 diff --git a/etc/init.d/mediaserver b/etc/init.d/mediaserver old mode 100644 new mode 100755 index ea6968e..a89ac9e --- a/etc/init.d/mediaserver +++ b/etc/init.d/mediaserver @@ -47,8 +47,8 @@ VERBOSE=yes # do_start() { - # start background image - fbi -a -noverbose -t 1 -T 1 -d /dev/fb0 /usr/share/mediaserver/background-image.png >/dev/null 2> /dev/null & + # start background image (No, fbdrawer can be used for that now) + # fbi -a -noverbose -t 1 -T 1 -d /dev/fb0 /usr/share/mediaserver/background-image.png >/dev/null 2> /dev/null & # Return # 0 if daemon has been started @@ -71,7 +71,7 @@ do_start() do_stop() { # kill background image (fbi) - killall fbi + # killall fbi # Return # 0 if daemon has been stopped diff --git a/mediacore-mediaserver b/mediacore-mediaserver old mode 100644 new mode 100755 diff --git a/mediaserver/PKG_CONFIG.py b/mediaserver/PKG_CONFIG.py old mode 100644 new mode 100755 diff --git a/mediaserver/__init__.py b/mediaserver/__init__.py old mode 100644 new mode 100755 diff --git a/mediaserver/audioplayer.py b/mediaserver/audioplayer.py deleted file mode 100644 index ab165a9..0000000 --- a/mediaserver/audioplayer.py +++ /dev/null @@ -1,68 +0,0 @@ -# perform gstreamer imports (python3 style) -import gi -gi.require_version('Gst','1.0') - -from gi.repository import GObject - -from threading import Thread -import time - -# import from this module -from basicplayer import BasicPlayer - -class MonitorThread(Thread): - def __init__(self,player): - self.player = player - Thread.__init__(self) - self.daemon = True # as in: don't wait on this thread to quit - self._running = False - - def stop(self): - if self._running: - self._running = False - #self.join(0.5) - - def run(self): - self._running = True - while self._running: - if self.player.player_state == "PLAYING" and self.player.playtime > 0: - position = self.player.position() - if (position - self.player.startpos) >= self.player.playtime: - self.player._finished() - if self._running and time is not None: - time.sleep(0.2) - pass - -class AudioPlayer(BasicPlayer): - - def __init__(self): - BasicPlayer.__init__(self) - # setup monitor thread - self.monitor = MonitorThread(self) - self.monitor.start() - self.playtime = -1 - - def __del__(self): - self.monitor.stop() - - def playfor(self,duration): - if duration > 0: # not much point in wasting system resources otherwise - self.startpos = self.position() - self.playtime = duration -# self.monitor.start() - BasicPlayer.play(self) - elif duration < 0: # same as normal play - self.play() - - def play(self): - if self.player_state != "PAUSED": - self.playtime = -1; - BasicPlayer.play(self) - - def stop(self): - self.playtime = -1; - BasicPlayer.stop(self) - #self.monitor.stop() - -GObject.type_register(AudioPlayer) - \ No newline at end of file diff --git a/mediaserver/basicplayer.py b/mediaserver/basicplayer.py old mode 100644 new mode 100755 index 8b53eaf..cd48c77 --- a/mediaserver/basicplayer.py +++ b/mediaserver/basicplayer.py @@ -100,7 +100,6 @@ class BasicPlayer(GObject.GObject): self.player_state = "LOADING" self.tags.clear() - def stop(self): if self.player_state in ["PLAYING","PAUSED",]: @@ -109,25 +108,27 @@ class BasicPlayer(GObject.GObject): self.player_state = "READY" self.emit('playback-stopped') - def pause(self): + def pause(self,notify=True): # cannot pause/unpause if we're waiting for buffer if self.player_state in ["PLAYING",] and not self.buffering: self.player_state = "PAUSED" self.pipeline.set_state(Gst.State.PAUSED) - self.emit('playback-paused') + if notify: + self.emit('playback-paused') def _finished(self): self._running = False self.pipeline.set_state(Gst.State.READY) self.seek(0) - self.player_state = "READY" + self.player_state = "PAUSED" self.emit('playback-finished') + def prepare_gstreamer(self): # bin containing the recorder stuff self.pipeline = Gst.ElementFactory.make("playbin", None) - videosink = Gst.ElementFactory.make("eglglessink", None) + videosink = Gst.ElementFactory.make("glimagesink", None) alsasink = Gst.ElementFactory.make("alsasink", None) # set output device diff --git a/mediaserver/client.py b/mediaserver/client.py new file mode 100755 index 0000000..bef2c04 --- /dev/null +++ b/mediaserver/client.py @@ -0,0 +1,357 @@ +import dbus +import dbus.service +import dbus.mainloop.glib + +import dbus_smartobject +import event + +import __main__ +import os + +import gi +from gi.repository import GLib +from gi.repository import GObject + +class MediaClient(dbus_smartobject.DBusSmartObject): + def __init__(self,quickfolder=None): + + if not quickfolder is None: + self._quickfolder = quickfolder + else: + self._quickfolder = os.path.dirname(__main__.__file__) + + self.Playing = False + self.Paused = False + self.Stopped = True + self.Buffering = False + + self.status_url = None + self.status_position = 0 + self.status_reloading = False + + # initialize the events + self.OnLoading = event.Event() + self.OnReady = event.Event() + self.OnPlaying = event.Event() + self.OnPaused = event.Event() + + self.OnReconnectReady = event.Event() + self.OnReconnectPlaying = event.Event() + self.OnReconnectPaused = event.Event() + + self.OnStopped = event.Event() + self.OnFinished = event.Event() + self.OnFinishing = event.Event() + + self.OnLoadFail = event.Event() + self.OnRunFail = event.Event() + + self.OnConnect = event.Event() + self.OnReconnect = event.Event() + self.OnDisconnect = event.Event() + + self.OnBuffering = event.Event() + + # init base object + dbus_smartobject.DBusSmartObject.__init__( self, + service='nl.miqra.MediaCore.Media', + path='/nl/miqra/MediaCore/Media', + interface='nl.miqra.MediaCore.Media', + systembus=True) + + self.statusticker() + + def init_busobject(self,busobject): + # this is what gives us the multi media keys. + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnLoading', self._onLoading) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnReady', self._onReady) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnPlaying', self._onPlaying) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnPaused', self._onPaused) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnStopped', self._onStopped) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnFinished', self._onFinished) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnFinishing', self._onFinishing) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnLoadFail', self._onLoadFail) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnRunFail', self._onRunFail) + + # connect_to_signal registers our callback function. + busobject.connect_to_signal('OnBuffering', self._onBuffering) + + def on_connection_lost(self): + self.OnDisconnect() + + def on_connection_made(self): + self.OnConnect() + + def on_connection_regained(self): + if self.status_url is not None: + self.status_reloading = True + self.LoadUrl(self.status_url) + + def statusticker(self): + if self.connected(): + if not self.status_reloading and (self.Playing or self.Paused): + self.status_position = self.Position() + #print "backed up position: {0}".format(self.status_position) + + GObject.timeout_add(1000, self.statusticker); + + #callback function + def _onLoading(self,url): + """ gets called when a source has been requested for playback + """ + self.OnLoading(url) + + def _onBuffering(self,pct): + """ gets called when a source has been requested for playback + """ + if pct < 100: + self.Buffering = True + else: + self.Buffering = False + self.OnBuffering(pct) + + def _onReady(self,url,tags): + """ gets called when a source is ready for playback + """ + if not self.status_reloading: + self.Playing = False + self.Paused = False + self.Stopped = True + self.status_url = url + self.OnReady(url,tags) + else: + if self.Playing or self.Paused: + self.Play() + else: + self.status_reloading = False + self.OnReconnectReady() + + def _onPlaying(self): + """ gets called when a playback has started + """ + if not self.status_reloading: + self.Playing = True + self.Paused = False + self.Stopped = False + self.OnPlaying() + else: + self.Seek(self.status_position) + if self.Paused: + self.Pause() + else: + self.status_reloading = False + self.OnReconnectPlaying() + pass + + def _onPaused(self): + """ gets called when a playback is paused + """ + if not self.status_reloading: + self.Playing = False + self.Paused = True + self.Stopped = False + self.OnPaused() + else: + self.status_reloading = False + self.OnReconnectPaused() + + + def _onStopped(self): + """ gets called when playback has stopped + """ + self.Playing = False + self.Paused = False + self.Stopped = True + self.status_position = 0 + self.OnStopped() + + def _onFinished(self): + """ gets called when playback has completed + """ + self.Playing = False + self.Paused = False + self.Stopped = True + self.status_position = 0 + self.OnFinished() + + def _onFinishing(self): + self.OnFinishing() + + def _onLoadFail(self,reason): + """ gets called when loading a source for playback fails + """ + self.OnLoadFail(reason) + + def _onRunFail(self,reason): + """ gets called when playback fails + """ + self.OnRunFail(reason) + + def QuickPlay(self, file, duration = None): + """ Directly play back a local file + """ + path = file + # check if the file exists, if not, try to find it in the base folder for quickplay or else as a subfolder of the __main__ folder + if not os.path.isfile(path): + path = os.path.join(self._quickfolder,file) + + if not os.path.isfile(path): + path = os.path.join(os.path.dirname(__main__.__file__),file) + + try: + if duration is None or duration <= 0: + self.call("QuickPlay",file) + else: + self.call("QuickPlayFor",file,duration) + except dbus_smartobject.NoConnectionError as x: + print "Could not call QuickPlay function because of no connection" + pass + + def QuickPlayUrl(self, url, duration = None): + """ Directly play back a URL + """ + try: + if duration is None or duration <= 0: + self.call("QuickPlayUrl",url) + else: + self.call("QuickPlayUrlFor",url,duration) + except dbus_smartobject.NoConnectionError as x: + print "Could not call QuickPlayUrl function because of no connection" + pass + + def QuickLoop(self, file): + """ Directly play back a local file + """ + path = file + # check if the file exists, if not, try to find it in the base folder for quickplay or else as a subfolder of the __main__ folder + if not os.path.isfile(path): + path = os.path.join(self._quickfolder,file) + + if not os.path.isfile(path): + path = os.path.join(os.path.dirname(__main__.__file__),file) + + try: + self.call("QuickLoop",file) + except dbus_smartobject.NoConnectionError as x: + print "Could not call QuickPlay function because of no connection" + pass + + def QuickLoopUrl(self, url): + """ Directly play back a URL + """ + try: + self.call("QuickLoopUrl",url) + except dbus_smartobject.NoConnectionError as x: + print "Could not call QuickPlayUrl function because of no connection" + pass + + def LoadFile(self, file): + """ Load a local file for playback + """ + try: + self.call("LoadFile",file) + except dbus_smartobject.NoConnectionError as x: + print "Could not call LoadFile because of no connection" + pass + + def LoadUrl(self, url): + """ Load an url for playback + """ + try: + self.call("LoadUrl",url) + except dbus_smartobject.NoConnectionError as x: + print "Could not call LoadUrl because of no connection" + pass + + def PlayFor(self, duration): + """ Starts playback for [duration] seconds + """ + try: + self.call("PlayFor",duration) + except dbus_smartobject.NoConnectionError as x: + print "Could not call PlayFor because of no connection" + pass + + def Play(self): + """ Starts/resumes playback + """ + try: + self.call("Play") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Play because of no connection" + pass + + def Loop(self): + """ Starts/resumes playback + """ + try: + self.call("Loop") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Loop because of no connection" + pass + + def Pause(self): + """ Pauses playback + """ + try: + self.call("Pause") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Pause because of no connection" + pass + + def Stop(self): + """ Stops playback + """ + try: + self.call("Stop") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Stop because of no connection" + pass + + def Length(self): + """ returns length of currently loaded source + """ + try: + return self.call("Length") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Length because of no connection" + return 0 + + def Position(self): + """ returns current position in the source + """ + try: + return self.call("Position") + except dbus_smartobject.NoConnectionError as x: + print "Could not call Position because of no connection" + return 0 + + def Seek(self,position): + """ jump to new position in the source + """ + try: + self.call("Seek",position) + self.status_position = position + + except dbus_smartobject.NoConnectionError as x: + print "Could not call because of no connection" + pass + diff --git a/mediaserver/dbus_smartobject.py b/mediaserver/dbus_smartobject.py new file mode 100755 index 0000000..9064f06 --- /dev/null +++ b/mediaserver/dbus_smartobject.py @@ -0,0 +1,144 @@ +import dbus +import dbus.service +import dbus.mainloop.glib +import event + +class NoConnectionError(Exception): + pass + +class DBusSmartObject: + def __init__(self,service,path,interface,systembus=False): + # store service name and object path + self._service = service + self._object_path = path + self._interface = interface + + if systembus == True: + self._bus_type = dbus.Bus.TYPE_SYSTEM + else: + self._bus_type = dbus.Bus.TYPE_SESSION + + # prepare flex object + self._bus = None + self.busobject = None + self._beenconnected = False + + # set up the glib main loop. + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + # connect to the dbus object to get information about existing connections + self._dbus = dbus.Bus(dbus.Bus.TYPE_SYSTEM) + self._dbus_object = self._dbus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + + # this is what gives us the multi media keys. + self._dbus_interface='org.freedesktop.DBus' + + # connect_to_signal registers our callback function. + self._dbus_object.connect_to_signal('NameOwnerChanged', self._onNameOwnerChanged) + + # start initializing the connection + self._initialize_new_connection() + + def close(self): + if self._bus is not None: + self._bus = None + self.busobject = None + + if self._dbus is not None: + self._dbus = None + + def init_busobject(self,busobject): + ''' Override in child class to initialize bus signals on connect and reconnect + ''' + pass + + def on_connection_lost(self): + ''' Override in child class to handle connection lost + ''' + pass + + def on_connection_made(self): + ''' Override in child class to handle connection made + ''' + pass + + def on_connection_regained(self): + ''' Override in child class to handle connection regained + ''' + pass + + def _initialize_new_connection(self): + # test if service is available on the selected bus. Skip otherwise + if self._dbus_object.NameHasOwner(self._service, dbus_interface=self._dbus_interface): + print "Initializing new connection to {0}".format(self._service) + + self._bus = dbus.Bus(self._bus_type) + self.busobject = self._bus.get_object(self._service, self._object_path) + self.init_busobject(self.busobject) + self.on_connection_made() + + if self._beenconnected == True: + self.on_connection_regained() + else: + self._beenconnected = True + else: + print "Could not create connection to {0}".format(self._service) + # bus object is not available - so disconnect the bus + + if self._bus is not None: + self._bus = None + self.busobject = None + + def _close_existing_connection(self): + if self._bus is not None: + print "Lost connection to {0}".format(self._service) + #self._bus.close() + self._bus = None + self.busobject = None + self.on_connection_lost() + + def _onNameOwnerChanged(self,name,old_adr,new_adr): + ''' Detects changes in service availablility + ''' + if name == self._service: + if old_adr == "": # Service just became available + self._initialize_new_connection() + elif new_adr == "": # Service just became unavailable + self._close_existing_connection() + else: # service changed address + self._initialize_new_connection() + + def call(self,method, *args, **kwargs): + if kwargs.has_key("interface"): + interface = kwargs["interface"] + else: + interface = self._interface + + if self.busobject is not None: + #print "Attempting to call {0}".format(method) + method = self.busobject.get_dbus_method(method,dbus_interface=interface) + + #print "Got method - calling with arguments: {0}".format(args) + return method(*args) + else: + raise NoConnectionError("Currently no connection to service {0}".format(self._service)) + + def trycall(self,method, *args, **kwargs): + # see if we have an default return value in case of error + if 'default' in kwargs: + default = kwargs['default'] + del kwargs['default'] + else: + default = None + try: + return self.call(method, *args, **kwargs) + except NoConnectionError as x: + print "Could not call {0} because there is currently no connection to {1}".format(method,self._service) + return default + + def connected(self): + if self.busobject is not None: + return True + else: + return False + \ No newline at end of file diff --git a/mediaserver/event.py b/mediaserver/event.py old mode 100644 new mode 100755 diff --git a/mediaserver/mediaserver.py b/mediaserver/mediaserver.py old mode 100644 new mode 100755 index 7c87f49..082e916 --- a/mediaserver/mediaserver.py +++ b/mediaserver/mediaserver.py @@ -26,7 +26,7 @@ from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) # imports from module -from audioplayer import AudioPlayer +from monitoredplayer import MonitoredPlayer from quickplayer import QuickPlayer @@ -36,7 +36,7 @@ class MediaService(dbus.service.Object): bus_name = dbus.service.BusName('nl.miqra.MediaCore.Media', bus=dbus.SystemBus()) dbus.service.Object.__init__(self, bus_name, '/nl/miqra/MediaCore/Media') - self.player = AudioPlayer() + self.player = MonitoredPlayer() self.quickplayer = QuickPlayer() self.player.connect('playback-ready',self.onPlayerReady) # parameters: source_file tag_dict @@ -50,30 +50,103 @@ class MediaService(dbus.service.Object): self.loadcount = 0; self.loadmax = 1; - + + self.quickplay = False + self.quickplayduration = 0 + self.quickplayloop = False + + self.tick() + + # Keepalive timer to help react to Keyboard interrupts + def tick(self): + GObject.timeout_add(100, self.tick) + @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='') def QuickPlay(self, file,): """ Directly play back a local file """ - self.quickplayer.play(file) + if self.loadcount < self.loadmax: + print "Quickplaying file {0}".format(file) + self.quickplay = True + self.quickplayduration = 0 + self.quickplayloop = False + self.player.load(file) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='sd', out_signature='') def QuickPlayFor(self, file, duration): """ Directly play back a local file """ - self.quickplayer.playfor(file,duration) + if self.loadcount < self.loadmax: + print "Quickplaying file {0} for {1} seconds".format(file,duration) + self.quickplay = True + self.quickplayduration = duration + self.quickplayloop = False + self.player.load(file) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); +# self.quickplayer.playfor(file,duration) @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='') def QuickPlayUrl(self, url,): """ Directly play back a url """ - self.quickplayer.playurl(url) + if self.loadcount < self.loadmax: + print "Quickplaying file {0}".format(file) + self.quickplay = True + self.quickplayduration = 0 + self.quickplayloop = False + self.player.load_uri(file) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='sd', out_signature='') def QuickPlayUrlFor(self, url, duration): """ Directly play back a url """ - self.quickplayer.playurlfor(url,duration) + if self.loadcount < self.loadmax: + print "Quickplaying file {0} for {1} seconds".format(file,duration) + self.quickplay = True + self.quickplayduration = duration + self.quickplayloop = False + self.player.load_uri(file) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); + + @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='') + def QuickLoop(self, file,): + """ Directly play back a url + """ + if self.loadcount < self.loadmax: + print "Quickplaying file {0}".format(file) + self.quickplay = True + self.quickplayduration = 0 + self.quickplayloop = True + self.player.load(file) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); + + @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='') + def QuickLoopUrl(self, url,): + """ Directly play back a url + """ + if self.loadcount < self.loadmax: + print "Quickplaying url {0}".format(uri) + self.quickplay = True + self.quickplayduration = 0 + self.quickplayloop = True + self.player.load_uri(url) + self.loadcount += 1; + else: + print "Skipping load of file {0} because maximum simultaneous loads ({1}) was reached. Wait until ready....".format(file,self.loadmax); + + @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='s', out_signature='') @@ -82,7 +155,7 @@ class MediaService(dbus.service.Object): """ if self.loadcount < self.loadmax: print "Loading file {0}".format(file) - print file + self.quickplay = False self.player.load(file) self.loadcount += 1; self.OnLoading("file://{0}".format(file)) @@ -95,6 +168,7 @@ class MediaService(dbus.service.Object): """ if self.loadcount < self.loadmax: print "Loading url {0}".format(uri) + self.quickplay = False self.player.load_uri(uri) self.loadcount += 1; self.OnLoading(uri) @@ -115,6 +189,14 @@ class MediaService(dbus.service.Object): pos = self.player.position() print "Starting playback at timestamp {0}".format(pos) self.player.play() + + @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='') + def Loop(self): + """ Starts/resumes playback + """ + pos = self.player.position() + print "Starting playback at timestamp {0}".format(pos) + self.player.loop() @dbus.service.method(dbus_interface='nl.miqra.MediaCore.Media', in_signature='', out_signature='') def Pause(self): @@ -163,8 +245,19 @@ class MediaService(dbus.service.Object): # reset load counter self.loadcount = 0; - # signal ready - self.OnReady(filename,taglist) + if self.quickplay: + if self.quickplayloop: + print("Direct looping") + self.player.loop() + elif self.quickplayduration > 0: + print("Direct playing for {0} s".format(self.quickplayduration)) + self.player.playfor(self.quickplayduration) + else: + print("Direct playing") + self.player.play() + else: + # signal ready + self.OnReady(filename,taglist) def onPlayerPlaying(self,player): self.OnPlaying() @@ -237,6 +330,12 @@ class MediaService(dbus.service.Object): """ gets called when playback has completed """ pass + + @dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='') + def OnFinishing(self): + """ gets called when playback is nearly completed + """ + pass @dbus.service.signal(dbus_interface='nl.miqra.MediaCore.Media', signature='s') def OnLoadFail(self,reason): @@ -262,4 +361,8 @@ def Run(): signal.signal(signal.SIGINT, onsigint) print "Starting..." - loop.run() + try: + loop.run() + except KeyboardInterrupt: + print "Quitting" + loop.quit() \ No newline at end of file diff --git a/mediaserver/monitoredplayer.py b/mediaserver/monitoredplayer.py new file mode 100755 index 0000000..7a30797 --- /dev/null +++ b/mediaserver/monitoredplayer.py @@ -0,0 +1,102 @@ +# perform gstreamer imports (python3 style) +import gi +gi.require_version('Gst','1.0') + +from gi.repository import GObject + +from threading import Thread +import time + +# import from this module +from basicplayer import BasicPlayer + +class MonitorThread:#(Thread): + def __init__(self,player): + self.player = player + #Thread.__init__(self) + self.daemon = True # as in: don't wait on this thread to quit + self._running = False + + def stop(self): + if self._running: + self._running = False + #self.join(0.5) + + def start(self): + self._running = True + self.run() + + def run(self): + if self.player.player_state == "PLAYING": + position = self.player.position() + duration = self.player.duration() + if position < 0: # handle invalid operation + position = duration + + print("\rTime Left: {0}".format(duration - position)) + + # handle finishing of limited playtime + if self.player.playtime > 0: + if (position - self.player.startpos) - self.player.playtime <= 0: + #self._player.pause(notify=False) + print "Finished limited play" + self.player._finished() + + if self._running and time is not None: + GObject.timeout_add(20, self.run) + +class MonitoredPlayer(BasicPlayer): + def __init__(self): + BasicPlayer.__init__(self) + # setup monitor thread + self.monitor = MonitorThread(self) + self.monitor.start() + self.playtime = -1 + self.looping=False + + def __del__(self): + self.monitor.stop() + + def playfor(self,duration,loop=False): + loop = False + if duration > 0: # not much point in wasting system resources otherwise + self.startpos = self.position() + self.playtime = duration + BasicPlayer.play(self) + elif duration < 0: # same as normal play + self.play() + + def loop(self): + self.looping = True + if self.player_state != "PAUSED": + self.startpos = 0 + self.playtime = -1; + BasicPlayer.play(self) + + + def play(self): + self.looping = False + if self.player_state != "PAUSED": + self.startpos = 0 + self.playtime = -1; + BasicPlayer.play(self) + + def stop(self): + self.playtime = -1; + BasicPlayer.stop(self) + #self.monitor.stop() + + + def _finished(self): + if self.looping: + print("Finished - looping") + self.seek(0) + if self.player_state in ["READY","PAUSED",]: + self.player_state = "PLAYING" + self.pipeline.set_state(Gst.State.PLAYING) + else: + BasicPlayer._finished(self) + + +GObject.type_register(MonitoredPlayer) + \ No newline at end of file diff --git a/mediaserver/quickplayer.py b/mediaserver/quickplayer.py old mode 100644 new mode 100755 index b91df3a..b152688 --- a/mediaserver/quickplayer.py +++ b/mediaserver/quickplayer.py @@ -5,13 +5,13 @@ gi.require_version('Gst','1.0') from gi.repository import GObject # import from this module -from audioplayer import AudioPlayer +from monitoredplayer import MonitoredPlayer class QuickPlayer(GObject.GObject): def __init__(self): GObject.GObject.__init__(self) - self.player = AudioPlayer() + self.player = MonitoredPlayer() self._duration = -1; self.player.connect("playback-ready",self.onReady) self.player.connect("playback-error",self.onError) diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 diff --git a/stdeb.cfg b/stdeb.cfg old mode 100644 new mode 100755 index efa8e71..5ffe076 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -1,6 +1,6 @@ [DEFAULT] -Depends: python-dbus, python-gi, fbi, gir1.2-gstreamer-1.0, gir1.2-gst-plugins-base-1.0, gstreamer1.0-plugins-good, gstreamer1.0-plugins-ugly, gstreamer1.0-plugins-bad, gstreamer1.0-alsa, gstreamer1.0-omx, gstreamer1.0-libav +Depends: python-dbus, python-gi, gir1.2-gstreamer-1.0, gir1.2-gst-plugins-base-1.0, gstreamer1.0-plugins-good, gstreamer1.0-plugins-ugly, gstreamer1.0-plugins-bad, gstreamer1.0-alsa, gstreamer1.0-omx, gstreamer1.0-libav XS-Python-Version: >= 2.6 Section: sound Package: mediaserver diff --git a/test_basicplayer.py b/test_basicplayer.py old mode 100644 new mode 100755 index 085cbf5..b00443a --- a/test_basicplayer.py +++ b/test_basicplayer.py @@ -3,7 +3,7 @@ from mediaserver.audioplayer import AudioPlayer from mediaserver.quickplayer import QuickPlayer from gi.repository import GObject - +import sys def onReady(player, file, tags): print "Starting {0} ...\n".format(file) @@ -29,13 +29,14 @@ def onStop(player): print "Quitting...." loop.quit() -player = AudioPlayer() -player.connect("playback-ready",onReady) -player.connect("playback-playing",onPlaying) -player.connect("playback-finished",onStop) -player.connect("playback-stopped",onStop) +player = QuickPlayer() +#player.connect("playback-ready",onReady) +#player.connect("playback-playing",onPlaying) +#player.connect("playback-finished",onStop) +#player.connect("playback-stopped",onStop) -player.load("/opt/mediacore/mediaserver2/snnw_E_138.mp3") +#print "Attempting to play {0}".format(sys.argv[1]) +player.play(sys.argv[1]) diff --git a/test_client.py b/test_client.py new file mode 100755 index 0000000..d6d17a5 --- /dev/null +++ b/test_client.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +from mediaserver.client import MediaClient + +#!/usr/bin/env python +import sys, os, glob +import signal + +import gi +from gi.repository import GLib +from gi.repository import GObject + + +#import Resources + +class TestController: + def __init__(self): + #initialize loop + self.mainloop = GLib.MainLoop() # main loop + #initialize sensing + self.player = MediaClient() + self.player.OnPlaying += self.onPlayerPlaying + self.player.OnStopped += self.onPlayerStopped + self.player.OnPaused += self.onPlayerPaused + self.player.OnFinished += self.onPlayerFinished + self.player.OnFinishing += self.onPlayerFinishing + self.player.OnLoading += self.onPlayerLoading + self.player.OnReady += self.onPlayerReady + + GObject.timeout_add(10, self.tick) + + def tick(self): + self.player.QuickLoop("/opt/src/robotvideo/ogen open.mp4") + #GObject.timeout_add(3000, self.reboot) + pass + + def onStarting(self): + pass + + def onStopped(self): + pass + + def onPlayerPlaying(self): + pass + + def onPlayerStopped(self): + pass + + def onPlayerPaused(self): + pass + + def onPlayerFinished(self): + pass + + def onPlayerFinishing(self): + pass + + def onPlayerLoading(self, uri): + pass + + def onPlayerReady(self,url,tags): + print('Trigger Ready') + pass + + def Start(self): + print("Starting Mainloop") + # initia + self.onStarting() + try: + self.mainloop.run() + except KeyboardInterrupt: + self.Stop() + + def Stop(self): + print("Stopping controller") + self.player.Stop() + self.mainloop.quit() + self.onStopped() + + +# Sigterm Callback function +def signalSIGTERM(self,signum): + controller.Stop() + +signal.signal(signal.SIGTERM, signalSIGTERM) + +controller = TestController() +controller.Start(); + + diff --git a/usr-share-mediaserver/background-image.png b/usr-share-mediaserver/background-image.png old mode 100644 new mode 100755 diff --git a/usr-share-mediaserver/background-image.svg b/usr-share-mediaserver/background-image.svg old mode 100644 new mode 100755