! -e src/plugins/media-export/librygel_media_export_la_vala.stamp -o \
! -e src/plugins/mediathek/librygel_mediathek_la_vala.stamp -o \
! -e src/plugins/gst-launch/librygel_gst_launch_la_vala.stamp -o \
+ ! -e src/plugins/mpris/librygel_mpris_la_vala.stamp -o \
! -e src/plugins/external/librygel_external_la_vala.stamp ; then
enable_vala=yes
fi
[ --enable-external-plugin build External plugin],,
enable_external_plugin=yes)
+# Build MPRIS2 plugin
+AC_ARG_ENABLE(mpris-plugin,
+ [ --enable-mpris-plugin build MPRIS2 plugin],,
+ enable_mpris_plugin=yes)
+
# Build GstRenderer plugin
AC_ARG_ENABLE(playbin-plugin,
[ --enable-playbin_plugin build Gstreamer Playbin plugin],,
AM_CONDITIONAL([BUILD_MEDIATHEK_PLUGIN], [test "x$enable_mediathek_plugin" = "xyes"])
AM_CONDITIONAL([BUILD_MEDIA_EXPORT_PLUGIN], [test "x$enable_media_export_plugin" = "xyes"])
AM_CONDITIONAL([BUILD_EXTERNAL_PLUGIN], [test "x$enable_external_plugin" = "xyes"])
+AM_CONDITIONAL([BUILD_MPRIS_PLUGIN], [test "x$enable_mpris_plugin" = "xyes"])
AM_CONDITIONAL([BUILD_PLAYBIN_PLUGIN], [test "x$enable_playbin_plugin" = "xyes"])
AM_CONDITIONAL([BUILD_GST_LAUNCH_PLUGIN], [test "x$enable_gst_launch_plugin" = "xyes"])
src/plugins/Makefile
src/plugins/media-export/Makefile
src/plugins/external/Makefile
+src/plugins/mpris/Makefile
src/plugins/gst-launch/Makefile
src/plugins/mediathek/Makefile
src/plugins/tracker/Makefile
mediathek: ${enable_mediathek_plugin}
media-export ${enable_media_export_plugin}
external: ${enable_external_plugin}
+ MPRIS2: ${enable_mpris_plugin}
gst-launch: ${enable_gst_launch_plugin}
playbin: ${enable_playbin_plugin}
"
EXTERNAL_PLUGIN = external
endif
+if BUILD_MPRIS_PLUGIN
+MPRIS_PLUGIN = mpris
+endif
+
if BUILD_GST_LAUNCH_PLUGIN
GST_LAUNCH_PLUGIN = gst-launch
endif
$(MEDIATHEK_PLUGIN) \
$(MEDIA_EXPORT_PLUGIN) \
$(EXTERNAL_PLUGIN) \
+ $(MPRIS_PLUGIN) \
$(GST_LAUNCH_PLUGIN) \
$(PLAYBIN)
--- /dev/null
+if UNINSTALLED
+shareddir = $(abs_top_builddir)/data
+else
+shareddir = $(datadir)/rygel
+endif
+
+plugindir = $(libdir)/rygel-1.0
+
+plugin_LTLIBRARIES = librygel-mpris.la
+
+AM_CFLAGS = $(LIBGUPNP_CFLAGS) \
+ $(LIBGUPNP_AV_CFLAGS) \
+ $(GEE_CFLAGS) \
+ $(LIBGSTREAMER_CFLAGS) \
+ $(LIBDBUS_GLIB_CFLAGS) \
+ $(UUID_CFLAGS) \
+ -I$(top_srcdir)/src/rygel -DDATA_DIR='"$(shareddir)"' \
+ -include config.h
+
+librygel_mpris_la_SOURCES = rygel-mpris-player.vala \
+ rygel-mpris-plugin.vala \
+ rygel-mpris-plugin-factory.vala \
+ rygel-mpris-interfaces.vala
+
+librygel_mpris_la_VALAFLAGS = --vapidir=$(top_srcdir)/src/rygel \
+ --vapidir=$(srcdir) \
+ --pkg rygel-1.0 --pkg rygel-build-config \
+ --pkg gupnp-1.0 --pkg gupnp-av-1.0 \
+ --pkg gee-1.0 --pkg gstreamer-0.10 -g
+
+librygel_mpris_la_LIBADD = $(LIBGUPNP_LIBS) \
+ $(LIBGUPNP_AV_LIBS) \
+ $(LIBDBUS_GLIB_LIBS) \
+ $(GEE_LIBS) \
+ $(LIBGSTREAMER_LIBS) \
+ $(UUID_LIBS)
+librygel_mpris_la_LDFLAGS = -shared -fPIC -module -avoid-version
+
+MAINTAINERCLEANFILES = Makefile.in
--- /dev/null
+/*
+ * Copyright (C) 2009,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ * <zeeshan.ali@nokia.com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using DBus;
+
+[DBus (name = "org.mpris.MediaPlayer2")]
+public interface Rygel.MPRIS.MediaPlayerProxy : DBus.Object {
+ public const string IFACE = "org.mpris.MediaPlayer2";
+
+ public abstract string identity { owned get; }
+ public abstract string[] supported_uri_schemes { owned get; }
+ public abstract string[] supported_mime_types { owned get; }
+}
+
+[DBus (name = "org.mpris.MediaPlayer2.Player")]
+public interface Rygel.MPRIS.MediaPlayer.PlayerProxy : DBus.Object {
+ public const string IFACE = "org.mpris.MediaPlayer2.Player";
+
+ public abstract string playback_status { owned get; }
+ public abstract double volume { get; set; }
+ public abstract int64 position { get; }
+ public abstract HashTable<string,Value?> metadata { owned get; }
+
+ public abstract void pause () throws DBus.Error;
+ public abstract void play_pause () throws DBus.Error;
+ public abstract void stop () throws DBus.Error;
+ public abstract void play () throws DBus.Error;
+ public abstract void seek (int64 offset) throws DBus.Error;
+ public abstract void open_uri (string uri) throws DBus.Error;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Jorn Baayen <jorn@openedhand.com>
+ * Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ * <zeeshan.ali@nokia.com>
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Rygel.MPRIS;
+using Rygel.MPRIS.MediaPlayer;
+using FreeDesktop;
+
+public class Rygel.MPRIS.Player : GLib.Object, Rygel.MediaPlayer {
+ private string[] protocols;
+ private string[] mime_types;
+
+ private PlayerProxy actual_player;
+ private Properties properties;
+
+ public string playback_state {
+ owned get {
+ return this.mpris_to_upnp_state (actual_player.playback_status);
+ }
+
+ set {
+ debug (_("Changing playback state to %s.."), value);
+
+ /* FIXME: Do something about errors below */
+ switch (value) {
+ case "STOPPED":
+ try {
+ this.actual_player.stop ();
+ } catch (Error error) {}
+
+ break;
+ case "PAUSED_PLAYBACK":
+ try {
+ this.actual_player.pause ();
+ } catch (Error error) {}
+
+ break;
+ case "PLAYING":
+ try {
+ this.actual_player.play ();
+ } catch (Error error) {}
+
+ break;
+ default:
+ assert_not_reached ();
+ }
+ }
+ }
+
+ public string? uri {
+ owned get {
+ var val = this.actual_player.metadata.lookup ("xesam:url");
+
+ if (val != null) {
+ return (string) val;
+ } else {
+ return null;
+ }
+ }
+
+ set {
+ try {
+ this.actual_player.open_uri (value);
+ } catch (Error error) {}
+ }
+ }
+
+ public double volume {
+ get {
+ return this.actual_player.volume;
+ }
+
+ set {
+ this.actual_player.volume = value;
+ }
+ }
+
+ public int64 duration {
+ get {
+ var val = this.actual_player.metadata.lookup ("mpris:length");
+ int64 dur = 0;
+
+ if (val != null) {
+ dur = (int64) val * 1000;
+ }
+
+ return dur;
+ }
+ }
+
+ public int64 position {
+ get {
+ return this.actual_player.position * 1000;
+ }
+ }
+
+ public Player (PlayerProxy actual_player,
+ Properties properties,
+ string[] mime_types,
+ string[] protocols) {
+ this.actual_player = actual_player;
+ this.properties = properties;
+ this.mime_types = mime_types;
+ this.protocols = protocols;
+
+ this.properties.properties_changed.connect (this.on_properties_changed);
+ }
+
+ public bool seek (Gst.ClockTime time) {
+ var ret = false;
+
+ try {
+ this.actual_player.seek (time / 1000);
+ ret = true;
+ } catch (Error error) {}
+
+ return ret;
+ }
+
+ public string[] get_protocols () {
+ return this.protocols;
+ }
+
+ public string[] get_mime_types () {
+ return this.mime_types;
+ }
+
+ private string mpris_to_upnp_state (string state) {
+ switch (state) {
+ case "Stopped":
+ return "STOPPED";
+ case "Paused":
+ return "PAUSED_PLAYBACK";
+ case "Playing":
+ return "PLAYING";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ private void on_properties_changed (string iface,
+ HashTable<string,Value?> changed,
+ string[] invalidated) {
+ if (changed.lookup ("PlaybackStatus") != null) {
+ this.notify_property ("playback-state");
+ }
+
+ if (changed.lookup ("Volume") != null) {
+ this.notify_property ("volume");
+ }
+
+ if (changed.lookup ("Metadata") != null) {
+ var metadata = this.actual_player.metadata;
+
+ if (metadata.lookup ("xesam:url") != null) {
+ this.notify_property ("uri");
+ }
+
+ if (metadata.lookup ("mpris:length") != null) {
+ this.notify_property ("duration");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
+ * Copyright (C) 2009,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ * <zeeshan.ali@nokia.com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Rygel;
+using Gee;
+using FreeDesktop;
+
+private MPRIS.PluginFactory plugin_factory;
+
+public void module_init (PluginLoader loader) {
+ try {
+ plugin_factory = new MPRIS.PluginFactory (loader);
+ } catch (DBus.Error error) {
+ critical ("Failed to fetch list of MPRIS services: %s\n",
+ error.message);
+ }
+}
+
+public class Rygel.MPRIS.PluginFactory {
+ private const string DBUS_SERVICE = "org.freedesktop.DBus";
+ private const string DBUS_OBJECT = "/org/freedesktop/DBus";
+
+ private const string SERVICE_PREFIX = "org.mpris.MediaPlayer2.";
+ private const string MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2";
+
+ DBusObject dbus_obj;
+ DBus.Connection connection;
+ PluginLoader loader;
+
+ public PluginFactory (PluginLoader loader) throws DBus.Error {
+ this.connection = DBus.Bus.get (DBus.BusType.SESSION);
+
+ this.dbus_obj = this.connection.get_object (DBUS_SERVICE, DBUS_OBJECT)
+ as DBusObject;
+ this.loader = loader;
+
+ this.load_plugins.begin ();
+ }
+
+ private async void load_plugins () throws DBus.Error {
+ var services = yield this.dbus_obj.list_names ();
+
+ foreach (var service in services) {
+ if (service.has_prefix (SERVICE_PREFIX) &&
+ this.loader.get_plugin_by_name (service) == null) {
+ yield this.load_plugin (service);
+ }
+ }
+
+ yield this.load_activatable_plugins ();
+ }
+
+ private async void load_activatable_plugins () throws DBus.Error {
+ var services = yield this.dbus_obj.list_activatable_names ();
+
+ foreach (var service in services) {
+ if (service.has_prefix (SERVICE_PREFIX) &&
+ this.loader.get_plugin_by_name (service) == null) {
+ yield this.load_plugin (service);
+ }
+ }
+
+ this.dbus_obj.name_owner_changed.connect (this.name_owner_changed);
+ }
+
+ private void name_owner_changed (DBusObject dbus_obj,
+ string name,
+ string old_owner,
+ string new_owner) {
+ var plugin = this.loader.get_plugin_by_name (name);
+
+ if (plugin != null) {
+ if (old_owner != "" && new_owner == "") {
+ debug ("Service '%s' going down, marking it as unavailable",
+ name);
+ plugin.available = false;
+ } else if (old_owner == "" && new_owner != "") {
+ debug ("Service '%s' up again, marking it as available", name);
+ plugin.available = true;
+ }
+ } else if (name.has_prefix (SERVICE_PREFIX)) {
+ // Ah, new plugin available, lets use it
+ this.load_plugin.begin (name);
+ }
+ }
+
+ private async void load_plugin (string service_name) {
+ // Create proxy to MediaObject iface to get the display name through
+ var props = this.connection.get_object (service_name, MEDIA_PLAYER_PATH)
+ as Properties;
+
+ HashTable<string,Value?> props_hash;
+
+ try {
+ props_hash = yield props.get_all (MediaPlayerProxy.IFACE);
+ } catch (DBus.Error err) {
+ warning ("Failed to fetch properties of plugin %s: %s.",
+ service_name,
+ err.message);
+
+ return;
+ }
+
+ var title = (string) props_hash.lookup ("Identity");
+ if (title == null) {
+ title = service_name;
+ }
+
+ var mime_types = (string[]) props_hash.lookup ("SupportedMimeTypes");
+ var schemes = (string[]) props_hash.lookup ("SupportedUriSchemes");
+
+ var plugin = new MPRIS.Plugin (service_name,
+ title,
+ mime_types,
+ schemes);
+
+ this.loader.add_plugin (plugin);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ * <zeeshan.ali@nokia.com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using DBus;
+using Rygel.MPRIS;
+using Rygel.MPRIS.MediaPlayer;
+
+public class Rygel.MPRIS.Plugin : Rygel.MediaRendererPlugin {
+ private const string MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2";
+
+ private PlayerProxy actual_player;
+ private FreeDesktop.Properties properties;
+
+ private string[] mime_types;
+ private string[] protocols;
+
+ public Plugin (string service_name,
+ string title,
+ string[] mime_types,
+ string[] schemes) {
+ base (service_name, title);
+
+ this.mime_types = mime_types;
+ this.protocols = this.schemes_to_protocols (schemes);
+
+ try {
+ var connection = DBus.Bus.get (DBus.BusType.SESSION);
+
+ // Create proxy to MediaPlayer.Player iface
+ this.actual_player = connection.get_object (service_name,
+ MEDIA_PLAYER_PATH)
+ as PlayerProxy;
+ // Create proxy to FreeDesktop.Properties iface
+ this.properties = connection.get_object (service_name,
+ MEDIA_PLAYER_PATH)
+ as FreeDesktop.Properties;
+ } catch (GLib.Error err) {
+ critical ("Failed to connect to session bus: %s", err.message);
+ }
+ }
+
+ public override Rygel.MediaPlayer? get_player () {
+ return new MPRIS.Player (this.actual_player,
+ this.properties,
+ this.mime_types,
+ this.protocols);
+ }
+
+ private string[] schemes_to_protocols (string[] schemes) {
+ var protocols = new string[schemes.length];
+
+ for (var i = 0; i < schemes.length; i++) {
+ protocols[i] = this.scheme_to_protocol (schemes[i]);
+ }
+
+ return protocols;
+ }
+
+ private string scheme_to_protocol (string scheme) {
+ switch (scheme) {
+ case "http":
+ return "http-get";
+ case "file":
+ return "internal";
+ default:
+ return scheme;
+ }
+ }
+}
+