[Merge] lp:~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator into lp:media-hub

Jim Hodapp jim.hodapp at canonical.com
Wed Sep 10 20:48:18 UTC 2014


Review: Needs Fixing code

Just a few simple changes.

Diff comments:

> === modified file 'CMakeLists.txt'
> --- CMakeLists.txt	2014-06-27 08:06:14 +0000
> +++ CMakeLists.txt	2014-09-10 07:05:35 +0000
> @@ -2,7 +2,7 @@
>  
>  project(ubuntu-media-hub)
>  
> -set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 1)
> +set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 2)
>  set(UBUNTU_MEDIA_HUB_VERSION_MINOR 0)
>  set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0)
>  
> 
> === modified file 'debian/changelog'
> --- debian/changelog	2014-09-08 14:26:38 +0000
> +++ debian/changelog	2014-09-10 07:05:35 +0000
> @@ -1,3 +1,9 @@
> +media-hub (2.0.0) UNRELEASED; urgency=medium
> +
> +  * Bump major version to account for signature changes in public interface. 
> +
> + -- Thomas Voß <thomas.voss at canonical.com>  Thu, 28 Aug 2014 09:10:33 +0200
> +
>  media-hub (1.0.0+14.10.20140908-0ubuntu1) utopic; urgency=low
>  
>    [ Jim Hodapp ]
> 
> === modified file 'debian/control'
> --- debian/control	2014-06-27 08:06:14 +0000
> +++ debian/control	2014-09-10 07:05:35 +0000
> @@ -39,8 +39,8 @@
>  Architecture: any
>  Multi-Arch: same
>  Pre-Depends: dpkg (>= 1.15.6~)
> -Depends: libmedia-hub-common1 (= ${binary:Version}),
> -         libmedia-hub-client1 (= ${binary:Version}),
> +Depends: libmedia-hub-common2 (= ${binary:Version}),
> +         libmedia-hub-client2 (= ${binary:Version}),
>           ${misc:Depends},
>           libproperties-cpp-dev,
>  Suggests: libmedia-hub-doc
> @@ -62,7 +62,7 @@
>   .
>   This package contains the runtime.
>  
> -Package: libmedia-hub-common1
> +Package: libmedia-hub-common2
>  Architecture: any
>  Multi-Arch: same
>  Pre-Depends: dpkg (>= 1.15.6~)
> @@ -74,7 +74,7 @@
>   .
>   This package contains the common libraries.
>  
> -Package: libmedia-hub-client1
> +Package: libmedia-hub-client2
>  Architecture: any
>  Multi-Arch: same
>  Pre-Depends: dpkg (>= 1.15.6~)
> 
> === renamed file 'debian/libmedia-hub-client1.install' => 'debian/libmedia-hub-client2.install'
> === renamed file 'debian/libmedia-hub-common1.install' => 'debian/libmedia-hub-common2.install'
> === renamed file 'debian/libmedia-hub-common1.symbols' => 'debian/libmedia-hub-common2.symbols'
> --- debian/libmedia-hub-common1.symbols	2014-07-12 06:55:19 +0000
> +++ debian/libmedia-hub-common2.symbols	2014-09-10 07:05:35 +0000
> @@ -1,19 +1,2 @@
> -libmedia-hub-common.so.1 libmedia-hub-common1 #MINVER#
> - (c++)"the_session_bus()@Base" 0.0.1
> - (c++)"std::shared_ptr<core::dbus::Bus>::~shared_ptr()@Base" 0.0.1
> - (c++)"std::shared_ptr<core::dbus::Executor>::~shared_ptr()@Base" 0.0.1
> - (c++)"std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.0.1
> - (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.0.1
> - (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.0.1
> - (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.0.1
> - (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.0.1
> - (c++)"typeinfo for std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"typeinfo for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"typeinfo for std::_Sp_make_shared_tag at Base" 0.0.1
> - (c++)"typeinfo for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"typeinfo name for std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"typeinfo name for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"typeinfo name for std::_Sp_make_shared_tag at Base" 0.0.1
> - (c++)"typeinfo name for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"vtable for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> - (c++)"vtable for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
> +libmedia-hub-common.so.2 libmedia-hub-common2 #MINVER#
> + (c++)"core::ubuntu::media::the_session_bus()@Base" 0replaceme
> 
> === modified file 'include/core/media/player.h'
> --- include/core/media/player.h	2014-08-29 14:03:42 +0000
> +++ include/core/media/player.h	2014-09-10 07:05:35 +0000
> @@ -124,8 +124,8 @@
>      virtual const core::Property<Volume>& volume() const = 0;
>      virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0;
>      virtual const core::Property<PlaybackRate>& maximum_playback_rate() const = 0;
> -    virtual const core::Property<uint64_t>& position() const = 0;
> -    virtual const core::Property<uint64_t>& duration() const = 0;
> +    virtual const core::Property<int64_t>& position() const = 0;
> +    virtual const core::Property<int64_t>& duration() const = 0;
>      virtual const core::Property<AudioStreamRole>& audio_stream_role() const = 0;
>  
>      virtual core::Property<LoopStatus>& loop_status() = 0;
> @@ -134,7 +134,7 @@
>      virtual core::Property<Volume>& volume() = 0;
>      virtual core::Property<AudioStreamRole>& audio_stream_role() = 0;
>  
> -    virtual const core::Signal<uint64_t>& seeked_to() const = 0;
> +    virtual const core::Signal<int64_t>& seeked_to() const = 0;
>      virtual const core::Signal<void>& end_of_stream() const = 0;
>      virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;
>    protected:
> 
> === modified file 'src/core/media/CMakeLists.txt'
> --- src/core/media/CMakeLists.txt	2014-09-10 07:05:35 +0000
> +++ src/core/media/CMakeLists.txt	2014-09-10 07:05:35 +0000
> @@ -1,6 +1,10 @@
>  pkg_check_modules(PC_GSTREAMER_1_0 REQUIRED gstreamer-1.0)
>  include_directories(${PC_GSTREAMER_1_0_INCLUDE_DIRS} ${HYBRIS_MEDIA_CFLAGS})
>  
> +# We compile with all symbols visible by default. For the shipping library, we strip
> +# out all symbols that are not in core::ubuntu::media*
> +set(symbol_map "${CMAKE_SOURCE_DIR}/symbols.map")
> +
>  file(GLOB MPRIS_HEADERS mpris/ *.h)
>  
>  add_library(
> @@ -22,6 +26,8 @@
>    PROPERTIES
>    VERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}.${UBUNTU_MEDIA_HUB_VERSION_MINOR}.${UBUNTU_MEDIA_HUB_VERSION_PATCH}
>    SOVERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}
> +  LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}"
> +  LINK_DEPENDS ${symbol_map}
>  )
>  
>  add_library(
> @@ -43,6 +49,8 @@
>    PROPERTIES
>    VERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}.${UBUNTU_MEDIA_HUB_VERSION_MINOR}.${UBUNTU_MEDIA_HUB_VERSION_PATCH}
>    SOVERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}
> +  LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}"
> +  LINK_DEPENDS ${symbol_map}
>  )
>  
>  target_link_libraries(
> @@ -66,6 +74,7 @@
>  
>      ${MPRIS_HEADERS}
>  
> +    cover_art_resolver.cpp
>      engine.cpp
>      gstreamer/engine.cpp
>  
> 
> === modified file 'src/core/media/apparmor.h'
> --- src/core/media/apparmor.h	2014-04-22 17:23:43 +0000
> +++ src/core/media/apparmor.h	2014-09-10 07:05:35 +0000
> @@ -16,36 +16,78 @@
>   * Author: Jim Hodapp <jim.hodapp at canonical.com>
>   */
>  
> -#ifndef APPARMOR_H_DBUS_
> -#define APPARMOR_H_DBUS_
> +#ifndef APPARMOR_DBUS_H_
> +#define APPARMOR_DBUS_H_
> +
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/service.h>
>  
>  #include <string>
>  #include <chrono>
>  
> -namespace core
> -{
> -
> -struct Apparmor
> -{
> -    static std::string& name()
> +// TODO(tvoss): This really should live in trust-store, providing a straightforward
> +// way for parties involved in managing trust relationships to query peers' apparmor
> +// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
> +// related bug
> +namespace org
> +{
> +namespace freedesktop
> +{
> +namespace dbus
> +{
> +struct DBus
> +{
> +    static const std::string& name()
>      {
> -        static std::string s = "org.freedesktop.DBus";
> +        static const std::string s = "org.freedesktop.DBus";
>          return s;
>      }
>  
> -    struct getConnectionAppArmorSecurityContext
> +    // Gets the AppArmor confinement string associated with the unique connection name. If
> +    // D-Bus is not performing AppArmor mediation, the
> +    // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> +    DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
> +
> +    struct Stub
>      {
> -        static std::string name()
> -        {
> -            static std::string s = "GetConnectionAppArmorSecurityContext";
> -            return s;
> -        }
> -
> -        static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
> -
> -        typedef Apparmor Interface;
> +        // Creates a new stub instance for the given object to access
> +        // DBus functionality.
> +        Stub(const core::dbus::Object::Ptr& object) : object{object}
> +        {
> +        }
> +
> +        // Creates a new stub instance for the given bus connection
> +        Stub(const core::dbus::Bus::Ptr& bus)
> +            : object
> +              {
> +                  core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
> +                      ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
> +              }
> +        {
> +        }
> +
> +        // Gets the AppArmor confinement string associated with the unique connection name. If
> +        // D-Bus is not performing AppArmor mediation, the
> +        // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> +        //
> +        // Invokes the given handler on completion.
> +        void get_connection_app_armor_security_async(
> +                    const std::string& name,
> +                    std::function<void(const std::string&)> handler)
> +        {
> +            object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
> +                        [handler](const core::dbus::Result<std::string>& result)
> +                        {
> +                            if (not result.is_error()) handler(result.value());
> +                        }, name);
> +        }
> +
> +        core::dbus::Object::Ptr object;
>      };
>  };
>  }
> +}
> +}
>  
> -#endif
> +#endif // APPARMOR_DBUS_H_
> 
> === added file 'src/core/media/cover_art_resolver.cpp'
> --- src/core/media/cover_art_resolver.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/media/cover_art_resolver.cpp	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,27 @@
> +/**
> + * Copyright (C) 2013-2014 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include "cover_art_resolver.h"
> +
> +core::ubuntu::media::CoverArtResolver core::ubuntu::media::always_missing_cover_art_resolver()
> +{
> +    return [](const std::string&, const std::string&, const std::string&)
> +    {
> +        return "file:///usr/share/unity/icons/album_missing.png";
> +    };
> +}
> 
> === added file 'src/core/media/cover_art_resolver.h'
> --- src/core/media/cover_art_resolver.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/cover_art_resolver.h	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,51 @@
> +/**
> + * Copyright (C) 2013-2014 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
> +#define CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
> +
> +#include <functional>
> +#include <string>
> +
> +namespace core
> +{
> +namespace ubuntu
> +{
> +namespace media
> +{
> +// Functional modelling a helper to resolve artist/album names to
> +// cover art.
> +typedef std::function
> +<
> +    std::string             // Returns a URL pointing to the album art
> +    (
> +        const std::string&, // The title of the track
> +        const std::string&, // The name of the album
> +        const std::string&  // The name of the artist
> +    )
> +> CoverArtResolver;
> +
> +// Return a CoverArtResolver that always resolves to
> +// file:///usr/share/unity/icons/album_missing.png
> +CoverArtResolver always_missing_cover_art_resolver();
> +}
> +}
> +}
> +
> +#endif // CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
> +
> 
> === modified file 'src/core/media/engine.cpp'
> --- src/core/media/engine.cpp	2014-08-29 14:03:42 +0000
> +++ src/core/media/engine.cpp	2014-09-10 07:05:35 +0000
> @@ -24,120 +24,6 @@
>  
>  namespace media = core::ubuntu::media;
>  
> -const std::string& media::Engine::Xesam::album()
> -{
> -    static const std::string s{"xesam:album"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::album_artist()
> -{
> -    static const std::string s{"xesam:album_artist"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::artist()
> -{
> -    static const std::string s{"xesam:artist"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::as_text()
> -{
> -    static const std::string s{"xesam:as_text"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::audio_bpm()
> -{
> -    static const std::string s{"xesam:audio_bmp"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::auto_rating()
> -{
> -    static const std::string s{"xesam:autoRating"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::comment()
> -{
> -    static const std::string s{"xesam:comment"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::composer()
> -{
> -    static const std::string s{"xesam:composer"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::content_created()
> -{
> -    static const std::string s{"xesam:contentCreated"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::disc_number()
> -{
> -    static const std::string s{"xesam:discNumber"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::first_used()
> -{
> -    static const std::string s{"xesam:firstUsed"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::genre()
> -{
> -    static const std::string s{"xesam:genre"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::last_used()
> -{
> -    static const std::string s{"xesam:lastUsed"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::lyricist()
> -{
> -    static const std::string s{"xesam:lyricist"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::title()
> -{
> -    static const std::string s{"xesam:title"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::track_number()
> -{
> -    static const std::string s{"xesam:trackNumber"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::url()
> -{
> -    static const std::string s{"xesam:url"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::use_count()
> -{
> -    static const std::string s{"xesam:useCount"};
> -    return s;
> -}
> -
> -const std::string& media::Engine::Xesam::user_rating()
> -{
> -    static const std::string s{"xesam:userRating"};
> -    return s;
> -}
> -
>  double media::Engine::Volume::min()
>  {
>      return 0.;
> 
> === modified file 'src/core/media/engine.h'
> --- src/core/media/engine.h	2014-08-29 14:03:42 +0000
> +++ src/core/media/engine.h	2014-09-10 07:05:35 +0000
> @@ -45,29 +45,6 @@
>          stopped
>      };
>  
> -    struct Xesam
> -    {
> -        static const std::string& album();
> -        static const std::string& album_artist();
> -        static const std::string& artist();
> -        static const std::string& as_text();
> -        static const std::string& audio_bpm();
> -        static const std::string& auto_rating();
> -        static const std::string& comment();
> -        static const std::string& composer();
> -        static const std::string& content_created();
> -        static const std::string& disc_number();
> -        static const std::string& first_used();
> -        static const std::string& genre();
> -        static const std::string& last_used();
> -        static const std::string& lyricist();
> -        static const std::string& title();
> -        static const std::string& track_number();
> -        static const std::string& url();
> -        static const std::string& use_count();
> -        static const std::string& user_rating();
> -    };
> -
>      struct Volume
>      {
>          static double min();
> 
> === modified file 'src/core/media/gstreamer/engine.cpp'
> --- src/core/media/gstreamer/engine.cpp	2014-08-29 21:45:58 +0000
> +++ src/core/media/gstreamer/engine.cpp	2014-09-10 07:05:35 +0000
> @@ -58,105 +58,7 @@
>      void on_tag_available(const gstreamer::Bus::Message::Detail::Tag& tag)
>      {
>          media::Track::MetaData md;
> -
> -        gst_tag_list_foreach(
> -                    tag.tag_list,
> -                    [](const GstTagList *list,
> -                    const gchar* tag,
> -                    gpointer user_data)
> -        {
> -            (void) list;
> -
> -            static const std::map<std::string, std::string> gstreamer_to_mpris_tag_lut =
> -            {
> -                {GST_TAG_ALBUM, media::Engine::Xesam::album()},
> -                {GST_TAG_ALBUM_ARTIST, media::Engine::Xesam::album_artist()},
> -                {GST_TAG_ARTIST, media::Engine::Xesam::artist()},
> -                {GST_TAG_LYRICS, media::Engine::Xesam::as_text()},
> -                {GST_TAG_COMMENT, media::Engine::Xesam::comment()},
> -                {GST_TAG_COMPOSER, media::Engine::Xesam::composer()},
> -                {GST_TAG_DATE, media::Engine::Xesam::content_created()},
> -                {GST_TAG_ALBUM_VOLUME_NUMBER, media::Engine::Xesam::disc_number()},
> -                {GST_TAG_GENRE, media::Engine::Xesam::genre()},
> -                {GST_TAG_TITLE, media::Engine::Xesam::title()},
> -                {GST_TAG_TRACK_NUMBER, media::Engine::Xesam::track_number()},
> -                {GST_TAG_USER_RATING, media::Engine::Xesam::user_rating()}
> -            };
> -
> -            auto md = static_cast<media::Track::MetaData*>(user_data);
> -            std::stringstream ss;
> -
> -            switch(gst_tag_get_type(tag))
> -            {
> -            case G_TYPE_BOOLEAN:
> -            {
> -                gboolean value;
> -                if (gst_tag_list_get_boolean(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_INT:
> -            {
> -                gint value;
> -                if (gst_tag_list_get_int(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_UINT:
> -            {
> -                guint value;
> -                if (gst_tag_list_get_uint(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_INT64:
> -            {
> -                gint64 value;
> -                if (gst_tag_list_get_int64(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_UINT64:
> -            {
> -                guint64 value;
> -                if (gst_tag_list_get_uint64(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_FLOAT:
> -            {
> -                gfloat value;
> -                if (gst_tag_list_get_float(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_DOUBLE:
> -            {
> -                double value;
> -                if (gst_tag_list_get_double(list, tag, &value))
> -                    ss << value;
> -                break;
> -            }
> -            case G_TYPE_STRING:
> -            {
> -                gchar* value;
> -                if (gst_tag_list_get_string(list, tag, &value))
> -                {
> -                    ss << value;
> -                    g_free(value);
> -                }
> -                break;
> -            }
> -            default:
> -                break;
> -            }
> -
> -            (*md).set(
> -                        (gstreamer_to_mpris_tag_lut.count(tag) > 0 ? gstreamer_to_mpris_tag_lut.at(tag) : tag),
> -                        ss.str());
> -        },
> -        &md);
> -
> +        gstreamer::MetaDataExtractor::on_tag_available(tag, md);
>          track_meta_data.set(std::make_tuple(playbin.uri(), md));
>      }
>  
> @@ -318,6 +220,10 @@
>  
>  bool gstreamer::Engine::stop()
>  {
> +    // No need to wait, and we can immediately return.
> +    if (d->state == media::Engine::State::stopped)
> +        return true;
> +
>      auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
>  
>      if (result)
> 
> === modified file 'src/core/media/gstreamer/meta_data_extractor.h'
> --- src/core/media/gstreamer/meta_data_extractor.h	2014-02-12 15:53:57 +0000
> +++ src/core/media/gstreamer/meta_data_extractor.h	2014-09-10 07:05:35 +0000
> @@ -20,6 +20,7 @@
>  #define GSTREAMER_META_DATA_EXTRACTOR_H_
>  
>  #include "../engine.h"
> +#include "../xesam.h"
>  
>  #include "bus.h"
>  
> @@ -33,78 +34,25 @@
>  class MetaDataExtractor : public core::ubuntu::media::Engine::MetaDataExtractor
>  {
>  public:
> -    MetaDataExtractor()
> -        : pipe(gst_pipeline_new("meta_data_extractor_pipeline")),
> -          decoder(gst_element_factory_make ("uridecodebin", NULL)),
> -          bus(GST_ELEMENT_BUS(pipe))
> -    {
> -        gst_bin_add(GST_BIN(pipe), decoder);
> -
> -        auto sink = gst_element_factory_make ("fakesink", NULL);
> -        gst_bin_add (GST_BIN (pipe), sink);
> -
> -        g_signal_connect (decoder, "pad-added", G_CALLBACK (on_new_pad), sink);
> -    }
> -
> -    ~MetaDataExtractor()
> -    {
> -        gst_element_set_state(pipe, GST_STATE_NULL);
> -        // gst_object_unref(pipe);
> -    }
> -
> -    core::ubuntu::media::Track::MetaData meta_data_for_track_with_uri(const core::ubuntu::media::Track::UriType& uri)
> -    {
> -        if (!gst_uri_is_valid(uri.c_str()))
> -            throw std::runtime_error("Invalid uri");
> -
> -        core::ubuntu::media::Track::MetaData meta_data;
> -        std::promise<core::ubuntu::media::Track::MetaData> promise;
> -        std::future<core::ubuntu::media::Track::MetaData> future{promise.get_future()};
> -
> -        core::ScopedConnection on_new_message_connection
> +    static const std::map<std::string, std::string>& gstreamer_to_mpris_tag_lut()
> +    {
> +        static const std::map<std::string, std::string> lut
>          {
> -            bus.on_new_message.connect(
> -                    [&](const gstreamer::Bus::Message& msg)
> -                    {
> -                        std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
> -                        if (msg.type == GST_MESSAGE_TAG)
> -                        {
> -                            MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
> -                        } else if (msg.type == GST_MESSAGE_ASYNC_DONE)
> -                        {
> -                            promise.set_value(meta_data);
> -                        }
> -                    })
> +            {GST_TAG_ALBUM, std::string{xesam::Album::name}},
> +            {GST_TAG_ALBUM_ARTIST, std::string{xesam::AlbumArtist::name}},
> +            {GST_TAG_ARTIST, std::string{xesam::Artist::name}},
> +            {GST_TAG_LYRICS, std::string{xesam::AsText::name}},
> +            {GST_TAG_COMMENT, std::string{xesam::Comment::name}},
> +            {GST_TAG_COMPOSER, std::string{xesam::Composer::name}},
> +            {GST_TAG_DATE, std::string{xesam::ContentCreated::name}},
> +            {GST_TAG_ALBUM_VOLUME_NUMBER, std::string{xesam::DiscNumber::name}},
> +            {GST_TAG_GENRE, std::string{xesam::Genre::name}},
> +            {GST_TAG_TITLE, std::string{xesam::Title::name}},
> +            {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}},
> +            {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}}
>          };
>  
> -        g_object_set(decoder, "uri", uri.c_str(), NULL);
> -        gst_element_set_state(pipe, GST_STATE_PAUSED);
> -
> -        if (std::future_status::ready != future.wait_for(std::chrono::seconds(2)))
> -        {
> -            gst_element_set_state(pipe, GST_STATE_NULL);
> -            throw std::runtime_error("Problem extracting meta data for track");
> -        } else
> -        {
> -            gst_element_set_state(pipe, GST_STATE_NULL);
> -        }
> -
> -        return future.get();
> -    }
> -
> -private:
> -    static void on_new_pad(GstElement*, GstPad* pad, GstElement* fakesink)
> -    {
> -        GstPad *sinkpad;
> -
> -        sinkpad = gst_element_get_static_pad (fakesink, "sink");
> -
> -        if (!gst_pad_is_linked (sinkpad)) {
> -            if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
> -                g_error ("Failed to link pads!");
> -        }
> -
> -        gst_object_unref (sinkpad);
> +        return lut;
>      }
>  
>      static void on_tag_available(
> @@ -121,22 +69,6 @@
>          {
>              (void) list;
>  
> -            static const std::map<std::string, std::string> gstreamer_to_mpris_tag_lut =
> -            {
> -                {GST_TAG_ALBUM, media::Engine::Xesam::album()},
> -                {GST_TAG_ALBUM_ARTIST, media::Engine::Xesam::album_artist()},
> -                {GST_TAG_ARTIST, media::Engine::Xesam::artist()},
> -                {GST_TAG_LYRICS, media::Engine::Xesam::as_text()},
> -                {GST_TAG_COMMENT, media::Engine::Xesam::comment()},
> -                {GST_TAG_COMPOSER, media::Engine::Xesam::composer()},
> -                {GST_TAG_DATE, media::Engine::Xesam::content_created()},
> -                {GST_TAG_ALBUM_VOLUME_NUMBER, media::Engine::Xesam::disc_number()},
> -                {GST_TAG_GENRE, media::Engine::Xesam::genre()},
> -                {GST_TAG_TITLE, media::Engine::Xesam::title()},
> -                {GST_TAG_TRACK_NUMBER, media::Engine::Xesam::track_number()},
> -                {GST_TAG_USER_RATING, media::Engine::Xesam::user_rating()}
> -            };
> -
>              auto md = static_cast<media::Track::MetaData*>(user_data);
>              std::stringstream ss;
>  
> @@ -206,12 +138,86 @@
>              }
>  
>              (*md).set(
> -                        (gstreamer_to_mpris_tag_lut.count(tag) > 0 ? gstreamer_to_mpris_tag_lut.at(tag) : tag),
> +                        (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag),
>                          ss.str());
>          },
>          &md);
>      }
>  
> +    MetaDataExtractor()
> +        : pipe(gst_pipeline_new("meta_data_extractor_pipeline")),
> +          decoder(gst_element_factory_make ("uridecodebin", NULL)),
> +          bus(GST_ELEMENT_BUS(pipe))
> +    {
> +        gst_bin_add(GST_BIN(pipe), decoder);
> +
> +        auto sink = gst_element_factory_make ("fakesink", NULL);
> +        gst_bin_add (GST_BIN (pipe), sink);
> +
> +        g_signal_connect (decoder, "pad-added", G_CALLBACK (on_new_pad), sink);
> +    }
> +
> +    ~MetaDataExtractor()
> +    {
> +        gst_element_set_state(pipe, GST_STATE_NULL);
> +        // gst_object_unref(pipe);
> +    }
> +
> +    core::ubuntu::media::Track::MetaData meta_data_for_track_with_uri(const core::ubuntu::media::Track::UriType& uri)
> +    {
> +        if (!gst_uri_is_valid(uri.c_str()))
> +            throw std::runtime_error("Invalid uri");
> +
> +        core::ubuntu::media::Track::MetaData meta_data;
> +        std::promise<core::ubuntu::media::Track::MetaData> promise;
> +        std::future<core::ubuntu::media::Track::MetaData> future{promise.get_future()};
> +
> +        core::ScopedConnection on_new_message_connection
> +        {
> +            bus.on_new_message.connect(
> +                    [&](const gstreamer::Bus::Message& msg)
> +                    {
> +                        std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
> +                        if (msg.type == GST_MESSAGE_TAG)
> +                        {
> +                            MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
> +                        } else if (msg.type == GST_MESSAGE_ASYNC_DONE)
> +                        {
> +                            promise.set_value(meta_data);
> +                        }
> +                    })
> +        };
> +
> +        g_object_set(decoder, "uri", uri.c_str(), NULL);
> +        gst_element_set_state(pipe, GST_STATE_PAUSED);
> +
> +        if (std::future_status::ready != future.wait_for(std::chrono::seconds(2)))
> +        {
> +            gst_element_set_state(pipe, GST_STATE_NULL);
> +            throw std::runtime_error("Problem extracting meta data for track");
> +        } else
> +        {
> +            gst_element_set_state(pipe, GST_STATE_NULL);
> +        }
> +
> +        return future.get();
> +    }
> +
> +private:
> +    static void on_new_pad(GstElement*, GstPad* pad, GstElement* fakesink)
> +    {
> +        GstPad *sinkpad;
> +
> +        sinkpad = gst_element_get_static_pad (fakesink, "sink");
> +
> +        if (!gst_pad_is_linked (sinkpad)) {
> +            if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
> +                g_error ("Failed to link pads!");
> +        }
> +
> +        gst_object_unref (sinkpad);
> +    }    
> +
>      GstElement* pipe;
>      GstElement* decoder;
>      Bus bus;
> 
> === modified file 'src/core/media/mpris/media_player2.h'
> --- src/core/media/mpris/media_player2.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/mpris/media_player2.h	2014-09-10 07:05:35 +0000
> @@ -22,6 +22,8 @@
>  #include <core/dbus/macros.h>
>  #include <core/dbus/object.h>
>  #include <core/dbus/property.h>
> +#include <core/dbus/interfaces/properties.h>
> +#include <core/dbus/types/variant.h>
>  
>  #include <string>
>  #include <vector>
> @@ -102,7 +104,20 @@
>              // The bus connection that should be used
>              core::dbus::Bus::Ptr bus;
>              // The dbus object that should implement org.mpris.MediaPlayer2
> -            core::dbus::Object::Ptr object;     
> +            core::dbus::Object::Ptr object;
> +            // Default values assigned to properties on construction
> +            struct Defaults
> +            {
> +                Properties::CanQuit::ValueType can_quit{false};
> +                Properties::Fullscreen::ValueType fullscreen{false};
> +                Properties::CanSetFullscreen::ValueType can_set_fullscreen{false};
> +                Properties::CanRaise::ValueType can_raise{false};
> +                Properties::HasTrackList::ValueType has_track_list{false};
> +                Properties::Identity::ValueType identity{};
> +                Properties::DesktopEntry::ValueType desktop_entry{};
> +                Properties::SupportedUriSchemes::ValueType supported_uri_schemes{};
> +                Properties::SupportedMimeTypes::ValueType supported_mime_types{};
> +            } defaults;
>          };
>  
>          // Creates a new instance, sets up player properties and installs method handlers.
> @@ -119,8 +134,46 @@
>                    configuration.object->get_property<Properties::DesktopEntry>(),
>                    configuration.object->get_property<Properties::SupportedUriSchemes>(),
>                    configuration.object->get_property<Properties::SupportedMimeTypes>()
> +              },
> +              signals
> +              {
> +                  configuration.object->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
>                }
> -        {            
> +        {
> +            // Initialize property values of the media_player instance.
> +            properties.can_quit->set(configuration.defaults.can_quit);
> +            properties.fullscreen->set(configuration.defaults.fullscreen);
> +            properties.can_set_fullscreen->set(configuration.defaults.can_set_fullscreen);
> +            properties.can_raise->set(configuration.defaults.can_raise);
> +            properties.has_track_list->set(configuration.defaults.has_track_list);
> +            properties.desktop_entry->set(configuration.defaults.desktop_entry);
> +            properties.identity->set(configuration.defaults.identity);
> +            properties.supported_mime_types->set(configuration.defaults.supported_mime_types);
> +        }
> +
> +        std::map<std::string, core::dbus::types::Variant> get_all_properties()
> +        {
> +            std::map<std::string, core::dbus::types::Variant> dict;
> +            dict[Properties::CanQuit::name()]
> +                    = core::dbus::types::Variant::encode(properties.can_quit->get());
> +            dict[Properties::Fullscreen::name()]
> +                    = core::dbus::types::Variant::encode(properties.fullscreen->get());
> +            dict[Properties::CanSetFullscreen::name()]
> +                    = core::dbus::types::Variant::encode(properties.can_set_fullscreen->get());
> +            dict[Properties::CanRaise::name()]
> +                    = core::dbus::types::Variant::encode(properties.can_raise->get());
> +            dict[Properties::HasTrackList::name()]
> +                    = core::dbus::types::Variant::encode(properties.has_track_list->get());
> +            dict[Properties::CanSetFullscreen::name()]
> +                    = core::dbus::types::Variant::encode(properties.can_set_fullscreen->get());
> +            dict[Properties::DesktopEntry::name()]
> +                    = core::dbus::types::Variant::encode(properties.desktop_entry->get());
> +            dict[Properties::Identity::name()]
> +                    = core::dbus::types::Variant::encode(properties.identity->get());
> +            dict[Properties::SupportedMimeTypes::name()]
> +                    = core::dbus::types::Variant::encode(properties.supported_mime_types->get());
> +
> +            return dict;
>          }
>  
>          // We just store creation time properties here.
> @@ -139,6 +192,15 @@
>              std::shared_ptr<core::dbus::Property<Properties::SupportedUriSchemes>> supported_uri_schemes;
>              std::shared_ptr<core::dbus::Property<Properties::SupportedMimeTypes>> supported_mime_types;
>          } properties;
> +
> +        struct
> +        {
> +            core::dbus::Signal
> +            <
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged,
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
> +            >::Ptr properties_changed;
> +        } signals;
>      };
>  };
>  }
> 
> === added file 'src/core/media/mpris/metadata.h'
> --- src/core/media/mpris/metadata.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/mpris/metadata.h	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef MPRIS_METADATA_H_
> +#define MPRIS_METADATA_H_
> +
> +#include <core/dbus/types/object_path.h>
> +
> +#include <cstdint>
> +
> +#include <string>
> +
> +namespace mpris
> +{
> +namespace metadata
> +{
> +// D-Bus path: A unique identity for this track within the context of an MPRIS object (eg: tracklist).
> +struct TrackId
> +{
> +    static constexpr const char* name{"mpris:trackid"};
> +    typedef core::dbus::types::ObjectPath ValueType;
> +};
> +// 64-bit integer: The duration of the track in microseconds.
> +struct Length
> +{
> +    static constexpr const char* name{"mpris:length"};
> +    typedef std::int64_t ValueType;
> +};
> +// URI: The location of an image representing the track or album.
> +// Clients should not assume this will continue to exist when the media player
> +// stops giving out the URL.
> +struct ArtUrl
> +{
> +    static constexpr const char* name{"mpris:artUrl"};
> +    typedef std::string ValueType;
> +};
> +}
> +}
> +
> +#endif // MPRIS_METADATA_H_
> 
> === modified file 'src/core/media/mpris/player.h'
> --- src/core/media/mpris/player.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/mpris/player.h	2014-09-10 07:05:35 +0000
> @@ -29,6 +29,7 @@
>  #include <core/dbus/macros.h>
>  #include <core/dbus/object.h>
>  #include <core/dbus/property.h>
> +#include <core/dbus/interfaces/properties.h>
>  #include <core/dbus/types/any.h>
>  #include <core/dbus/types/object_path.h>
>  #include <core/dbus/types/variant.h>
> @@ -55,6 +56,21 @@
>      {
>          LoopStatus() = delete;
>  
> +        static const char* from(core::ubuntu::media::Player::LoopStatus status)
> +        {
> +            switch(status)
> +            {
> +            case core::ubuntu::media::Player::LoopStatus::none:
> +                return LoopStatus::none;
> +            case core::ubuntu::media::Player::LoopStatus::track:
> +                return LoopStatus::track;
> +            case core::ubuntu::media::Player::LoopStatus::playlist:
> +                return LoopStatus::playlist;
> +            }
> +
> +            return nullptr;
> +        }
> +
>          static constexpr const char* none{"None"};
>          static constexpr const char* track{"Track"};
>          static constexpr const char* playlist{"Playlist"};
> @@ -103,7 +119,7 @@
>  
>      struct Signals
>      {
> -        DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::uint64_t)
> +        DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::int64_t)
>          DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void)
>          DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)
>      };
> @@ -119,11 +135,11 @@
>          DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double)
>          DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double)
>          DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool)
> -        DBUS_CPP_READABLE_PROPERTY_DEF(MetaData, Player, Dictionary)
> +        DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary)
>          DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData)
>          DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double)
> -        DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::uint64_t)
> -        DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::uint64_t)
> +        DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t)
> +        DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t)
>          DBUS_CPP_READABLE_PROPERTY_DEF(MinimumRate, Player, double)
>          DBUS_CPP_READABLE_PROPERTY_DEF(MaximumRate, Player, double)
>          DBUS_CPP_READABLE_PROPERTY_DEF(IsVideoSource, Player, bool)
> @@ -139,6 +155,11 @@
>      // Convenience struct to create a skeleton implementation for org.mpris.MediaPlayer2.Player
>      struct Skeleton
>      {
> +        static const std::vector<std::string>& the_empty_list_of_invalided_properties()

you either meant the_empty_list_of_invalid_properties or the_empty_list_of_invalidated_properties

> +        {
> +            static const std::vector<std::string> instance; return instance;
> +        }
> +
>          // Creation time options go here.
>          struct Configuration
>          {
> @@ -146,41 +167,141 @@
>              core::dbus::Bus::Ptr bus;
>              // The dbus object that should implement org.mpris.MediaPlayer2
>              core::dbus::Object::Ptr object;
> +
> +            // Default values for properties
> +            struct Defaults
> +            {
> +                Properties::CanPlay::ValueType can_play{true};
> +                Properties::CanPause::ValueType can_pause{true};
> +                Properties::CanSeek::ValueType can_seek{true};
> +                Properties::CanControl::ValueType can_control{true};
> +                Properties::CanGoNext::ValueType can_go_next{true};
> +                Properties::CanGoPrevious::ValueType can_go_previous{true};
> +                Properties::IsVideoSource::ValueType is_video_source{false};
> +                Properties::IsAudioSource::ValueType is_audio_source{true};
> +                Properties::PlaybackStatus::ValueType playback_status{PlaybackStatus::stopped};
> +                Properties::TypedPlaybackStatus::ValueType typed_playback_status{core::ubuntu::media::Player::PlaybackStatus::null};
> +                Properties::LoopStatus::ValueType loop_status{LoopStatus::none};
> +                Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none};
> +                Properties::PlaybackRate::ValueType playback_rate{1.f};
> +                Properties::Shuffle::ValueType shuffle{false};
> +                Properties::TypedMetaData::ValueType typed_meta_data{};
> +                Properties::Volume::ValueType volume{0.f};
> +                Properties::Position::ValueType position{0};
> +                Properties::Duration::ValueType duration{0};
> +                Properties::MinimumRate::ValueType minimum_rate{1.f};
> +                Properties::MaximumRate::ValueType maximum_rate{1.f};
> +            } defaults;
>          };
>  
>          Skeleton(const Configuration& configuration)
>              : configuration(configuration),
>                properties
>                {
> -                  configuration.object->template get_property<mpris::Player::Properties::CanPlay>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::CanPause>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::CanSeek>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::CanControl>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::CanGoNext>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::CanGoPrevious>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::IsVideoSource>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::IsAudioSource>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::PlaybackStatus>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::TypedPlaybackStatus>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::LoopStatus>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::TypedLoopStatus>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::AudioStreamRole>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::PlaybackRate>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::Shuffle>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::TypedMetaData>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::Volume>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::Position>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::Duration>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::MinimumRate>(),
> -                  configuration.object->template get_property<mpris::Player::Properties::MaximumRate>()
> +                  configuration.object->template get_property<Properties::CanPlay>(),
> +                  configuration.object->template get_property<Properties::CanPause>(),
> +                  configuration.object->template get_property<Properties::CanSeek>(),
> +                  configuration.object->template get_property<Properties::CanControl>(),
> +                  configuration.object->template get_property<Properties::CanGoNext>(),
> +                  configuration.object->template get_property<Properties::CanGoPrevious>(),
> +                  configuration.object->template get_property<Properties::IsVideoSource>(),
> +                  configuration.object->template get_property<Properties::IsAudioSource>(),
> +                  configuration.object->template get_property<Properties::PlaybackStatus>(),
> +                  configuration.object->template get_property<Properties::TypedPlaybackStatus>(),
> +                  configuration.object->template get_property<Properties::LoopStatus>(),
> +                  configuration.object->template get_property<Properties::TypedLoopStatus>(),
> +                  configuration.object->template get_property<Properties::AudioStreamRole>(),
> +                  configuration.object->template get_property<Properties::PlaybackRate>(),
> +                  configuration.object->template get_property<Properties::Shuffle>(),
> +                  configuration.object->template get_property<Properties::TypedMetaData>(),
> +                  configuration.object->template get_property<Properties::Volume>(),
> +                  configuration.object->template get_property<Properties::Position>(),
> +                  configuration.object->template get_property<Properties::Duration>(),
> +                  configuration.object->template get_property<Properties::MinimumRate>(),
> +                  configuration.object->template get_property<Properties::MaximumRate>()
>                },
>                signals
>                {
> -                  configuration.object->template get_signal<mpris::Player::Signals::Seeked>(),
> -                  configuration.object->template get_signal<mpris::Player::Signals::EndOfStream>(),
> -                  configuration.object->template get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
> +                  configuration.object->template get_signal<Signals::Seeked>(),
> +                  configuration.object->template get_signal<Signals::EndOfStream>(),
> +                  configuration.object->template get_signal<Signals::PlaybackStatusChanged>(),
> +                  configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
>                }
> -        {            
> +        {
> +            properties.can_play->set(configuration.defaults.can_play);
> +            properties.can_pause->set(configuration.defaults.can_pause);
> +            properties.can_seek->set(configuration.defaults.can_seek);
> +            properties.can_control->set(configuration.defaults.can_control);
> +            properties.can_go_next->set(configuration.defaults.can_go_next);
> +            properties.can_go_previous->set(configuration.defaults.can_go_previous);
> +            properties.is_video_source->set(configuration.defaults.is_video_source);
> +            properties.is_audio_source->set(configuration.defaults.is_audio_source);
> +            properties.playback_status->set(configuration.defaults.playback_status);
> +            properties.typed_playback_status->set(configuration.defaults.typed_playback_status);
> +            properties.loop_status->set(configuration.defaults.loop_status);
> +            properties.typed_loop_status->set(configuration.defaults.typed_loop_status);
> +            properties.audio_stream_role->set(core::ubuntu::media::Player::AudioStreamRole::multimedia);
> +            properties.playback_rate->set(configuration.defaults.playback_rate);
> +            properties.is_shuffle->set(configuration.defaults.shuffle);
> +            properties.position->set(configuration.defaults.position);
> +            properties.duration->set(configuration.defaults.duration);
> +            properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);
> +            properties.maximum_playback_rate->set(configuration.defaults.maximum_rate);            
> +
> +            properties.position->changed().connect([this](std::int64_t position)
> +            {
> +                on_property_value_changed<Properties::Position>(position);
> +            });
> +
> +            properties.duration->changed().connect([this](std::int64_t duration)
> +            {
> +                on_property_value_changed<Properties::Duration>(duration);
> +            });
> +
> +            properties.playback_status->changed().connect([this](const std::string& status)
> +            {
> +                on_property_value_changed<Properties::PlaybackStatus>(status);
> +            });
> +
> +            properties.loop_status->changed().connect([this](const std::string& status)
> +            {
> +                on_property_value_changed<Properties::LoopStatus>(status);
> +            });                       
> +        }
> +
> +        template<typename Property>
> +        void on_property_value_changed(const typename Property::ValueType& value)
> +        {
> +            Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
> +
> +            signals.properties_changed->emit(std::make_tuple(
> +                            dbus::traits::Service<Player>::interface_name(),
> +                            dict,
> +                            the_empty_list_of_invalided_properties()));
> +        }
> +
> +        Dictionary get_all_properties()
> +        {
> +            Dictionary dict;
> +            dict[Properties::CanPlay::name()] = dbus::types::Variant::encode(properties.can_play->get());
> +            dict[Properties::CanPause::name()] = dbus::types::Variant::encode(properties.can_pause->get());
> +            dict[Properties::CanSeek::name()] = dbus::types::Variant::encode(properties.can_seek->get());
> +            dict[Properties::CanControl::name()] = dbus::types::Variant::encode(properties.can_control->get());
> +            dict[Properties::CanGoNext::name()] = dbus::types::Variant::encode(properties.can_go_next->get());
> +            dict[Properties::CanGoPrevious::name()] = dbus::types::Variant::encode(properties.can_go_previous->get());
> +            dict[Properties::PlaybackStatus::name()] = dbus::types::Variant::encode(properties.playback_status->get());
> +            dict[Properties::TypedPlaybackStatus::name()] = dbus::types::Variant::encode(properties.typed_playback_status->get());
> +            dict[Properties::LoopStatus::name()] = dbus::types::Variant::encode(properties.loop_status->get());
> +            dict[Properties::TypedLoopStatus::name()] = dbus::types::Variant::encode(properties.typed_loop_status->get());
> +            dict[Properties::AudioStreamRole::name()] = dbus::types::Variant::encode(properties.audio_stream_role->get());
> +            dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());
> +            dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.is_shuffle->get());
> +            dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());
> +            dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());
> +            dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());
> +            dict[Properties::MaximumRate::name()] = dbus::types::Variant::encode(properties.maximum_playback_rate->get());
> +
> +            return dict;
>          }
>  
>          // We just store creation time properties
> @@ -188,34 +309,41 @@
>          // All the properties exposed to the bus go here.
>          struct
>          {
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> typed_playback_status;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> typed_loop_status;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::AudioStreamRole>> audio_stream_role;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
> -            std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
> +            std::shared_ptr<core::dbus::Property<Properties::CanPlay>> can_play;
> +            std::shared_ptr<core::dbus::Property<Properties::CanPause>> can_pause;
> +            std::shared_ptr<core::dbus::Property<Properties::CanSeek>> can_seek;
> +            std::shared_ptr<core::dbus::Property<Properties::CanControl>> can_control;
> +            std::shared_ptr<core::dbus::Property<Properties::CanGoNext>> can_go_next;
> +            std::shared_ptr<core::dbus::Property<Properties::CanGoPrevious>> can_go_previous;
> +            std::shared_ptr<core::dbus::Property<Properties::IsVideoSource>> is_video_source;
> +            std::shared_ptr<core::dbus::Property<Properties::IsAudioSource>> is_audio_source;
> +
> +            std::shared_ptr<core::dbus::Property<Properties::PlaybackStatus>> playback_status;
> +            std::shared_ptr<core::dbus::Property<Properties::TypedPlaybackStatus>> typed_playback_status;
> +            std::shared_ptr<core::dbus::Property<Properties::LoopStatus>> loop_status;
> +            std::shared_ptr<core::dbus::Property<Properties::TypedLoopStatus>> typed_loop_status;
> +            std::shared_ptr<core::dbus::Property<Properties::AudioStreamRole>> audio_stream_role;
> +            std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;
> +            std::shared_ptr<core::dbus::Property<Properties::Shuffle>> is_shuffle;
> +            std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track;
> +            std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;
> +            std::shared_ptr<core::dbus::Property<Properties::Position>> position;
> +            std::shared_ptr<core::dbus::Property<Properties::Duration>> duration;
> +            std::shared_ptr<core::dbus::Property<Properties::MinimumRate>> minimum_playback_rate;
> +            std::shared_ptr<core::dbus::Property<Properties::MaximumRate>> maximum_playback_rate;
>          } properties;
>  
>          struct
>          {
> -            typename core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType>::Ptr seeked_to;
> -            typename core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
> -            typename core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
> +            typename core::dbus::Signal<Signals::Seeked, Signals::Seeked::ArgumentType>::Ptr seeked_to;
> +            typename core::dbus::Signal<Signals::EndOfStream, Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
> +            typename core::dbus::Signal<Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
> +
> +            dbus::Signal
> +            <
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged,
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
> +            >::Ptr properties_changed;
>          } signals;
>      };
>  };
> 
> === added file 'src/core/media/mpris/playlists.h'
> --- src/core/media/mpris/playlists.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/mpris/playlists.h	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,216 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.

Needs to include 2014

> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef MPRIS_PLAYLISTS_H_
> +#define MPRIS_PLAYLISTS_H_
> +
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/property.h>
> +#include <core/dbus/interfaces/properties.h>
> +#include <core/dbus/types/struct.h>
> +#include <core/dbus/types/variant.h>
> +
> +#include <string>
> +#include <vector>
> +
> +namespace core
> +{
> +namespace dbus
> +{
> +namespace types
> +{
> +template<typename T>
> +bool operator !=(const Struct<T>& lhs, const Struct<T>& rhs)
> +{
> +    return lhs.value != rhs.value;
> +}
> +}
> +}
> +}
> +
> +namespace mpris
> +{
> +// Models interface org.mpris.MediaPlayer2.Playlists, see:
> +//   http://specifications.freedesktop.org/mpris-spec/latest/Playlists_Interface.html
> +// for detailed documentation
> +struct Playlists
> +{
> +    static const std::string& name()
> +    {
> +        static const std::string s{"org.mpris.MediaPlayer2.Playlists"}; return s;
> +    }
> +
> +    // All known orderings
> +    struct Orderings
> +    {
> +        // Alphabetical ordering by name, ascending.
> +        static constexpr const char* alphabetical{"Alphabetical"};
> +        // Ordering by creation date, oldest first.
> +        static constexpr const char* creation_date{"CreationDate"};
> +        // Ordering by last modified date, oldest first.
> +        static constexpr const char* modified_date{"ModifiedDate"};
> +        // Ordering by date of last playback, oldest first.
> +        static constexpr const char* last_play_date{"LastPlayDate"};
> +        // A user-defined ordering.
> +        static constexpr const char* user_defined{"UserDefined"};
> +    };
> +
> +    // A datastructure describing a playlist.
> +    typedef core::dbus::types::Struct
> +    <
> +        std::tuple
> +        <
> +            // A unique identifier for the playlist.
> +            // This should remain the same if the playlist is renamed.
> +            core::dbus::types::ObjectPath,
> +            // The name of the playlist, typically given by the user.
> +            std::string,
> +            // The URI of an (optional) icon.
> +            std::string
> +        >
> +    > Playlist;
> +
> +    // A data structure describing a playlist, or nothing.
> +    typedef core::dbus::types::Struct
> +    <
> +        std::tuple
> +        <
> +            // Whether this structure refers to a valid playlist.
> +            bool,
> +            // The playlist, providing Valid is true, otherwise undefined.
> +            Playlist
> +        >
> +    > MaybePlaylist;
> +
> +    struct Methods
> +    {
> +        Methods() = delete;
> +
> +        // Starts playing the given playlist.
> +        // Note that this must be implemented. If the media player does not
> +        // allow clients to change the playlist, it should not implement this
> +        // interface at all.
> +        DBUS_CPP_METHOD_DEF(ActivatePlaylist, Playlists)
> +
> +        // Gets a set of playlists.
> +        // Parameters
> +        // Index — u
> +        //   The index of the first playlist to be fetched (according to the ordering).
> +        // MaxCount — u
> +        //   The maximum number of playlists to fetch.
> +        // Order — s (Playlist_Ordering)
> +        //   The ordering that should be used.
> +        // ReverseOrder — b
> +        //   Whether the order should be reversed.
> +        //
> +        // Returns
> +        // Playlists — a(oss) (Playlist_List)
> +        //   A list of (at most MaxCount) playlists.
> +        DBUS_CPP_METHOD_DEF(GetPlaylists, Playlists)
> +    };
> +
> +    struct Signals
> +    {
> +        Signals() = delete;
> +
> +        // Indicates that either the Name or Icon attribute of a playlist has changed.
> +        // Client implementations should be aware that this signal may not be implemented.
> +        DBUS_CPP_SIGNAL_DEF(PlaylistsChanged, Playlists, Playlist)
> +    };
> +
> +    struct Properties
> +    {
> +        Properties() = delete;
> +
> +        // The number of playlists available.
> +        DBUS_CPP_READABLE_PROPERTY_DEF(PlaylistCount, Playlists, std::uint32_t)
> +        // The available orderings. At least one must be offered.
> +        DBUS_CPP_READABLE_PROPERTY_DEF(Orderings, Playlists, std::vector<std::string>)
> +        // The currently-active playlist.
> +        DBUS_CPP_READABLE_PROPERTY_DEF(ActivePlaylist, Playlists, MaybePlaylist)
> +    };
> +
> +    struct Skeleton
> +    {
> +        // Creation time properties go here.
> +        struct Configuration
> +        {
> +            // The bus connection that should be used
> +            core::dbus::Bus::Ptr bus;
> +            // The dbus object that should implement org.mpris.MediaPlayer2
> +            core::dbus::Object::Ptr object;
> +            // Default values assigned to properties on construction
> +            struct Defaults
> +            {
> +                Properties::PlaylistCount::ValueType playlist_count{0};
> +                Properties::Orderings::ValueType orderings{{Orderings::alphabetical}};
> +                Properties::ActivePlaylist::ValueType active_playlist
> +                {
> +                    std::make_tuple(false, Playlist
> +                    {
> +                        std::make_tuple(
> +                            core::dbus::types::ObjectPath{"/"},
> +                            std::string{},
> +                            std::string{})
> +                    })
> +                };
> +            } defaults;
> +        };
> +
> +        Skeleton(const Configuration& configuration)
> +            : configuration(configuration),
> +              properties
> +              {
> +                  configuration.object->get_property<Properties::PlaylistCount>(),
> +                  configuration.object->get_property<Properties::Orderings>()
> +              },
> +              signals
> +              {
> +                  configuration.object->get_signal<Signals::PlaylistsChanged>()
> +              }
> +        {
> +            properties.playlist_count->set(configuration.defaults.playlist_count);
> +            properties.orderings->set(configuration.defaults.orderings);
> +        }
> +
> +        std::map<std::string, core::dbus::types::Variant> get_all_properties()
> +        {
> +            std::map<std::string, core::dbus::types::Variant> dict;
> +            dict[Properties::PlaylistCount::name()] = core::dbus::types::Variant::encode(properties.playlist_count->get());
> +            dict[Properties::Orderings::name()] = core::dbus::types::Variant::encode(properties.orderings->get());
> +
> +            return dict;
> +        }
> +
> +        Configuration configuration;
> +
> +        struct
> +        {
> +            std::shared_ptr<core::dbus::Property<Properties::PlaylistCount>> playlist_count;
> +            std::shared_ptr<core::dbus::Property<Properties::Orderings>> orderings;
> +        } properties;
> +
> +        struct
> +        {
> +            core::dbus::Signal<Signals::PlaylistsChanged, Signals::PlaylistsChanged::ArgumentType>::Ptr playlist_changed;
> +        } signals;
> +    };
> +};
> +}
> +#endif // MPRIS_PLAYLISTS_H_
> 
> === modified file 'src/core/media/player.cpp'
> --- src/core/media/player.cpp	2014-02-12 15:53:57 +0000
> +++ src/core/media/player.cpp	2014-09-10 07:05:35 +0000
> @@ -24,7 +24,13 @@
>  
>  const media::Player::Configuration& media::Player::Client::default_configuration()
>  {
> -    static const media::Player::Configuration config;
> +    static const media::Player::Configuration config
> +    {
> +        std::string{""},
> +        0,
> +        nullptr,
> +        nullptr
> +    };
>      return config;
>  }
>  
> 
> === modified file 'src/core/media/player_configuration.h'
> --- src/core/media/player_configuration.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_configuration.h	2014-09-10 07:05:35 +0000
> @@ -23,9 +23,18 @@
>  #include <core/dbus/bus.h>
>  #include <core/dbus/object.h>
>  
> +// Our internal structure for handing down parameters from the skeleton
> +// to the implementation in a way that is opaque to the client.
>  struct core::ubuntu::media::Player::Configuration
>  {
> +    // An identifier that is helpful in referencing the player instance
> +    // across multiple services.
> +    std::string identity;
> +    // Unique key for identifying the session.
> +    core::ubuntu::media::Player::PlayerKey key;
> +    // The bus connection to expose objects on.
>      std::shared_ptr<core::dbus::Bus> bus;
> +    // The actual session object representing a player instance.
>      std::shared_ptr<core::dbus::Object> session;
>  };
>  
> 
> === modified file 'src/core/media/player_implementation.cpp'
> --- src/core/media/player_implementation.cpp	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_implementation.cpp	2014-09-10 07:05:35 +0000
> @@ -47,7 +47,7 @@
>          WAKELOCK_CLEAR_DISPLAY,
>          WAKELOCK_CLEAR_SYSTEM,
>          WAKELOCK_CLEAR_INVALID
> -    };
> +    };    
>  
>      Private(PlayerImplementation* parent,
>              const dbus::types::ObjectPath& session_path,
> @@ -102,6 +102,10 @@
>              }
>              case Engine::State::playing:
>              {
> +                // We update the track meta data prior to updating the playback status.
> +                // Some MPRIS clients expect this order of events.
> +                parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get()));
> +                // And update our playback status.
>                  parent->playback_status().set(media::Player::playing);
>                  if (previous_state == Engine::State::stopped || previous_state == Engine::State::paused)
>                  {
> @@ -135,7 +139,6 @@
>              // Keep track of the previous Engine playback state:
>              previous_state = state;
>          });
> -
>      }
>  
>      ~Private()
> @@ -281,14 +284,24 @@
>      std::unique_ptr<timeout> wakelock_timeout;
>      Engine::State previous_state;
>      PlayerImplementation::PlayerKey key;
> +    core::Signal<> on_client_disconnected;
>  };
>  
>  media::PlayerImplementation::PlayerImplementation(
> +        const std::string& identity,
>          const std::shared_ptr<core::dbus::Bus>& bus,
>          const std::shared_ptr<core::dbus::Object>& session,
>          const std::shared_ptr<Service>& service,
>          PlayerKey key)
> -    : media::PlayerSkeleton(bus, session),
> +    : media::PlayerSkeleton
> +      {
> +          media::PlayerSkeleton::Configuration
> +          {
> +              bus,
> +              session,
> +              identity
> +          }
> +      },
>        d(new Private(
>              this,
>              session->path(),
> @@ -362,6 +375,8 @@
>          // If the client disconnects, make sure both wakelock types
>          // are cleared
>          d->clear_wakelocks();
> +        // And tell the outside world that the client has gone away
> +        d->on_client_disconnected();
>      });
>  
>      d->engine->seeked_to_signal().connect([this](uint64_t value)
> @@ -431,6 +446,7 @@
>  
>  void media::PlayerImplementation::stop()
>  {
> +    std::cout << __PRETTY_FUNCTION__ << std::endl;
>      d->engine->stop();
>  }
>  
> @@ -450,3 +466,8 @@
>  {
>      d->engine->seek_to(ms);
>  }
> +
> +const core::Signal<>& media::PlayerImplementation::on_client_disconnected() const
> +{
> +    return d->on_client_disconnected;
> +}
> 
> === modified file 'src/core/media/player_implementation.h'
> --- src/core/media/player_implementation.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_implementation.h	2014-09-10 07:05:35 +0000
> @@ -36,6 +36,7 @@
>  {
>  public:
>      PlayerImplementation(
> +            const std::string& identity,
>              const std::shared_ptr<core::dbus::Bus>& bus,
>              const std::shared_ptr<core::dbus::Object>& session,
>              const std::shared_ptr<Service>& service,
> @@ -57,6 +58,7 @@
>      virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context);
>      virtual void seek_to(const std::chrono::microseconds& offset);
>  
> +    const core::Signal<>& on_client_disconnected() const;
>  private:
>      struct Private;
>      std::unique_ptr<Private> d;
> 
> === modified file 'src/core/media/player_skeleton.cpp'
> --- src/core/media/player_skeleton.cpp	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_skeleton.cpp	2014-09-10 07:05:35 +0000
> @@ -23,56 +23,44 @@
>  #include "player_traits.h"
>  #include "property_stub.h"
>  #include "the_session_bus.h"
> +#include "xesam.h"
>  
> +#include "mpris/media_player2.h"
> +#include "mpris/metadata.h"
>  #include "mpris/player.h"
> +#include "mpris/playlists.h"
>  
>  #include <core/dbus/object.h>
>  #include <core/dbus/property.h>
>  #include <core/dbus/stub.h>
> +
>  #include <core/dbus/asio/executor.h>
> +#include <core/dbus/interfaces/properties.h>
>  
>  namespace dbus = core::dbus;
>  namespace media = core::ubuntu::media;
>  
> -struct media::PlayerSkeleton::Private
> +struct media::PlayerSkeleton::Private : public std::enable_shared_from_this<media::PlayerSkeleton::Private>
>  {
>      Private(media::PlayerSkeleton* player,
> +            const std::string& identity,
>              const std::shared_ptr<core::dbus::Bus>& bus,
>              const std::shared_ptr<core::dbus::Object>& session)
>          : impl(player),
> +          identity(identity),
>            bus(bus),
>            object(session),
>            apparmor_session(nullptr),
> -          properties
> -          {
> -              object->get_property<mpris::Player::Properties::CanPlay>(),
> -              object->get_property<mpris::Player::Properties::CanPause>(),
> -              object->get_property<mpris::Player::Properties::CanSeek>(),
> -              object->get_property<mpris::Player::Properties::CanControl>(),
> -              object->get_property<mpris::Player::Properties::CanGoNext>(),
> -              object->get_property<mpris::Player::Properties::CanGoPrevious>(),
> -              object->get_property<mpris::Player::Properties::IsVideoSource>(),
> -              object->get_property<mpris::Player::Properties::IsAudioSource>(),
> -              object->get_property<mpris::Player::Properties::TypedPlaybackStatus>(),
> -              object->get_property<mpris::Player::Properties::TypedLoopStatus>(),
> -              object->get_property<mpris::Player::Properties::PlaybackRate>(),
> -              object->get_property<mpris::Player::Properties::Shuffle>(),
> -              object->get_property<mpris::Player::Properties::TypedMetaData>(),
> -              object->get_property<mpris::Player::Properties::Volume>(),
> -              object->get_property<mpris::Player::Properties::Position>(),
> -              object->get_property<mpris::Player::Properties::Duration>(),
> -              object->get_property<mpris::Player::Properties::AudioStreamRole>(),
> -              object->get_property<mpris::Player::Properties::MinimumRate>(),
> -              object->get_property<mpris::Player::Properties::MaximumRate>()
> -          },
> +          dbus_stub{bus},
> +          skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},
>            signals
>            {
> -              object->get_signal<mpris::Player::Signals::Seeked>(),
> -              object->get_signal<mpris::Player::Signals::EndOfStream>(),
> -              object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
> +              skeleton.signals.seeked_to,
> +              skeleton.signals.end_of_stream,
> +              skeleton.signals.playback_status_changed
>            }
>      {
> -    }
> +    }    
>  
>      void handle_next(const core::dbus::Message::Ptr& msg)
>      {
> @@ -95,10 +83,6 @@
>          bus->send(reply);
>      }
>  
> -    void handle_playpause(DBusMessage*)
> -    {
> -    }
> -
>      void handle_stop(const core::dbus::Message::Ptr& msg)
>      {
>          impl->stop();
> @@ -113,6 +97,25 @@
>          bus->send(reply);
>      }
>  
> +    void handle_play_pause(const core::dbus::Message::Ptr& msg)
> +    {
> +        switch(impl->playback_status().get())
> +        {
> +        case core::ubuntu::media::Player::PlaybackStatus::ready:
> +        case core::ubuntu::media::Player::PlaybackStatus::paused:
> +        case core::ubuntu::media::Player::PlaybackStatus::stopped:
> +            impl->play();
> +            break;
> +        case core::ubuntu::media::Player::PlaybackStatus::playing:
> +            impl->pause();
> +            break;
> +        default:
> +            break;
> +        }
> +
> +        bus->send(dbus::Message::make_method_return(msg));
> +    }
> +
>      void handle_seek(const core::dbus::Message::Ptr& in)
>      {
>          uint64_t ticks;
> @@ -135,25 +138,7 @@
>  
>          auto reply = dbus::Message::make_method_return(in);
>          bus->send(reply);
> -    }
> -
> -    std::string get_client_apparmor_context(const core::dbus::Message::Ptr& msg)
> -    {
> -        auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
> -        bus->install_executor(dbus::asio::make_executor(bus));
> -
> -        auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Apparmor>::interface_name());
> -        apparmor_session = stub_service->object_for_path(dbus::types::ObjectPath("/org/freedesktop/DBus"));
> -        // Get the AppArmor security context for the client
> -        auto result = apparmor_session->invoke_method_synchronously<core::Apparmor::getConnectionAppArmorSecurityContext, std::string>(msg->sender());
> -        if (result.is_error())
> -        {
> -            std::cout << "Error getting apparmor profile: " << result.error().print() << std::endl;
> -            return std::string();
> -        }
> -
> -        return result.value();
> -    }
> +    }    
>  
>      bool does_client_have_access(const std::string& context, const std::string& uri)
>      {
> @@ -239,49 +224,51 @@
>      }
>  
>      void handle_open_uri(const core::dbus::Message::Ptr& in)
> +    {        
> +        dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
> +        {
> +            Track::UriType uri;
> +            in->reader() >> uri;
> +
> +            bool have_access = does_client_have_access(profile, uri);
> +
> +            auto reply = dbus::Message::make_method_return(in);
> +            reply->writer() << (have_access ? impl->open_uri(uri) : false);
> +
> +            bus->send(reply);
> +        });
> +    }
> +
> +    template<typename Property>
> +    void on_property_value_changed(
> +            const typename Property::ValueType& value,
> +            const dbus::Signal
> +            <
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged,
> +                core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
> +            >::Ptr& signal)
>      {
> -        Track::UriType uri;
> -        in->reader() >> uri;
> -
> -        std::string context = get_client_apparmor_context(in);
> -        bool have_access = does_client_have_access(context, uri);
> -
> -        auto reply = dbus::Message::make_method_return(in);
> -        if (have_access)
> -            reply->writer() << impl->open_uri(uri);
> -        else
> -            reply->writer() << false;
> -        bus->send(reply);
> +        typedef std::map<std::string, dbus::types::Variant> Dictionary;
> +
> +        static const std::vector<std::string> the_empty_list_of_invalided_properties;

Same here, invalid or invalidated

> +
> +        Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
> +
> +        signal->emit(std::make_tuple(
> +                        dbus::traits::Service<typename Property::Interface>::interface_name(),
> +                        dict,
> +                        the_empty_list_of_invalided_properties));

Same here, invalid or invalidated

>      }
>  
>      media::PlayerSkeleton* impl;
> +    std::string identity;
>      dbus::Bus::Ptr bus;
>      dbus::Object::Ptr object;
>      dbus::Object::Ptr apparmor_session;
>  
> -    struct
> -    {
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
> +    org::freedesktop::dbus::DBus::Stub dbus_stub;
>  
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> playback_status;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::AudioStreamRole>> audio_role;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
> -        std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
> -    } properties;
> +    mpris::Player::Skeleton skeleton;    
>  
>      struct Signals
>      {
> @@ -289,92 +276,70 @@
>          typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
>          typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
>  
> -        Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
> -                const std::shared_ptr<DBusEndOfStreamSignal>& eos,
> -                const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status)
> -            : dbus
> -              {
> -                  seeked,
> -                  eos,
> -                  status
> -              },
> -              seeked_to(),
> -              end_of_stream(),
> -              playback_status_changed()
> +        Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,
> +                const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,
> +                const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed)
>          {
> -            seeked_to.connect([this](std::uint64_t value)
> -            {
> -                dbus.seeked_to->emit(value);
> -            });
> -
> -            end_of_stream.connect([this]()
> -            {
> -                dbus.end_of_stream->emit();
> -            });
> -
> -            playback_status_changed.connect([this](const media::Player::PlaybackStatus& status)
> -            {
> -                dbus.playback_status_changed->emit(status);
> +            seeked_to.connect([remote_seeked](std::uint64_t value)
> +            {
> +                remote_seeked->emit(value);
> +            });
> +
> +            end_of_stream.connect([remote_eos]()
> +            {
> +                remote_eos->emit();
> +            });
> +
> +            playback_status_changed.connect([remote_playback_status_changed](const media::Player::PlaybackStatus& status)
> +            {
> +                remote_playback_status_changed->emit(status);
>              });
>          }
>  
> -        struct DBus
> -        {
> -            std::shared_ptr<DBusSeekedToSignal> seeked_to;
> -            std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
> -            std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
> -        } dbus;
> -        core::Signal<uint64_t> seeked_to;
> +        core::Signal<int64_t> seeked_to;
>          core::Signal<void> end_of_stream;
>          core::Signal<media::Player::PlaybackStatus> playback_status_changed;
>      } signals;
>  
>  };
>  
> -media::PlayerSkeleton::PlayerSkeleton(
> -        const std::shared_ptr<core::dbus::Bus>& bus,
> -        const std::shared_ptr<core::dbus::Object>& session)
> -        : d(new Private{this, bus, session})
> +media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
> +        : d(new Private{this, config.identity, config.bus, config.session})
>  {
> -    d->object->install_method_handler<mpris::Player::Next>(
> -        std::bind(&Private::handle_next,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::Previous>(
> -        std::bind(&Private::handle_previous,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::Pause>(
> -        std::bind(&Private::handle_pause,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::Stop>(
> -        std::bind(&Private::handle_stop,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::Play>(
> -        std::bind(&Private::handle_play,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::Seek>(
> -        std::bind(&Private::handle_seek,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::SetPosition>(
> -        std::bind(&Private::handle_set_position,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> +    // Setup method handlers for mpris::Player methods.
> +    auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Next>(next);
> +
> +    auto previous = std::bind(&Private::handle_previous, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Previous>(previous);
> +
> +    auto pause = std::bind(&Private::handle_pause, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Pause>(pause);
> +
> +    auto stop = std::bind(&Private::handle_stop, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Stop>(stop);
> +
> +    auto play = std::bind(&Private::handle_play, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Play>(play);
> +
> +    auto seek = std::bind(&Private::handle_seek, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::Seek>(seek);
> +
> +    auto set_position = std::bind(&Private::handle_set_position, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::SetPosition>(set_position);
> +
> +    auto open_uri = std::bind(&Private::handle_open_uri, d, std::placeholders::_1);
> +    d->object->install_method_handler<mpris::Player::OpenUri>(open_uri);
> +
> +    // All the method handlers that exceed the mpris spec go here.
>      d->object->install_method_handler<mpris::Player::CreateVideoSink>(
>          std::bind(&Private::handle_create_video_sink,
> -                  std::ref(d),
> +                  d,
>                    std::placeholders::_1));
> +
>      d->object->install_method_handler<mpris::Player::Key>(
>          std::bind(&Private::handle_key,
> -                  std::ref(d),
> -                  std::placeholders::_1));
> -    d->object->install_method_handler<mpris::Player::OpenUri>(
> -        std::bind(&Private::handle_open_uri,
> -                  std::ref(d),
> +                  d,
>                    std::placeholders::_1));
>  }
>  
> @@ -384,191 +349,191 @@
>  
>  const core::Property<bool>& media::PlayerSkeleton::can_play() const
>  {
> -    return *d->properties.can_play;
> +    return *d->skeleton.properties.can_play;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::can_pause() const
>  {
> -    return *d->properties.can_pause;
> +    return *d->skeleton.properties.can_pause;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::can_seek() const
>  {
> -    return *d->properties.can_seek;
> +    return *d->skeleton.properties.can_seek;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::can_go_previous() const
>  {
> -    return *d->properties.can_go_previous;
> +    return *d->skeleton.properties.can_go_previous;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::can_go_next() const
>  {
> -    return *d->properties.can_go_next;
> +    return *d->skeleton.properties.can_go_next;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::is_video_source() const
>  {
> -    return *d->properties.is_video_source;
> +    return *d->skeleton.properties.is_video_source;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const
>  {
> -    return *d->properties.is_audio_source;
> +    return *d->skeleton.properties.is_audio_source;
>  }
>  
>  const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const
>  {
> -    return *d->properties.playback_status;
> +    return *d->skeleton.properties.typed_playback_status;
>  }
>  
>  const core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() const
>  {
> -    return *d->properties.loop_status;
> +    return *d->skeleton.properties.typed_loop_status;
>  }
>  
>  const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() const
>  {
> -    return *d->properties.playback_rate;
> +    return *d->skeleton.properties.playback_rate;
>  }
>  
>  const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const
>  {
> -    return *d->properties.is_shuffle;
> +    return *d->skeleton.properties.is_shuffle;
>  }
>  
>  const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
>  {
> -    return *d->properties.meta_data_for_current_track;
> +    return *d->skeleton.properties.typed_meta_data_for_current_track;
>  }
>  
>  const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const
>  {
> -    return *d->properties.volume;
> -}
> -
> -const core::Property<uint64_t>& media::PlayerSkeleton::position() const
> -{
> -    return *d->properties.position;
> -}
> -
> -const core::Property<uint64_t>& media::PlayerSkeleton::duration() const
> -{
> -    return *d->properties.duration;
> +    return *d->skeleton.properties.volume;
> +}
> +
> +const core::Property<int64_t>& media::PlayerSkeleton::position() const
> +{
> +    return *d->skeleton.properties.position;
> +}
> +
> +const core::Property<int64_t>& media::PlayerSkeleton::duration() const
> +{
> +    return *d->skeleton.properties.duration;
>  }
>  
>  const core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role() const
>  {
> -    return *d->properties.audio_role;
> +    return *d->skeleton.properties.audio_stream_role;
>  }
>  
>  const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() const
>  {
> -    return *d->properties.minimum_playback_rate;
> +    return *d->skeleton.properties.minimum_playback_rate;
>  }
>  
>  const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() const
>  {
> -    return *d->properties.maximum_playback_rate;
> +    return *d->skeleton.properties.maximum_playback_rate;
>  }
>  
>  core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status()
>  {
> -    return *d->properties.loop_status;
> +    return *d->skeleton.properties.typed_loop_status;
>  }
>  
>  core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate()
>  {
> -    return *d->properties.playback_rate;
> +    return *d->skeleton.properties.playback_rate;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::is_shuffle()
>  {
> -    return *d->properties.is_shuffle;
> +    return *d->skeleton.properties.is_shuffle;
>  }
>  
>  core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
>  {
> -    return *d->properties.volume;
> -}
> -
> -core::Property<uint64_t>& media::PlayerSkeleton::position()
> -{
> -    return *d->properties.position;
> -}
> -
> -core::Property<uint64_t>& media::PlayerSkeleton::duration()
> -{
> -    return *d->properties.duration;
> +    return *d->skeleton.properties.volume;
> +}
> +
> +core::Property<int64_t>& media::PlayerSkeleton::position()
> +{
> +    return *d->skeleton.properties.position;
> +}
> +
> +core::Property<int64_t>& media::PlayerSkeleton::duration()
> +{
> +    return *d->skeleton.properties.duration;
>  }
>  
>  core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role()
>  {
> -    return *d->properties.audio_role;
> +    return *d->skeleton.properties.audio_stream_role;
>  }
>  
>  core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status()
>  {
> -    return *d->properties.playback_status;
> +    return *d->skeleton.properties.typed_playback_status;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::can_play()
>  {
> -    return *d->properties.can_play;
> +    return *d->skeleton.properties.can_play;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::can_pause()
>  {
> -    return *d->properties.can_pause;
> +    return *d->skeleton.properties.can_pause;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::can_seek()
>  {
> -    return *d->properties.can_seek;
> +    return *d->skeleton.properties.can_seek;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::can_go_previous()
>  {
> -    return *d->properties.can_go_previous;
> +    return *d->skeleton.properties.can_go_previous;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::can_go_next()
>  {
> -    return *d->properties.can_go_next;
> +    return *d->skeleton.properties.can_go_next;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::is_video_source()
>  {
> -    return *d->properties.is_video_source;
> +    return *d->skeleton.properties.is_video_source;
>  }
>  
>  core::Property<bool>& media::PlayerSkeleton::is_audio_source()
>  {
> -    return *d->properties.is_audio_source;
> +    return *d->skeleton.properties.is_audio_source;
>  }
>  
>  
>  core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
>  {
> -    return *d->properties.meta_data_for_current_track;
> +    return *d->skeleton.properties.typed_meta_data_for_current_track;
>  }
>  
>  core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()
>  {
> -    return *d->properties.minimum_playback_rate;
> +    return *d->skeleton.properties.minimum_playback_rate;
>  }
>  
>  core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate()
>  {
> -    return *d->properties.maximum_playback_rate;
> +    return *d->skeleton.properties.maximum_playback_rate;
>  }
>  
> -const core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to() const
> +const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const
>  {
>      return d->signals.seeked_to;
>  }
>  
> -core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to()
> +core::Signal<int64_t>& media::PlayerSkeleton::seeked_to()
>  {
>      return d->signals.seeked_to;
>  }
> 
> === modified file 'src/core/media/player_skeleton.h'
> --- src/core/media/player_skeleton.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_skeleton.h	2014-09-10 07:05:35 +0000
> @@ -59,8 +59,8 @@
>      virtual const core::Property<Volume>& volume() const;
>      virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
>      virtual const core::Property<PlaybackRate>& maximum_playback_rate() const;
> -    virtual const core::Property<uint64_t>& position() const;
> -    virtual const core::Property<uint64_t>& duration() const;
> +    virtual const core::Property<int64_t>& position() const;
> +    virtual const core::Property<int64_t>& duration() const;
>      virtual const core::Property<AudioStreamRole>& audio_stream_role() const;
>  
>      virtual core::Property<LoopStatus>& loop_status();
> @@ -69,14 +69,24 @@
>      virtual core::Property<Volume>& volume();
>      virtual core::Property<AudioStreamRole>& audio_stream_role();
>  
> -    virtual const core::Signal<uint64_t>& seeked_to() const;
> +    virtual const core::Signal<int64_t>& seeked_to() const;
>      virtual const core::Signal<void>& end_of_stream() const;
>      virtual core::Signal<PlaybackStatus>& playback_status_changed();
>  
> -  protected:
> -    PlayerSkeleton(
> -            const std::shared_ptr<core::dbus::Bus>& bus,
> -            const std::shared_ptr<core::dbus::Object>& session);
> +protected:
> +    // All creation time arguments go here.
> +    struct Configuration
> +    {
> +        // The bus connection we are associated with.
> +        std::shared_ptr<core::dbus::Bus> bus;
> +        // The session object that we want to expose the skeleton upon.
> +        std::shared_ptr<core::dbus::Object> session;
> +        // Our identity, an identifier we pass out to other parts of the system.
> +        // Defaults to the short app id (${PKG_NAME}_${APP}).
> +        std::string identity;
> +    };
> +
> +    PlayerSkeleton(const Configuration& configuration);
>  
>      virtual core::Property<PlaybackStatus>& playback_status();
>      virtual core::Property<bool>& can_play();
> @@ -89,15 +99,15 @@
>      virtual core::Property<Track::MetaData>& meta_data_for_current_track();
>      virtual core::Property<PlaybackRate>& minimum_playback_rate();
>      virtual core::Property<PlaybackRate>& maximum_playback_rate();
> -    virtual core::Property<uint64_t>& position();
> -    virtual core::Property<uint64_t>& duration();
> +    virtual core::Property<int64_t>& position();
> +    virtual core::Property<int64_t>& duration();
>  
> -    virtual core::Signal<uint64_t>& seeked_to();
> +    virtual core::Signal<int64_t>& seeked_to();
>      virtual core::Signal<void>& end_of_stream();
>  
>    private:
>      struct Private;
> -    std::unique_ptr<Private> d;
> +    std::shared_ptr<Private> d;
>  };
>  }
>  }
> 
> === modified file 'src/core/media/player_stub.cpp'
> --- src/core/media/player_stub.cpp	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_stub.cpp	2014-09-10 07:05:35 +0000
> @@ -221,7 +221,7 @@
>  
>          PlaybackCompleteCb playback_complete_cb;
>          void *playback_complete_context;
> -        core::Signal<uint64_t> seeked_to;
> +        core::Signal<int64_t> seeked_to;
>          core::Signal<void> end_of_stream;
>          core::Signal<media::Player::PlaybackStatus> playback_status_changed;
>      } signals;
> @@ -411,12 +411,12 @@
>      return *d->properties.volume;
>  }
>  
> -const core::Property<uint64_t>& media::PlayerStub::position() const
> +const core::Property<int64_t>& media::PlayerStub::position() const
>  {
>      return *d->properties.position;
>  }
>  
> -const core::Property<uint64_t>& media::PlayerStub::duration() const
> +const core::Property<int64_t>& media::PlayerStub::duration() const
>  {
>      return *d->properties.duration;
>  }
> @@ -461,7 +461,7 @@
>      return *d->properties.audio_role;
>  }
>  
> -const core::Signal<uint64_t>& media::PlayerStub::seeked_to() const
> +const core::Signal<int64_t>& media::PlayerStub::seeked_to() const
>  {
>      return d->signals.seeked_to;
>  }
> 
> === modified file 'src/core/media/player_stub.h'
> --- src/core/media/player_stub.h	2014-09-10 07:05:35 +0000
> +++ src/core/media/player_stub.h	2014-09-10 07:05:35 +0000
> @@ -73,8 +73,8 @@
>      virtual const core::Property<Volume>& volume() const;
>      virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
>      virtual const core::Property<PlaybackRate>& maximum_playback_rate() const;
> -    virtual const core::Property<uint64_t>& position() const;
> -    virtual const core::Property<uint64_t>& duration() const;
> +    virtual const core::Property<int64_t>& position() const;
> +    virtual const core::Property<int64_t>& duration() const;
>      virtual const core::Property<AudioStreamRole>& audio_stream_role() const;
>  
>      virtual core::Property<LoopStatus>& loop_status();
> @@ -83,7 +83,7 @@
>      virtual core::Property<Volume>& volume();
>      virtual core::Property<AudioStreamRole>& audio_stream_role();
>  
> -    virtual const core::Signal<uint64_t>& seeked_to() const;
> +    virtual const core::Signal<int64_t>& seeked_to() const;
>      virtual const core::Signal<void>& end_of_stream() const;
>      virtual core::Signal<PlaybackStatus>& playback_status_changed();
>  
> 
> === modified file 'src/core/media/service_implementation.cpp'
> --- src/core/media/service_implementation.cpp	2014-09-10 07:05:35 +0000
> +++ src/core/media/service_implementation.cpp	2014-09-10 07:05:35 +0000
> @@ -23,9 +23,9 @@
>  #include "player_configuration.h"
>  #include "player_implementation.h"
>  
> +#include <cstdint>
>  #include <map>
>  #include <memory>
> -#include <stdint.h>
>  #include <thread>
>  
>  namespace media = core::ubuntu::media;
> @@ -34,11 +34,8 @@
>  
>  struct media::ServiceImplementation::Private
>  {
> -    typedef map<media::Player::PlayerKey, std::shared_ptr<media::Player>> player_map_t;
> -
>      Private()
> -        : key_(0),
> -          resume_key(UINT32_MAX)
> +        : resume_key(std::numeric_limits<std::uint32_t>::max())
>      {
>          bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
>          bus->install_executor(dbus::asio::make_executor(bus));
> @@ -52,22 +49,7 @@
>          auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power");
>          indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery"));
>          power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>();
> -        power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
> -        {
> -            // When the battery level hits 10% or 5%, pause all multimedia sessions.
> -            // Playback will resume when the user clears the presented notification.
> -            if (level == "low" || level == "very_low")
> -                pause_all_multimedia_sessions();
> -        });
> -
>          is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>();
> -        is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
> -        {
> -            // If the low battery level notification is no longer being displayed,
> -            // resume what the user was previously playing
> -            if (!notifying)
> -                resume_multimedia_session();
> -        });
>      }
>  
>      ~Private()
> @@ -78,80 +60,6 @@
>              worker.join();
>      }
>  
> -    void track_player(const std::shared_ptr<media::Player>& player)
> -    {
> -        player_map.insert(
> -                std::pair<media::Player::PlayerKey,
> -                std::shared_ptr<media::Player>>(key_, player));
> -
> -        ++key_;
> -    }
> -
> -    inline media::Player::PlayerKey key() const
> -    {
> -        return key_;
> -    }
> -
> -    void pause_other_sessions(media::Player::PlayerKey key)
> -    {
> -        auto player_it = player_map.find(key);
> -        if (player_it != player_map.end())
> -        {
> -            auto &current_player = (*player_it).second;
> -            for (auto& player_pair : player_map)
> -            {
> -                // Only pause a Player if all of the following criteria are met:
> -                // 1) currently playing
> -                // 2) not the same player as the one passed in my key
> -                // 3) new Player has an audio stream role set to multimedia
> -                // 4) has an audio stream role set to multimedia
> -                if (player_pair.second->playback_status() == Player::playing
> -                        && player_pair.first != key
> -                        && current_player->audio_stream_role() == media::Player::multimedia
> -                        && player_pair.second->audio_stream_role() == media::Player::multimedia)
> -                {
> -                    cout << "Pausing Player with key: " << player_pair.first << endl;
> -                    player_pair.second->pause();
> -                }
> -            }
> -        }
> -        else
> -            cerr << "Could not find Player by key: " << key << endl;
> -    }
> -
> -    // Pauses all multimedia audio stream role type Players
> -    void pause_all_multimedia_sessions()
> -    {
> -        for (auto& player_pair : player_map)
> -        {
> -            if (player_pair.second->playback_status() == Player::playing
> -                    && player_pair.second->audio_stream_role() == media::Player::multimedia)
> -            {
> -                resume_key = player_pair.first;
> -                cout << "Will resume playback of Player with key: " << resume_key << endl;
> -                player_pair.second->pause();
> -            }
> -        }
> -    }
> -
> -    void resume_multimedia_session()
> -    {
> -        auto player_it = player_map.find(resume_key);
> -        if (player_it != player_map.end())
> -        {
> -            auto &player = (*player_it).second;
> -            if (player->playback_status() == Player::paused)
> -            {
> -                cout << "Resuming playback of Player with key: " << resume_key << endl;
> -                player->play();
> -                resume_key = UINT32_MAX;
> -            }
> -        }
> -    }
> -
> -    // Used for Player instance management
> -    player_map_t player_map;
> -    media::Player::PlayerKey key_;
>      // This holds the key of the multimedia role Player instance that was paused
>      // when the battery level reached 10% or 5%
>      media::Player::PlayerKey resume_key;
> @@ -160,12 +68,27 @@
>      std::shared_ptr<dbus::Object> indicator_power_session;
>      std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level;
>      std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning;
> -
>  };
>  
>  media::ServiceImplementation::ServiceImplementation() : d(new Private())
>  {
>      cout << __PRETTY_FUNCTION__ << endl;
> +
> +    d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
> +    {
> +        // When the battery level hits 10% or 5%, pause all multimedia sessions.
> +        // Playback will resume when the user clears the presented notification.
> +        if (level == "low" || level == "very_low")
> +            pause_all_multimedia_sessions();
> +    });
> +
> +    d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
> +    {
> +        // If the low battery level notification is no longer being displayed,
> +        // resume what the user was previously playing
> +        if (!notifying)
> +            resume_multimedia_session();
> +    });
>  }
>  
>  media::ServiceImplementation::~ServiceImplementation()
> @@ -175,13 +98,75 @@
>  std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
>          const media::Player::Configuration& conf)
>  {
> -    std::shared_ptr<media::Player> player = std::make_shared<media::PlayerImplementation>(
> -            conf.bus, conf.session, shared_from_this(), d->key());
> -    d->track_player(player);
> +    auto player = std::make_shared<media::PlayerImplementation>(
> +            conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
> +
> +    auto key = conf.key;
> +    player->on_client_disconnected().connect([this, key]()
> +    {
> +        remove_player_for_key(key);
> +    });
> +
>      return player;
>  }
>  
>  void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
>  {
> -    d->pause_other_sessions(key);
> +    if (not has_player_for_key(key))
> +    {
> +        cerr << "Could not find Player by key: " << key << endl;
> +        return;
> +    }
> +
> +    auto current_player = player_for_key(key);
> +
> +    // We immediately make the player known as new current player.
> +    if (current_player->audio_stream_role() == media::Player::multimedia)
> +        set_current_player_for_key(key);
> +
> +    enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
> +    {
> +        // Only pause a Player if all of the following criteria are met:
> +        // 1) currently playing
> +        // 2) not the same player as the one passed in my key
> +        // 3) new Player has an audio stream role set to multimedia
> +        // 4) has an audio stream role set to multimedia
> +        if (other_player->playback_status() == Player::playing &&
> +            other_key != key &&
> +            current_player->audio_stream_role() == media::Player::multimedia &&
> +            other_player->audio_stream_role() == media::Player::multimedia)
> +        {
> +            cout << "Pausing Player with key: " << other_key << endl;
> +            other_player->pause();
> +        }
> +    });
> +}
> +
> +void media::ServiceImplementation::pause_all_multimedia_sessions()
> +{
> +    enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
> +                      {
> +                          if (player->playback_status() == Player::playing
> +                              && player->audio_stream_role() == media::Player::multimedia)
> +                          {
> +                              d->resume_key = key;
> +                              cout << "Will resume playback of Player with key: " << d->resume_key << endl;
> +                              player->pause();
> +                          }
> +                      });
> +}
> +
> +void media::ServiceImplementation::resume_multimedia_session()
> +{
> +    if (not has_player_for_key(d->resume_key))
> +        return;
> +
> +    auto player = player_for_key(d->resume_key);
> +
> +    if (player->playback_status() == Player::paused)
> +    {
> +        cout << "Resuming playback of Player with key: " << d->resume_key << endl;
> +        player->play();
> +        d->resume_key = std::numeric_limits<std::uint32_t>::max();
> +    }
>  }
> 
> === modified file 'src/core/media/service_implementation.h'
> --- src/core/media/service_implementation.h	2014-04-23 19:00:55 +0000
> +++ src/core/media/service_implementation.h	2014-09-10 07:05:35 +0000
> @@ -32,7 +32,7 @@
>  
>  class ServiceImplementation : public ServiceSkeleton
>  {
> -  public:
> +public:
>      ServiceImplementation ();
>      ~ServiceImplementation ();
>  
> @@ -40,7 +40,10 @@
>  
>      void pause_other_sessions(Player::PlayerKey key);
>  
> -  private:
> +private:
> +    void pause_all_multimedia_sessions();
> +    void resume_multimedia_session();
> +
>      struct Private;
>      std::shared_ptr<Private> d;
>  };
> 
> === modified file 'src/core/media/service_skeleton.cpp'
> --- src/core/media/service_skeleton.cpp	2014-09-10 07:05:35 +0000
> +++ src/core/media/service_skeleton.cpp	2014-09-10 07:05:35 +0000
> @@ -18,15 +18,24 @@
>  
>  #include "service_skeleton.h"
>  
> +#include "apparmor.h"
> +
> +#include "mpris/media_player2.h"
> +#include "mpris/metadata.h"
> +#include "mpris/player.h"
> +#include "mpris/playlists.h"
>  #include "mpris/service.h"
> +
>  #include "player_configuration.h"
>  #include "the_session_bus.h"
> +#include "xesam.h"
>  
>  #include <core/dbus/message.h>
>  #include <core/dbus/object.h>
>  #include <core/dbus/types/object_path.h>
>  
>  #include <map>
> +#include <regex>
>  #include <sstream>
>  
>  namespace dbus = core::dbus;
> @@ -34,15 +43,17 @@
>  
>  namespace
>  {
> -std::map<dbus::types::ObjectPath, std::shared_ptr<media::Player>> session_store;
> +core::Signal<void> the_empty_signal;
>  }
>  
>  struct media::ServiceSkeleton::Private
>  {
> -    Private(media::ServiceSkeleton* impl)
> +    Private(media::ServiceSkeleton* impl, const media::CoverArtResolver& resolver)
>          : impl(impl),
>            object(impl->access_service()->add_object_for_path(
> -                     dbus::traits::Service<media::Service>::object_path()))
> +                     dbus::traits::Service<media::Service>::object_path())),
> +          dbus_stub(impl->access_bus()),
> +          exported(impl->access_bus(), resolver)
>      {
>          object->install_method_handler<mpris::Service::CreateSession>(
>                      std::bind(
> @@ -64,35 +75,42 @@
>          ss << "/core/ubuntu/media/Service/sessions/" << session_counter++;
>  
>          dbus::types::ObjectPath op{ss.str()};
> -        media::Player::Configuration config
> -        {
> -            impl->access_bus(),
> -            impl->access_service()->add_object_for_path(op)
> -        };
> -
> -        try
> -        {
> -            auto session = impl->create_session(config);
> -
> -            bool inserted = false;
> -            std::tie(std::ignore, inserted)
> -                    = session_store.insert(std::make_pair(op, session));
> -
> -            if (!inserted)
> -                throw std::runtime_error("Problem persisting session in session store.");
> -
> -            auto reply = dbus::Message::make_method_return(msg);
> -            reply->writer() << op;
> -
> -            impl->access_bus()->send(reply);
> -        } catch(const std::runtime_error& e)
> -        {
> -            auto reply = dbus::Message::make_error(
> -                        msg,
> -                        mpris::Service::Errors::CreatingSession::name(),
> -                        e.what());
> -            impl->access_bus()->send(reply);
> -        }
> +        media::Player::PlayerKey key{session_counter};
> +
> +        dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile)
> +        {
> +            media::Player::Configuration config
> +            {
> +                profile,
> +                key,
> +                impl->access_bus(),
> +                impl->access_service()->add_object_for_path(op)
> +            };
> +
> +            try
> +            {
> +                auto session = impl->create_session(config);
> +
> +                bool inserted = false;
> +                std::tie(std::ignore, inserted)
> +                        = session_store.insert(std::make_pair(key, session));
> +
> +                if (!inserted)
> +                    throw std::runtime_error("Problem persisting session in session store.");
> +
> +                auto reply = dbus::Message::make_method_return(msg);
> +                reply->writer() << op;
> +
> +                impl->access_bus()->send(reply);
> +            } catch(const std::runtime_error& e)
> +            {
> +                auto reply = dbus::Message::make_error(
> +                            msg,
> +                            mpris::Service::Errors::CreatingSession::name(),
> +                            e.what());
> +                impl->access_bus()->send(reply);
> +            }
> +        });
>      }
>  
>      void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
> @@ -108,11 +126,271 @@
>  
>      media::ServiceSkeleton* impl;
>      dbus::Object::Ptr object;
> +
> +    // We query the apparmor profile to obtain an identity for players.
> +    org::freedesktop::dbus::DBus::Stub dbus_stub;
> +    // We track all running player instances.
> +    std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store;
> +    // We expose the entire service as an MPRIS player.
> +    struct Exported
> +    {
> +        static mpris::MediaPlayer2::Skeleton::Configuration::Defaults media_player_defaults()
> +        {
> +            mpris::MediaPlayer2::Skeleton::Configuration::Defaults defaults;
> +            // TODO(tvoss): These three elements really should be configurable.
> +            defaults.identity = "core::media::Hub";
> +            defaults.desktop_entry = "mediaplayer-app";
> +            defaults.supported_mime_types = {"audio/mpeg3"};
> +
> +            return defaults;
> +        }
> +
> +        static mpris::Player::Skeleton::Configuration::Defaults player_defaults()
> +        {
> +            mpris::Player::Skeleton::Configuration::Defaults defaults;
> +
> +            // Disabled as track list is not fully implemented yet.
> +            defaults.can_go_next = false;
> +            // Disabled as track list is not fully implemented yet.
> +            defaults.can_go_previous = false;
> +
> +            return defaults;
> +        }
> +
> +        explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
> +            : bus{bus},
> +              service{dbus::Service::add_service(bus, "org.mpris.MediaPlayer2.MediaHub")},
> +              object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})},
> +              media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}},
> +              player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}},
> +              playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}},
> +              cover_art_resolver{cover_art_resolver}
> +        {
> +            object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg)
> +            {
> +                // Extract the interface
> +                std::string itf; msg->reader() >> itf;
> +                core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg);
> +
> +                if (itf == mpris::Player::name())
> +                    reply->writer() << player.get_all_properties();
> +                else if (itf == mpris::MediaPlayer2::name())
> +                    reply->writer() << media_player.get_all_properties();
> +                else if (itf == mpris::Playlists::name())
> +                    reply->writer() << playlists.get_all_properties();
> +
> +                Exported::bus->send(reply);
> +            });
> +
> +            // Setup method handlers for mpris::Player methods.
> +            auto next = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                    sp->next();
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::Next>(next);
> +
> +            auto previous = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                    sp->previous();
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::Previous>(previous);
> +
> +            auto pause = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                    sp->pause();
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::Pause>(pause);
> +
> +            auto stop = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                    sp->stop();
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::Stop>(stop);
> +
> +            auto play = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                    sp->play();
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::Play>(play);
> +
> +            auto play_pause = [this](const core::dbus::Message::Ptr& msg)
> +            {
> +                auto sp = current_player.lock();
> +
> +                if (sp)
> +                {
> +                    if (sp->playback_status() == media::Player::PlaybackStatus::playing)
> +                        sp->pause();
> +                    else if (sp->playback_status() != media::Player::PlaybackStatus::null)
> +                        sp->play();
> +                }
> +
> +                Exported::bus->send(core::dbus::Message::make_method_return(msg));
> +            };
> +            object->install_method_handler<mpris::Player::PlayPause>(play_pause);
> +        }
> +
> +        void set_current_player(const std::shared_ptr<media::Player>& cp)
> +        {
> +            unset_current_player();
> +
> +            // We will not keep the object alive.
> +            current_player = cp;
> +
> +            // And announce that we can be controlled again.
> +            player.properties.can_control->set(false);
> +
> +            // We wire up player state changes
> +            connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position)
> +            {
> +                player.signals.seeked_to->emit(position);
> +            });
> +
> +            connections.duration_changed = cp->duration().changed().connect([this](std::uint64_t duration)
> +            {
> +                player.properties.duration->set(duration);
> +            });
> +
> +            connections.position_changed = cp->position().changed().connect([this](std::uint64_t position)
> +            {
> +                player.properties.position->set(position);
> +            });
> +
> +            connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status)
> +            {
> +                player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status));
> +            });
> +
> +            connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status)
> +            {
> +                player.properties.loop_status->set(mpris::Player::LoopStatus::from(status));
> +            });
> +
> +            connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md)
> +            {
> +                mpris::Player::Dictionary dict;
> +
> +                bool has_title = md.count(xesam::Title::name) > 0;
> +                bool has_album_name = md.count(xesam::Album::name) > 0;
> +                bool has_artist_name = md.count(xesam::Artist::name) > 0;
> +
> +                if (has_title)
> +                    dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
> +                if (has_album_name)
> +                    dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
> +                if (has_artist_name)
> +                    dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
> +
> +                dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
> +                            cover_art_resolver(
> +                                has_title ? md.get(xesam::Title::name) : "",
> +                                has_album_name ? md.get(xesam::Album::name) : "",
> +                                has_artist_name ? md.get(xesam::Artist::name) : ""));
> +
> +                mpris::Player::Dictionary wrap;
> +                wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
> +
> +                player.signals.properties_changed->emit(
> +                            std::make_tuple(
> +                                dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(),
> +                                wrap,
> +                                std::vector<std::string>()));
> +            });
> +        }
> +
> +        void unset_current_player()
> +        {
> +            current_player.reset();
> +
> +            // We disconnect all previous event connections.
> +            connections.seeked_to.disconnect();
> +            connections.duration_changed.disconnect();
> +            connections.position_changed.disconnect();
> +            connections.playback_status_changed.disconnect();
> +            connections.loop_status_changed.disconnect();
> +            connections.meta_data_changed.disconnect();
> +
> +            // And announce that we cannot be controlled anymore.
> +            player.properties.can_control->set(false);
> +        }
> +
> +        void unset_if_current(const std::shared_ptr<media::Player>& cp)
> +        {
> +            if (cp == current_player.lock())
> +                unset_current_player();
> +        }
> +
> +        dbus::Bus::Ptr bus;
> +        dbus::Service::Ptr service;
> +        dbus::Object::Ptr object;
> +
> +        mpris::MediaPlayer2::Skeleton media_player;
> +        mpris::Player::Skeleton player;
> +        mpris::Playlists::Skeleton playlists;
> +
> +        // Helper to resolve (title, artist, album) tuples to cover art.
> +        media::CoverArtResolver cover_art_resolver;
> +        // The actual player instance.
> +        std::weak_ptr<media::Player> current_player;
> +        // We track event connections.
> +        struct
> +        {
> +            core::Connection seeked_to
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +            core::Connection duration_changed
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +            core::Connection position_changed
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +            core::Connection playback_status_changed
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +            core::Connection loop_status_changed
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +            core::Connection meta_data_changed
> +            {
> +                the_empty_signal.connect([](){})
> +            };
> +        } connections;
> +    } exported;
>  };
>  
> -media::ServiceSkeleton::ServiceSkeleton()
> +media::ServiceSkeleton::ServiceSkeleton(const media::CoverArtResolver& resolver)
>      : dbus::Skeleton<media::Service>(the_session_bus()),
> -      d(new Private(this))
> +      d(new Private(this, resolver))
>  {
>  }
>  
> @@ -120,6 +398,41 @@
>  {
>  }
>  
> +bool media::ServiceSkeleton::has_player_for_key(const media::Player::PlayerKey& key) const
> +{
> +    return d->session_store.count(key) > 0;
> +}
> +
> +std::shared_ptr<media::Player> media::ServiceSkeleton::player_for_key(const media::Player::PlayerKey& key) const
> +{
> +    return d->session_store.at(key);
> +}
> +
> +void media::ServiceSkeleton::enumerate_players(const media::ServiceSkeleton::PlayerEnumerator& enumerator) const
> +{
> +    for (const auto& pair : d->session_store)
> +        enumerator(pair.first, pair.second);
> +}
> +
> +void media::ServiceSkeleton::set_current_player_for_key(const media::Player::PlayerKey& key)
> +{
> +    if (not has_player_for_key(key))
> +        return;
> +
> +    d->exported.set_current_player(player_for_key(key));
> +}
> +
> +void media::ServiceSkeleton::remove_player_for_key(const media::Player::PlayerKey& key)
> +{
> +    if (not has_player_for_key(key))
> +        return;
> +
> +    auto player = player_for_key(key);
> +
> +    d->session_store.erase(key);
> +    d->exported.unset_if_current(player);
> +}
> +
>  void media::ServiceSkeleton::run()
>  {
>      access_bus()->run();
> 
> === modified file 'src/core/media/service_skeleton.h'
> --- src/core/media/service_skeleton.h	2014-04-23 19:00:55 +0000
> +++ src/core/media/service_skeleton.h	2014-09-10 07:05:35 +0000
> @@ -21,6 +21,7 @@
>  
>  #include <core/media/service.h>
>  
> +#include "cover_art_resolver.h"
>  #include "service_traits.h"
>  
>  #include <core/dbus/skeleton.h>
> @@ -35,10 +36,36 @@
>  {
>  class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service>
>  {
> -  public:
> -    ServiceSkeleton();
> +public:
> +    // Functor for enumerating all known (key, player) pairs.
> +    typedef std::function
> +    <
> +        void(
> +            // The key of the player.
> +            const core::ubuntu::media::Player::PlayerKey&,
> +            // The actual player instance.
> +            const std::shared_ptr<core::ubuntu::media::Player>&
> +        )
> +    > PlayerEnumerator;
> +
> +    ServiceSkeleton(const CoverArtResolver& cover_art_resolver = always_missing_cover_art_resolver());
>      ~ServiceSkeleton();
>  
> +    // We keep track of all known player sessions here and render them accessible via
> +    // the key. All of these functions are thread-safe but not reentrant.
> +    // Returns true iff a player is known for the given key.
> +    bool has_player_for_key(const Player::PlayerKey& key) const;
> +    // Returns the player for the given key or throws std::out_of_range if no player is known
> +    // for the given key.
> +    std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const;
> +    // Enumerates all known players and invokes the given enumerator for each
> +    // (key, player) pair.
> +    void enumerate_players(const PlayerEnumerator& enumerator) const;
> +    // Removes the player for the given key, and unsets it if it is the current one.
> +    void remove_player_for_key(const Player::PlayerKey& key);
> +    // Makes the player known under the given key current.
> +    void set_current_player_for_key(const Player::PlayerKey& key);
> +
>      void run();
>      void stop();
>  
> 
> === modified file 'src/core/media/the_session_bus.cpp'
> --- src/core/media/the_session_bus.cpp	2014-02-12 15:53:57 +0000
> +++ src/core/media/the_session_bus.cpp	2014-09-10 07:05:35 +0000
> @@ -25,7 +25,7 @@
>  std::once_flag once;
>  }
>  
> -core::dbus::Bus::Ptr the_session_bus()
> +core::dbus::Bus::Ptr core::ubuntu::media::the_session_bus()
>  {
>      static core::dbus::Bus::Ptr bus
>              = std::make_shared<core::dbus::Bus>(
> 
> === modified file 'src/core/media/the_session_bus.h'
> --- src/core/media/the_session_bus.h	2014-02-12 15:53:57 +0000
> +++ src/core/media/the_session_bus.h	2014-09-10 07:05:35 +0000
> @@ -21,6 +21,14 @@
>  
>  #include <core/dbus/bus.h>
>  
> +namespace core
> +{
> +namespace ubuntu
> +{
> +namespace media
> +{
>  core::dbus::Bus::Ptr the_session_bus();
> -
> +}
> +}
> +}
>  #endif // THE_SESSION_BUS_H_
> 
> === added file 'src/core/media/xesam.h'
> --- src/core/media/xesam.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/xesam.h	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef XESAM_H_
> +#define XESAM_H_
> +
> +#include <cstdint>
> +
> +#include <string>
> +#include <vector>
> +
> +#define DATUM(Type, Name, VType) \
> +    struct Type \
> +    {\
> +        static constexpr const char* name{#Name};\
> +        typedef VType ValueType;\
> +    };
> +
> +namespace xesam
> +{
> +DATUM(Album, xesam:album, std::string)
> +DATUM(AlbumArtist, xesam:albumArtist, std::vector<std::string>)
> +DATUM(Artist, xesam:artist, std::vector<std::string>)
> +DATUM(AsText, xesam:asText, std::string)
> +DATUM(AudioBpm, xesam:audioBpm, std::int32_t)
> +DATUM(AutoRating, xesam:autoRating, double)
> +DATUM(Comment, xesam:comment, std::vector<std::string>)
> +DATUM(Composer, xesam:composer, std::vector<std::string>)
> +DATUM(ContentCreated, xesam:comment, std::string)
> +DATUM(DiscNumber, xesam:discNumber, std::int32_t)
> +DATUM(FirstUsed, xesam:firstUsed, std::string)
> +DATUM(Genre, xesam:genre, std::vector<std::string>)
> +DATUM(LastUsed, xesam:lastUsed, std::string)
> +DATUM(Lyricist, xesam:lyricist, std::vector<std::string>)
> +DATUM(Title, xesam:title, std::string)
> +DATUM(TrackNumber, xesam:trackNumber, std::int32_t)
> +DATUM(Url, xesam:url, std::string)
> +DATUM(UserRating, xesam:userRating, double)
> +}
> +
> +#endif // XESAM_H_
> 
> === added file 'symbols.map'
> --- symbols.map	1970-01-01 00:00:00 +0000
> +++ symbols.map	2014-09-10 07:05:35 +0000
> @@ -0,0 +1,16 @@
> +{
> +global:
> +    extern "C++" {
> +        core::ubuntu::media::*;
> +	typeinfo?for?core::ubuntu::media::*;
> +	typeinfo?name?for?core::ubuntu::media::*;
> +	VTT?for?core::ubuntu::media::*;
> +	virtual?thunk?to?core::ubuntu::media::*;
> +	vtable?for?core::ubuntu::media::*;
> +	std::hash*;
> +    };
> +local:
> +    extern "C++" {
> +        *;
> +    };
> +};
> 
> === modified file 'tests/unit-tests/CMakeLists.txt'
> --- tests/unit-tests/CMakeLists.txt	2014-04-08 19:49:20 +0000
> +++ tests/unit-tests/CMakeLists.txt	2014-09-10 07:05:35 +0000
> @@ -8,6 +8,7 @@
>      test-gstreamer-engine
>  
>      libmedia-mock.cpp
> +    ${CMAKE_SOURCE_DIR}/src/core/media/cover_art_resolver.cpp
>      ${CMAKE_SOURCE_DIR}/src/core/media/engine.cpp
>      ${CMAKE_SOURCE_DIR}/src/core/media/gstreamer/engine.cpp
>      ${CMAKE_SOURCE_DIR}/src/core/media/player_skeleton.cpp
> 
> === modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
> --- tests/unit-tests/test-gstreamer-engine.cpp	2014-06-24 20:37:44 +0000
> +++ tests/unit-tests/test-gstreamer-engine.cpp	2014-09-10 07:05:35 +0000
> @@ -20,6 +20,7 @@
>  #include <core/media/player.h>
>  #include <core/media/track_list.h>
>  
> +#include "core/media/xesam.h"
>  #include "core/media/gstreamer/engine.h"
>  
>  #include "../test_data.h"
> @@ -79,18 +80,18 @@
>      engine.track_meta_data().changed().connect(
>                  [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
>                  {
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::album()))
> -                        EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::album()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::album_artist()))
> -                        EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::album_artist()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::artist()))
> -                        EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::artist()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::disc_number()))
> -                        EXPECT_EQ("42", std::get<1>(md).get(media::Engine::Xesam::disc_number()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::genre()))
> -                        EXPECT_EQ("Test", std::get<1>(md).get(media::Engine::Xesam::genre()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::track_number()))
> -                        EXPECT_EQ("42", std::get<1>(md).get(media::Engine::Xesam::track_number()));
> +                    if (0 < std::get<1>(md).count(xesam::Album::name))
> +                        EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::Album::name));
> +                    if (0 < std::get<1>(md).count(xesam::AlbumArtist::name))
> +                        EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::AlbumArtist::name));
> +                    if (0 < std::get<1>(md).count(xesam::Artist::name))
> +                        EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::Artist::name));
> +                    if (0 < std::get<1>(md).count(xesam::DiscNumber::name))
> +                        EXPECT_EQ("42", std::get<1>(md).get(xesam::DiscNumber::name));
> +                    if (0 < std::get<1>(md).count(xesam::Genre::name))
> +                        EXPECT_EQ("Test", std::get<1>(md).get(xesam::Genre::name));
> +                    if (0 < std::get<1>(md).count(xesam::TrackNumber::name))
> +                        EXPECT_EQ("42", std::get<1>(md).get(xesam::TrackNumber::name));
>                  });
>  
>      engine.state().changed().connect(
> @@ -126,12 +127,12 @@
>      engine.track_meta_data().changed().connect(
>                  [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
>                  {
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::album()))
> -                        EXPECT_EQ("Test series", std::get<1>(md).get(media::Engine::Xesam::album()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::artist()))
> -                        EXPECT_EQ("Canonical", std::get<1>(md).get(media::Engine::Xesam::artist()));
> -                    if (0 < std::get<1>(md).count(media::Engine::Xesam::genre()))
> -                        EXPECT_EQ("Documentary", std::get<1>(md).get(media::Engine::Xesam::genre()));
> +                    if (0 < std::get<1>(md).count(xesam::Album::name))
> +                        EXPECT_EQ("Test series", std::get<1>(md).get(xesam::Album::name));
> +                    if (0 < std::get<1>(md).count(xesam::Artist::name))
> +                        EXPECT_EQ("Canonical", std::get<1>(md).get(xesam::Artist::name));
> +                    if (0 < std::get<1>(md).count(xesam::Genre::name))
> +                        EXPECT_EQ("Documentary", std::get<1>(md).get(xesam::Genre::name));
>                  });
>  
>      engine.state().changed().connect(
> @@ -342,17 +343,17 @@
>      gstreamer::Engine engine;
>      auto md = engine.meta_data_extractor()->meta_data_for_track_with_uri(test_file_uri);
>  
> -    if (0 < md.count(media::Engine::Xesam::album()))
> -        EXPECT_EQ("Test", md.get(media::Engine::Xesam::album()));
> -    if (0 < md.count(media::Engine::Xesam::album_artist()))
> -        EXPECT_EQ("Test", md.get(media::Engine::Xesam::album_artist()));
> -    if (0 < md.count(media::Engine::Xesam::artist()))
> -        EXPECT_EQ("Test", md.get(media::Engine::Xesam::artist()));
> -    if (0 < md.count(media::Engine::Xesam::disc_number()))
> -        EXPECT_EQ("42", md.get(media::Engine::Xesam::disc_number()));
> -    if (0 < md.count(media::Engine::Xesam::genre()))
> -        EXPECT_EQ("Test", md.get(media::Engine::Xesam::genre()));
> -    if (0 < md.count(media::Engine::Xesam::track_number()))
> -        EXPECT_EQ("42", md.get(media::Engine::Xesam::track_number()));
> +    if (0 < md.count(xesam::Album::name))
> +        EXPECT_EQ("Test", md.get(xesam::Album::name));
> +    if (0 < md.count(xesam::AlbumArtist::name))
> +        EXPECT_EQ("Test", md.get(xesam::AlbumArtist::name));
> +    if (0 < md.count(xesam::Artist::name))
> +        EXPECT_EQ("Test", md.get(xesam::Artist::name));
> +    if (0 < md.count(xesam::DiscNumber::name))
> +        EXPECT_EQ("42", md.get(xesam::DiscNumber::name));
> +    if (0 < md.count(xesam::Genre::name))
> +        EXPECT_EQ("Test", md.get(xesam::Genre::name));
> +    if (0 < md.count(xesam::TrackNumber::name))
> +        EXPECT_EQ("42", md.get(xesam::TrackNumber::name));
>  }
>  
> 


-- 
https://code.launchpad.net/~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator/+merge/232178
Your team Ubuntu Phablet Team is subscribed to branch lp:media-hub.



More information about the Ubuntu-reviews mailing list