[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 ¬ifying)
> - {
> - // 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 ¤t_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 ¬ifying)
> + {
> + // 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