[Merge] lp:~phablet-team/media-hub/tracklist-cli into lp:media-hub
Thomas Voß
thomas.voss at canonical.com
Fri Apr 17 13:43:18 UTC 2015
Review: Needs Information
I noticed the top-approve, still wanted to leave my comments.
Diff comments:
> === modified file 'include/core/media/player.h'
> --- include/core/media/player.h 2015-03-11 16:17:23 +0000
> +++ include/core/media/player.h 2015-04-15 17:14:17 +0000
> @@ -27,6 +27,7 @@
> #include <core/property.h>
>
> #include <chrono>
> +#include <iosfwd>
> #include <memory>
>
> namespace core
> @@ -149,7 +150,7 @@
> virtual const core::Property<PlaybackStatus>& playback_status() const = 0;
> virtual const core::Property<LoopStatus>& loop_status() const = 0;
> virtual const core::Property<PlaybackRate>& playback_rate() const = 0;
> - virtual const core::Property<bool>& is_shuffle() const = 0;
> + virtual const core::Property<bool>& shuffle() const = 0;
> virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const = 0;
> virtual const core::Property<Volume>& volume() const = 0;
> virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0;
> @@ -162,7 +163,7 @@
>
> virtual core::Property<LoopStatus>& loop_status() = 0;
> virtual core::Property<PlaybackRate>& playback_rate() = 0;
> - virtual core::Property<bool>& is_shuffle() = 0;
> + virtual core::Property<bool>& shuffle() = 0;
> virtual core::Property<Volume>& volume() = 0;
> virtual core::Property<AudioStreamRole>& audio_stream_role() = 0;
> virtual core::Property<Lifetime>& lifetime() = 0;
> @@ -178,6 +179,42 @@
> Player();
>
> };
> +
> +// operator<< pretty prints the given playback status to the given output stream.
> +inline std::ostream& operator<<(std::ostream& out, Player::PlaybackStatus status)
Might be a better idea to move this into an implementation file, and including iosfwd instead of iostream. See http://llvm.org/docs/CodingStandards.html#include-iostream-is-forbidden for reference.
> +{
> + switch (status)
> + {
> + case Player::PlaybackStatus::null:
> + return out << "PlaybackStatus::null";
> + case Player::PlaybackStatus::ready:
> + return out << "PlaybackStatus::ready";
> + case Player::PlaybackStatus::playing:
> + return out << "PlaybackStatus::playing";
> + case Player::PlaybackStatus::paused:
> + return out << "PlaybackStatus::paused";
> + case Player::PlaybackStatus::stopped:
> + return out << "PlaybackStatus::stopped";
> + }
> +
> + return out;
> +}
> +
> +inline std::ostream& operator<<(std::ostream& out, Player::LoopStatus loop_status)
See before.
> +{
> + switch (loop_status)
> + {
> + case Player::LoopStatus::none:
> + return out << "LoopStatus::none";
> + case Player::LoopStatus::track:
> + return out << "LoopStatus::track";
> + case Player::LoopStatus::playlist:
> + return out << "LoopStatus::playlist";
> + }
> +
> + return out;
> +}
> +
> }
> }
> }
>
> === modified file 'include/core/media/track_list.h'
> --- include/core/media/track_list.h 2014-02-12 15:53:57 +0000
> +++ include/core/media/track_list.h 2015-04-15 17:14:17 +0000
> @@ -24,7 +24,8 @@
> #include <core/signal.h>
>
> #include <functional>
> -#include <list>
> +#include <iosfwd>
> +#include <vector>
> #include <memory>
>
> namespace core
> @@ -39,6 +40,7 @@
> {
> public:
> typedef std::vector<Track::Id> Container;
> + typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple;
> typedef Container::iterator Iterator;
> typedef Container::const_iterator ConstIterator;
>
> @@ -50,23 +52,54 @@
> TrackList& operator=(const TrackList&) = delete;
> bool operator==(const TrackList&) const = delete;
>
> + /** If set to false, calling add_track_with_uri_at or remove_track will have no effect. */
> virtual const core::Property<bool>& can_edit_tracks() const = 0;
> +
> + /** An array which contains the identifier of each track in the tracklist, in order. */
> virtual const core::Property<Container>& tracks() const = 0;
>
> + /** Gets all the metadata available for a given Track. */
> virtual Track::MetaData query_meta_data_for_track(const Track::Id& id) = 0;
> +
> + /** Adds a URI in the TrackList. */
> virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0;
> +
> + /** Removes a Track from the TrackList. */
> virtual void remove_track(const Track::Id& id) = 0;
>
> + /** Skip to the specified TrackId. */
> virtual void go_to(const Track::Id& track) = 0;
>
> - virtual const core::Signal<void>& on_track_list_replaced() const = 0;
> +
> + /** Reorders the tracks such that they are in a random order. */
> + virtual void shuffle_tracks() = 0;
> +
> + /** Restores the original order of tracks before shuffle mode was turned on. */
> + virtual void unshuffle_tracks() = 0;
> +
> + /** Clears and resets the TrackList to the same as a newly constructed instance. */
> + virtual void reset() = 0;
> +
> +
> + /** Indicates that the entire tracklist has been replaced. */
> + virtual const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const = 0;
> +
> + /** Indicates that a track has been added to the track list. */
> virtual const core::Signal<Track::Id>& on_track_added() const = 0;
> +
> + /** Indicates that a track has been removed from the track list. */
> virtual const core::Signal<Track::Id>& on_track_removed() const = 0;
> +
> + /** Indicates that the track list advanced from one track to another. */
> virtual const core::Signal<Track::Id>& on_track_changed() const = 0;
>
> + /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */
> + virtual const core::Signal<Track::Id>& on_go_to_track() const = 0;
> +
> protected:
> TrackList();
> };
> +
> }
> }
> }
>
> === modified file 'src/core/media/codec.h'
> --- src/core/media/codec.h 2015-01-26 09:23:41 +0000
> +++ src/core/media/codec.h 2015-04-15 17:14:17 +0000
> @@ -23,6 +23,8 @@
> #include <core/media/player.h>
> #include <core/media/track.h>
>
> +#include <core/dbus/types/stl/string.h>
> +#include <core/dbus/types/stl/vector.h>
> #include <core/dbus/codec.h>
>
> namespace core
>
> === modified file 'src/core/media/engine.h'
> --- src/core/media/engine.h 2015-01-26 09:23:41 +0000
> +++ src/core/media/engine.h 2015-04-15 17:14:17 +0000
> @@ -38,6 +38,7 @@
>
> enum class State
> {
> + no_media,
> ready,
> busy,
> playing,
> @@ -75,7 +76,7 @@
>
> virtual const core::Property<State>& state() const = 0;
>
> - virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri) = 0;
> + virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset) = 0;
> virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const Player::HeadersType&) = 0;
> // Throws core::ubuntu::media::Player::Error::OutOfProcessBufferStreamingNotSupported if the implementation does not
> // support this feature.
>
> === modified file 'src/core/media/gstreamer/engine.cpp'
> --- src/core/media/gstreamer/engine.cpp 2015-03-11 16:17:23 +0000
> +++ src/core/media/gstreamer/engine.cpp 2015-04-15 17:14:17 +0000
> @@ -313,12 +313,13 @@
> gstreamer::Engine::Engine() : d(new Private{})
> {
> cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;
> - d->state = media::Engine::State::ready;
> + d->state = media::Engine::State::no_media;
> }
>
> gstreamer::Engine::~Engine()
> {
> stop();
> + d->state = media::Engine::State::no_media;
> }
>
> const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const
> @@ -331,9 +332,9 @@
> return d->state;
> }
>
> -bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri)
> +bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri, bool do_pipeline_reset)
> {
> - d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{});
> + d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}, do_pipeline_reset);
> return true;
> }
>
> @@ -356,6 +357,7 @@
> {
> d->state = media::Engine::State::playing;
> cout << __PRETTY_FUNCTION__ << endl;
> + cout << "Engine: playing uri: " << d->playbin.uri() << endl;
> d->playback_status_changed(media::Player::PlaybackStatus::playing);
> }
>
>
> === modified file 'src/core/media/gstreamer/engine.h'
> --- src/core/media/gstreamer/engine.h 2015-01-26 09:23:41 +0000
> +++ src/core/media/gstreamer/engine.h 2015-04-15 17:14:17 +0000
> @@ -33,7 +33,7 @@
>
> const core::Property<State>& state() const;
>
> - bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri);
> + bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset);
> bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const core::ubuntu::media::Player::HeadersType& headers);
> void create_video_sink(uint32_t texture_id);
>
>
> === modified file 'src/core/media/gstreamer/meta_data_extractor.h'
> --- src/core/media/gstreamer/meta_data_extractor.h 2014-10-14 20:05:20 +0000
> +++ src/core/media/gstreamer/meta_data_extractor.h 2015-04-15 17:14:17 +0000
> @@ -177,7 +177,7 @@
> bus.on_new_message.connect(
> [&](const gstreamer::Bus::Message& msg)
> {
> - std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
> + //std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
Is that a left-over or is it intentionally commented out? If so, could we just remove it?
> if (msg.type == GST_MESSAGE_TAG)
> {
> MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
>
> === modified file 'src/core/media/gstreamer/playbin.cpp'
> --- src/core/media/gstreamer/playbin.cpp 2015-03-11 16:17:23 +0000
> +++ src/core/media/gstreamer/playbin.cpp 2015-04-15 17:14:17 +0000
> @@ -24,6 +24,8 @@
> #include <hybris/media/surface_texture_client_hybris.h>
> #include <hybris/media/media_codec_layer.h>
>
> +#include <utility>
> +
> namespace
> {
> void setup_video_sink_for_buffer_streaming(GstElement* video_sink)
> @@ -365,9 +367,11 @@
>
> void gstreamer::Playbin::set_uri(
> const std::string& uri,
> - const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType())
> + const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType(),
Does this actually compile despite the default argument being provided here?
> + bool do_pipeline_reset)
> {
> - reset_pipeline();
> + if (do_pipeline_reset)
> + reset_pipeline();
>
> g_object_set(pipeline, "uri", uri.c_str(), NULL);
> if (is_video_file(uri))
>
> === modified file 'src/core/media/gstreamer/playbin.h'
> --- src/core/media/gstreamer/playbin.h 2015-03-11 16:17:23 +0000
> +++ src/core/media/gstreamer/playbin.h 2015-04-15 17:14:17 +0000
> @@ -89,11 +89,13 @@
> uint64_t position() const;
> uint64_t duration() const;
>
> - void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers);
> + void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers, bool do_pipeline_reset = true);
> std::string uri() const;
>
> void setup_source(GstElement *source);
>
> + // Sets the pipeline's state (stopped, playing, paused, etc). Optional parameter makes this call
> + // in the main_loop context.
> bool set_state_and_wait(GstState new_state);
> bool seek(const std::chrono::microseconds& ms);
>
>
> === modified file 'src/core/media/mpris/player.h'
> --- src/core/media/mpris/player.h 2015-03-11 16:17:23 +0000
> +++ src/core/media/mpris/player.h 2015-04-15 17:14:17 +0000
> @@ -271,7 +271,7 @@
> properties.orientation->set(core::ubuntu::media::Player::Orientation::rotate0);
> properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal);
> properties.playback_rate->set(configuration.defaults.playback_rate);
> - properties.is_shuffle->set(configuration.defaults.shuffle);
> + properties.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);
> @@ -302,6 +302,11 @@
> {
> on_property_value_changed<Properties::LoopStatus>(status);
> });
> +
> + properties.shuffle->changed().connect([this](bool shuffle)
> + {
> + on_property_value_changed<Properties::Shuffle>(shuffle);
> + });
> }
>
> template<typename Property>
> @@ -332,7 +337,7 @@
> dict[Properties::Orientation::name()] = dbus::types::Variant::encode(properties.orientation->get());
> dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->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::Shuffle::name()] = dbus::types::Variant::encode(properties.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());
> @@ -363,7 +368,7 @@
> std::shared_ptr<core::dbus::Property<Properties::Orientation>> orientation;
> std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime;
> 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::Shuffle>> 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;
>
> === modified file 'src/core/media/mpris/track_list.h'
> --- src/core/media/mpris/track_list.h 2014-08-25 10:18:32 +0000
> +++ src/core/media/mpris/track_list.h 2015-04-15 17:14:17 +0000
> @@ -22,7 +22,10 @@
> #include <core/dbus/macros.h>
>
> #include <core/dbus/types/any.h>
> +#include <core/dbus/macros.h>
> #include <core/dbus/types/object_path.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/property.h>
> #include <core/dbus/types/variant.h>
>
> #include <boost/utility/identity_type.hpp>
> @@ -37,38 +40,42 @@
> {
> struct TrackList
> {
> + typedef std::map<std::string, core::dbus::types::Variant> Dictionary;
> +
> static const std::string& name()
> {
> - static const std::string s{"core.ubuntu.media.Service.Player.TrackList"};
> + static const std::string s{"org.mpris.MediaPlayer2.TrackList"};
> return s;
> }
>
> - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GetTracksMetadata, TrackList, 1000)
> - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(AddTrack, TrackList, 1000)
> - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(RemoveTrack, TrackList, 1000)
> - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GoTo, TrackList, 1000)
> + DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList)
> + DBUS_CPP_METHOD_DEF(AddTrack, TrackList)
> + DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList)
> + DBUS_CPP_METHOD_DEF(GoTo, TrackList)
>
> struct Signals
> {
> + Signals() = delete;
> +
> DBUS_CPP_SIGNAL_DEF
> (
> TrackListReplaced,
> TrackList,
> - BOOST_IDENTITY_TYPE((std::tuple<std::vector<dbus::types::ObjectPath>, dbus::types::ObjectPath>))
> + BOOST_IDENTITY_TYPE((std::tuple<std::vector<core::ubuntu::media::Track::Id>, core::ubuntu::media::Track::Id>))
> )
>
> DBUS_CPP_SIGNAL_DEF
> (
> TrackAdded,
> TrackList,
> - BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>))
> + core::ubuntu::media::Track::Id
> )
>
> DBUS_CPP_SIGNAL_DEF
> (
> TrackRemoved,
> TrackList,
> - dbus::types::ObjectPath
> + core::ubuntu::media::Track::Id
> )
>
> DBUS_CPP_SIGNAL_DEF
> @@ -81,9 +88,94 @@
>
> struct Properties
> {
> - DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<std::string>)
> + Properties() = delete;
> +
> + DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<core::ubuntu::media::Track::Id>)
> DBUS_CPP_READABLE_PROPERTY_DEF(CanEditTracks, TrackList, bool)
> };
> +
> + struct Skeleton
> + {
> + static const std::vector<std::string>& the_empty_list_of_invalidated_properties()
> + {
> + static const std::vector<std::string> instance; return instance;
> + }
> +
> + // Object instance creation time properties go here.
> + struct Configuration
> + {
> + // The dbus object that should implement org.mpris.MediaPlayer2
> + core::dbus::Object::Ptr object;
> + // Default values assigned to exported dbus interface properties on construction
> + struct Defaults
> + {
> + Properties::Tracks::ValueType tracks{std::vector<core::ubuntu::media::Track::Id>()};
> + Properties::CanEditTracks::ValueType can_edit_tracks{true};
> + } defaults;
> + };
> +
> + Skeleton(const Configuration& configuration)
> + : configuration(configuration),
> + properties
> + {
> + configuration.object->get_property<Properties::Tracks>(),
> + configuration.object->get_property<Properties::CanEditTracks>(),
> + },
> + signals
> + {
> + configuration.object->get_signal<Signals::TrackListReplaced>(),
> + configuration.object->get_signal<Signals::TrackAdded>(),
> + configuration.object->get_signal<Signals::TrackRemoved>(),
> + configuration.object->get_signal<Signals::TrackMetadataChanged>(),
> + configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
> + }
> + {
> + // Set the default value of the properties on the MPRIS TrackList dbus interface
> + properties.tracks->set(configuration.defaults.tracks);
> + properties.can_edit_tracks->set(configuration.defaults.can_edit_tracks);
> + }
> +
> + 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<TrackList>::interface_name(),
> + dict,
> + the_empty_list_of_invalidated_properties()));
> + }
> +
> + std::map<std::string, core::dbus::types::Variant> get_all_properties()
> + {
> + std::map<std::string, core::dbus::types::Variant> dict;
> + dict[Properties::Tracks::name()] = core::dbus::types::Variant::encode(properties.tracks->get());
> + dict[Properties::CanEditTracks::name()] = core::dbus::types::Variant::encode(properties.can_edit_tracks->get());
> +
> + return dict;
> + }
> +
> + Configuration configuration;
> +
> + struct
> + {
> + std::shared_ptr<core::dbus::Property<Properties::Tracks>> tracks;
> + std::shared_ptr<core::dbus::Property<Properties::CanEditTracks>> can_edit_tracks;
> + } properties;
> +
> + struct
> + {
> + core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced;
> + core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added;
> + core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed;
> + core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed;
> +
> + dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged,
> + core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
> + >::Ptr properties_changed;
> + } signals;
> + };
> };
> }
>
>
> === modified file 'src/core/media/player_implementation.cpp'
> --- src/core/media/player_implementation.cpp 2015-03-19 19:08:47 +0000
> +++ src/core/media/player_implementation.cpp 2015-04-15 17:14:17 +0000
> @@ -1,4 +1,5 @@
> /*
> + * Copyright © 2013-2015 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,
> @@ -23,7 +24,6 @@
>
> #include "client_death_observer.h"
> #include "engine.h"
> -#include "null_track_list.h"
> #include "track_list_implementation.h"
>
> #include "gstreamer/engine.h"
> @@ -58,14 +58,16 @@
> display_state_lock(config.power_state_controller->display_state_lock()),
> system_state_lock(config.power_state_controller->system_state_lock()),
> engine(std::make_shared<gstreamer::Engine>()),
> - track_list(std::make_shared<NullTrackList>()),
> + track_list(std::make_shared<TrackListImplementation>(
> + dbus::types::ObjectPath(config.parent.session->path().as_string() + "/TrackList"),
> + engine->meta_data_extractor())),
> system_wakelock_count(0),
> display_wakelock_count(0),
> previous_state(Engine::State::stopped),
> engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())),
> - engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler()))
> + engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())),
> + doing_go_to_track(false)
> {
> - std::cout << "Private parent instance: " << parent << std::endl;
> // Poor man's logging of release/acquire events.
> display_state_lock->acquired().connect([](media::power::DisplayState state)
> {
> @@ -99,7 +101,6 @@
> // by disconnecting the state change signal
> engine_state_change_connection.disconnect();
>
> - std::cout << "** Disconnecting playback_status_changed_signal connection";
> // The engine destructor can lead to a playback status change which will
> // trigger the playback status change handler. Ensure the handler is not called
> // by disconnecting the playback status change signal
> @@ -169,7 +170,7 @@
> {
> return [this](const media::Player::PlaybackStatus& status)
> {
> - std::cout << "Emiting playback_status_changed for parent: " << parent << std::endl;
> + std::cout << "Emiting playback_status_changed signal: " << status << std::endl;
> parent->emit_playback_status_changed(status);
> };
> }
> @@ -290,13 +291,15 @@
> media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
>
> std::shared_ptr<Engine> engine;
> - std::shared_ptr<media::NullTrackList> track_list;
> + std::shared_ptr<media::TrackListImplementation> track_list;
> std::atomic<int> system_wakelock_count;
> std::atomic<int> display_wakelock_count;
> Engine::State previous_state;
> core::Signal<> on_client_disconnected;
> core::Connection engine_state_change_connection;
> core::Connection engine_playback_status_change_connection;
> + // Prevent the TrackList from auto advancing to the next track
> + std::atomic<bool> doing_go_to_track;
> };
>
> template<typename Parent>
> @@ -312,7 +315,7 @@
> Parent::can_go_next().set(true);
> Parent::is_video_source().set(false);
> Parent::is_audio_source().set(false);
> - Parent::is_shuffle().set(true);
> + Parent::shuffle().set(false);
> Parent::playback_rate().set(1.f);
> Parent::playback_status().set(Player::PlaybackStatus::null);
> Parent::loop_status().set(Player::LoopStatus::none);
> @@ -352,6 +355,18 @@
> };
> Parent::is_audio_source().install(audio_type_getter);
>
> + // When the client changes the loop status, make sure to update the TrackList
> + Parent::loop_status().changed().connect([this](media::Player::LoopStatus loop_status)
> + {
> + d->track_list->on_loop_status_changed(loop_status);
> + });
> +
> + // When the client changes the shuffle setting, make sure to update the TrackList
> + Parent::shuffle().changed().connect([this](bool shuffle)
> + {
> + d->track_list->on_shuffle_changed(shuffle);
> + });
> +
> // Make sure that the audio_stream_role property gets updated on the Engine side
> // whenever the client side sets the role
> Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
> @@ -375,12 +390,21 @@
> {
> Parent::about_to_finish()();
>
> - if (d->track_list->has_next())
> + if (!d->doing_go_to_track)
> {
> - Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
> - if (!uri.empty())
> - d->parent->open_uri(uri);
> + const media::Track::Id prev_track_id = d->track_list->current();
> + // Make sure that the TrackList keeps advancing. The logic for what gets played next,
> + // if anything at all, occurs in TrackListSkeleton::next()
> + const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
> + if (prev_track_id != d->track_list->current() && !uri.empty())
> + {
> + std::cout << "Setting next track on playbin: " << uri << std::endl;
> + static const bool do_pipeline_reset = false;
> + d->engine->open_resource_for_uri(uri, do_pipeline_reset);
> + }
> }
> + else
> + std::cout << "Not auto-advancing the TrackList since doing_go_to_track is true" << std::endl;
> });
>
> d->engine->client_disconnected_signal().connect([this]()
> @@ -388,6 +412,7 @@
> // If the client disconnects, make sure both wakelock types
> // are cleared
> d->clear_wakelocks();
> + d->track_list->reset();
> // And tell the outside world that the client has gone away
> d->on_client_disconnected();
> });
> @@ -412,6 +437,28 @@
> Parent::error()(e);
> });
>
> + d->track_list->on_go_to_track().connect([this](const media::Track::Id& id)
> + {
> + // This prevents the TrackList from auto advancing in other areas such as the about_to_finish signal
> + // handler.
> + d->doing_go_to_track = true;
> +
> + d->engine->stop();
> +
> + const Track::UriType uri = d->track_list->query_uri_for_track(id);
> + if (!uri.empty())
> + {
> + std::cout << "Setting next track on playbin (on_go_to_track signal): " << uri << std::endl;
> + std::cout << "\twith a Track::Id: " << id << std::endl;
> + static const bool do_pipeline_reset = true;
> + d->engine->open_resource_for_uri(uri, do_pipeline_reset);
> + }
> +
> + d->engine->play();
> +
> + d->doing_go_to_track = false;
> + });
> +
> // Everything is setup, we now subscribe to death notifications.
> std::weak_ptr<Private> wp{d};
>
> @@ -487,7 +534,12 @@
> template<typename Parent>
> bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)
> {
> - return d->engine->open_resource_for_uri(uri);
> + if (d->track_list != nullptr)
> + // Set new track as the current track to play
> + d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), true);
> +
> + static const bool do_pipeline_reset = true;
Might make sense to define these constants in an anonymous namespace at the beginning of the implementation file.
> + return d->engine->open_resource_for_uri(uri, do_pipeline_reset);
> }
>
> template<typename Parent>
> @@ -509,6 +561,16 @@
> template<typename Parent>
> void media::PlayerImplementation<Parent>::play()
> {
> + if (d->track_list != nullptr && d->track_list->tracks()->size() > 0 && d->engine->state() == media::Engine::State::no_media)
> + {
> + // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet
> + // to load a media resource
> + std::cout << "No media loaded yet, calling open_uri on first track in track_list" << std::endl;
> + static const bool do_pipeline_reset = true;
> + d->engine->open_resource_for_uri(d->track_list->query_uri_for_track(d->track_list->current()), do_pipeline_reset);
> + std::cout << *d->track_list << endl;
> + }
> +
> d->engine->play();
> }
>
>
> === modified file 'src/core/media/player_implementation.h'
> --- src/core/media/player_implementation.h 2015-03-19 19:08:47 +0000
> +++ src/core/media/player_implementation.h 2015-04-15 17:14:17 +0000
> @@ -78,8 +78,8 @@
> struct Private;
> std::shared_ptr<Private> d;
> };
> +
> }
> }
> }
> #endif // CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
> -
>
> === modified file 'src/core/media/player_skeleton.cpp'
> --- src/core/media/player_skeleton.cpp 2015-03-11 16:17:23 +0000
> +++ src/core/media/player_skeleton.cpp 2015-04-15 17:14:17 +0000
> @@ -407,9 +407,9 @@
> return *d->skeleton.properties.playback_rate;
> }
>
> -const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const
> +const core::Property<bool>& media::PlayerSkeleton::shuffle() const
> {
> - return *d->skeleton.properties.is_shuffle;
> + return *d->skeleton.properties.shuffle;
> }
>
> const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
> @@ -467,9 +467,9 @@
> return *d->skeleton.properties.playback_rate;
> }
>
> -core::Property<bool>& media::PlayerSkeleton::is_shuffle()
> +core::Property<bool>& media::PlayerSkeleton::shuffle()
> {
> - return *d->skeleton.properties.is_shuffle;
> + return *d->skeleton.properties.shuffle;
> }
>
> core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
>
> === modified file 'src/core/media/player_skeleton.h'
> --- src/core/media/player_skeleton.h 2015-03-11 16:17:23 +0000
> +++ src/core/media/player_skeleton.h 2015-04-15 17:14:17 +0000
> @@ -73,7 +73,7 @@
> virtual const core::Property<PlaybackStatus>& playback_status() const;
> virtual const core::Property<LoopStatus>& loop_status() const;
> virtual const core::Property<PlaybackRate>& playback_rate() const;
> - virtual const core::Property<bool>& is_shuffle() const;
> + virtual const core::Property<bool>& shuffle() const;
> virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;
> virtual const core::Property<Volume>& volume() const;
> virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
> @@ -86,7 +86,7 @@
>
> virtual core::Property<LoopStatus>& loop_status();
> virtual core::Property<PlaybackRate>& playback_rate();
> - virtual core::Property<bool>& is_shuffle();
> + virtual core::Property<bool>& shuffle();
> virtual core::Property<Volume>& volume();
> virtual core::Property<AudioStreamRole>& audio_stream_role();
> virtual core::Property<Lifetime>& lifetime();
>
> === modified file 'src/core/media/player_stub.cpp'
> --- src/core/media/player_stub.cpp 2015-03-16 14:54:47 +0000
> +++ src/core/media/player_stub.cpp 2015-04-15 17:14:17 +0000
> @@ -108,7 +108,7 @@
> 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::Shuffle>> 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;
> @@ -153,7 +153,7 @@
> {
> dbus.seeked_to->connect([this](std::uint64_t value)
> {
> - std::cout << "seeked_to signal arrived via the bus." << std::endl;
> + std::cout << "SeekedTo signal arrived via the bus." << std::endl;
> seeked_to(value);
> });
>
> @@ -362,9 +362,9 @@
> return *d->properties.playback_rate;
> }
>
> -const core::Property<bool>& media::PlayerStub::is_shuffle() const
> +const core::Property<bool>& media::PlayerStub::shuffle() const
> {
> - return *d->properties.is_shuffle;
> + return *d->properties.shuffle;
> }
>
> const core::Property<media::Track::MetaData>& media::PlayerStub::meta_data_for_current_track() const
> @@ -422,9 +422,9 @@
> return *d->properties.playback_rate;
> }
>
> -core::Property<bool>& media::PlayerStub::is_shuffle()
> +core::Property<bool>& media::PlayerStub::shuffle()
> {
> - return *d->properties.is_shuffle;
> + return *d->properties.shuffle;
> }
>
> core::Property<media::Player::Volume>& media::PlayerStub::volume()
>
> === modified file 'src/core/media/player_stub.h'
> --- src/core/media/player_stub.h 2015-03-11 16:17:23 +0000
> +++ src/core/media/player_stub.h 2015-04-15 17:14:17 +0000
> @@ -67,7 +67,7 @@
> virtual const core::Property<PlaybackStatus>& playback_status() const;
> virtual const core::Property<LoopStatus>& loop_status() const;
> virtual const core::Property<PlaybackRate>& playback_rate() const;
> - virtual const core::Property<bool>& is_shuffle() const;
> + virtual const core::Property<bool>& shuffle() const;
> virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;
> virtual const core::Property<Volume>& volume() const;
> virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
> @@ -80,7 +80,7 @@
>
> virtual core::Property<LoopStatus>& loop_status();
> virtual core::Property<PlaybackRate>& playback_rate();
> - virtual core::Property<bool>& is_shuffle();
> + virtual core::Property<bool>& shuffle();
> virtual core::Property<Volume>& volume();
> virtual core::Property<AudioStreamRole>& audio_stream_role();
> virtual core::Property<Lifetime>& lifetime();
>
> === modified file 'src/core/media/service_implementation.cpp'
> --- src/core/media/service_implementation.cpp 2015-04-02 14:46:52 +0000
> +++ src/core/media/service_implementation.cpp 2015-04-15 17:14:17 +0000
> @@ -53,6 +53,8 @@
>
> struct media::ServiceImplementation::Private
> {
> + // Create all of the appropriate observers and helper class instances to be
> + // passed to the PlayerImplementation
> Private(const ServiceImplementation::Configuration& configuration)
> : configuration(configuration),
> resume_key(std::numeric_limits<std::uint32_t>::max()),
> @@ -72,7 +74,7 @@
> media::ServiceImplementation::Configuration configuration;
> // 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;
> + media::Player::PlayerKey resume_key;
> media::power::BatteryObserver::Ptr battery_observer;
> media::power::StateController::Ptr power_state_controller;
> media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
> @@ -262,17 +264,17 @@
> void media::ServiceImplementation::pause_all_multimedia_sessions(bool resume_play_after_phonecall)
> {
> d->configuration.player_store->enumerate_players([this, resume_play_after_phonecall](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)
> - {
> - auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall);
> - d->paused_sessions.push_back(paused_player_pair);
> - std::cout << "Pausing Player with key: " << key << ", resuming after phone call? "
> - << (resume_play_after_phonecall ? "yes" : "no") << std::endl;
> - player->pause();
> - }
> - });
> + {
> + if (player->playback_status() == Player::playing
> + && player->audio_stream_role() == media::Player::multimedia)
> + {
> + auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall);
> + d->paused_sessions.push_back(paused_player_pair);
> + std::cout << "Pausing Player with key: " << key << ", resuming after phone call? "
> + << (resume_play_after_phonecall ? "yes" : "no") << std::endl;
> + player->pause();
> + }
> + });
> }
>
> void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)
>
> === modified file 'src/core/media/service_skeleton.cpp'
> --- src/core/media/service_skeleton.cpp 2014-12-15 14:43:44 +0000
> +++ src/core/media/service_skeleton.cpp 2015-04-15 17:14:17 +0000
> @@ -52,7 +52,7 @@
> Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config)
> : impl(impl),
> object(impl->access_service()->add_object_for_path(
> - dbus::traits::Service<media::Service>::object_path())),
> + dbus::traits::Service<media::Service>::object_path())),
> exported(impl->access_bus(), config.cover_art_resolver),
> configuration(config)
> {
> @@ -266,6 +266,7 @@
>
> static std::string service_name()
> {
> +#if 0
> static const bool export_to_indicator_sound_via_mpris
> {
> core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1"
> @@ -273,6 +274,9 @@
>
> return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" :
> "hidden.org.mpris.MediaPlayer2.MediaHub";
> +#else
> + return "org.mpris.MediaPlayer2.MediaHub";
> +#endif
> }
>
> explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
>
> === modified file 'src/core/media/track_list_implementation.cpp'
> --- src/core/media/track_list_implementation.cpp 2014-03-25 14:57:15 +0000
> +++ src/core/media/track_list_implementation.cpp 2015-04-15 17:14:17 +0000
> @@ -1,5 +1,5 @@
> /*
> - * Copyright © 2013 Canonical Ltd.
> + * Copyright © 2013-2015 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,
> @@ -16,8 +16,10 @@
> * Authored by: Thomas Voß <thomas.voss at canonical.com>
> */
>
> +#include <algorithm>
> #include <stdio.h>
> #include <stdlib.h>
> +#include <tuple>
>
> #include "track_list_implementation.h"
>
> @@ -31,15 +33,19 @@
> typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache;
>
> dbus::types::ObjectPath path;
> + size_t track_counter;
> MetaDataCache meta_data_cache;
> std::shared_ptr<media::Engine::MetaDataExtractor> extractor;
> + // Used for caching the original tracklist order to be used to restore the order
> + // to the live TrackList after shuffle is turned off
> + media::TrackList::Container original_tracklist;
> };
>
> media::TrackListImplementation::TrackListImplementation(
> const dbus::types::ObjectPath& op,
> const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor)
> : media::TrackListSkeleton(op),
> - d(new Private{op, Private::MetaDataCache{}, extractor})
> + d(new Private{op, 0, Private::MetaDataCache{}, extractor, media::TrackList::Container{}})
Would be great to have a constant being passed instead of the magic number 0 :)
> {
> can_edit_tracks().set(true);
> }
> @@ -73,15 +79,16 @@
> const media::Track::Id& position,
> bool make_current)
> {
> - static size_t track_counter = 0;
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
>
> - std::stringstream ss; ss << d->path.as_string() << "/" << track_counter++;
> + std::stringstream ss; ss << d->path.as_string() << "/" << d->track_counter++;
> Track::Id id{ss.str()};
>
> auto result = tracks().update([this, id, position, make_current](TrackList::Container& container)
> {
> auto it = std::find(container.begin(), container.end(), position);
> container.insert(it, id);
> +
> return true;
> });
>
> @@ -100,6 +107,7 @@
> if (make_current)
> go_to(id);
>
> + // Signal to the client that a track was added to the TrackList
> on_track_added()(id);
> }
> }
> @@ -123,5 +131,58 @@
>
> void media::TrackListImplementation::go_to(const media::Track::Id& track)
> {
> - (void) track;
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
> + // Signal the Player instance to go to a specific track for playback
> + on_go_to_track()(track);
> + on_track_changed()(track);
> +}
> +
> +void media::TrackListImplementation::shuffle_tracks()
Do we really have to shuffle the tracks in the container here? I would have expected that selecting the next track from the tracklist would be randomized. I would also imagine that it is surprising to the user if the music or media app suddenly shows the shuffled view of the tracklist. With that, we would also avoid the extra copy of the track list. Looking forward to hear your thoughts.
> +{
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
> +
> + auto result = tracks().update([this](TrackList::Container& container)
> + {
> + d->original_tracklist.assign(container.begin(), container.end());
> + std::random_shuffle(container.begin(), container.end());
> + return true;
> + });
> +
> + if (result)
> + {
> + media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
> + on_track_list_replaced()(t);
> + }
> +}
> +
> +void media::TrackListImplementation::unshuffle_tracks()
> +{
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
> +
> + auto result = tracks().update([this](TrackList::Container& container)
> + {
> + container.assign(d->original_tracklist.begin(), d->original_tracklist.end());
> + return true;
> + });
> +
> + if (result)
> + {
> + media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
> + on_track_list_replaced()(t);
> + }
> +}
> +
> +void media::TrackListImplementation::reset()
> +{
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
> +
> + auto result = tracks().update([this](TrackList::Container& container)
> + {
> + container.clear();
> + container.resize(0);
> + d->track_counter = 0;
> + return true;
> + });
> +
> + (void) result;
> }
>
> === modified file 'src/core/media/track_list_implementation.h'
> --- src/core/media/track_list_implementation.h 2014-03-25 14:57:15 +0000
> +++ src/core/media/track_list_implementation.h 2015-04-15 17:14:17 +0000
> @@ -1,5 +1,5 @@
> /*
> - * Copyright © 2013 Canonical Ltd.
> + * Copyright © 2013-2015 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,
> @@ -43,6 +43,9 @@
> void remove_track(const Track::Id& id);
>
> void go_to(const Track::Id& track);
> + void shuffle_tracks();
> + void unshuffle_tracks();
> + void reset();
>
> private:
> struct Private;
>
> === modified file 'src/core/media/track_list_skeleton.cpp'
> --- src/core/media/track_list_skeleton.cpp 2014-03-25 14:57:15 +0000
> +++ src/core/media/track_list_skeleton.cpp 2015-04-15 17:14:17 +0000
> @@ -17,6 +17,7 @@
> */
>
> #include "track_list_skeleton.h"
> +#include "track_list_implementation.h"
>
> #include <core/media/player.h>
> #include <core/media/track_list.h>
> @@ -35,6 +36,7 @@
> #include <core/dbus/types/stl/map.h>
> #include <core/dbus/types/stl/vector.h>
>
> +#include <iostream>
> #include <limits>
>
> namespace dbus = core::dbus;
> @@ -49,7 +51,15 @@
> can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()),
> tracks(object->get_property<mpris::TrackList::Properties::Tracks>()),
> current_track(tracks->get().begin()),
> - empty_iterator(tracks->get().begin())
> + empty_iterator(tracks->get().begin()),
> + loop_status(media::Player::LoopStatus::none),
> + skeleton{mpris::TrackList::Skeleton::Configuration{object, mpris::TrackList::Skeleton::Configuration::Defaults{}}},
> + signals
> + {
> + skeleton.signals.track_added,
> + skeleton.signals.track_removed,
> + skeleton.signals.tracklist_replaced
> + }
> {
> }
>
> @@ -92,6 +102,7 @@
> media::Track::Id track;
> msg->reader() >> track;
>
> + current_track = std::find(tracks->get().begin(), tracks->get().end(), track);
> impl->go_to(track);
>
> auto reply = dbus::Message::make_method_return(msg);
> @@ -105,11 +116,43 @@
> std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;
> TrackList::ConstIterator current_track;
> TrackList::ConstIterator empty_iterator;
> -
> - core::Signal<void> on_track_list_replaced;
> - core::Signal<Track::Id> on_track_added;
> - core::Signal<Track::Id> on_track_removed;
> - core::Signal<Track::Id> on_track_changed;
> + media::Player::LoopStatus loop_status;
> +
> + mpris::TrackList::Skeleton skeleton;
> +
> + struct Signals
> + {
> + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
> + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
> + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
> +
> + Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
> + const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
> + const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
> + {
> + // Connect all of the MPRIS interface signals to be emitted over dbus
> + on_track_added.connect([remote_track_added](const media::Track::Id &id)
> + {
> + remote_track_added->emit(id);
> + });
> +
> + on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
> + {
> + remote_track_removed->emit(id);
> + });
> +
> + on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple)
> + {
> + remote_track_list_replaced->emit(tltuple);
> + });
> + }
> +
> + core::Signal<Track::Id> on_track_added;
> + core::Signal<Track::Id> on_track_removed;
> + core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
> + core::Signal<Track::Id> on_track_changed;
> + core::Signal<Track::Id> on_go_to_track;
> + } signals;
> };
>
> media::TrackListSkeleton::TrackListSkeleton(
> @@ -144,80 +187,155 @@
>
> bool media::TrackListSkeleton::has_next() const
> {
> - return d->current_track != d->tracks->get().end();
> + const auto next_track = std::next(d->current_track);
> + std::cout << "has_next track? " << (next_track != d->tracks->get().end() ? "yes" : "no") << std::endl;
> + return next_track != d->tracks->get().end();
> }
>
> const media::Track::Id& media::TrackListSkeleton::next()
> {
> + std::cout << __PRETTY_FUNCTION__ << std::endl;
> if (d->tracks->get().empty())
> return *(d->current_track);
>
> + // Loop on the current track forever
> + if (d->loop_status == media::Player::LoopStatus::track)
> + {
> + std::cout << "Looping on the current track..." << std::endl;
> + return *(d->current_track);
> + }
> + // Loop over the whole playlist and repeat
> + else if (d->loop_status == media::Player::LoopStatus::playlist && !has_next())
> + {
> + std::cout << "Looping on the entire TrackList..." << std::endl;
> + d->current_track = d->tracks->get().begin();
> + return *(d->current_track);
> + }
> + else if (has_next())
> + {
> + // Keep returning the next track until the last track is reached
> + d->current_track = std::next(d->current_track);
> + std::cout << *this << std::endl;
> + }
> +
> + return *(d->current_track);
> +}
> +
> +const media::Track::Id& media::TrackListSkeleton::current()
> +{
> + // Prevent the TrackList from sitting at the end which will cause
> + // a segfault when calling current()
> if (d->tracks->get().size() && (d->current_track == d->empty_iterator))
> - {
> + {
> d->current_track = d->tracks->get().begin();
> - return *(d->current_track = std::next(d->current_track));
> }
> -
> - d->current_track = std::next(d->current_track);
> return *(d->current_track);
> }
>
> const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
> {
> - return *d->can_edit_tracks;
> + return *d->skeleton.properties.can_edit_tracks;
> }
>
> core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()
> {
> - return *d->can_edit_tracks;
> + return *d->skeleton.properties.can_edit_tracks;
> }
>
> core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()
> {
> - return *d->tracks;
> + return *d->skeleton.properties.tracks;
> +}
> +
> +void media::TrackListSkeleton::on_loop_status_changed(const media::Player::LoopStatus& loop_status)
> +{
> + d->loop_status = loop_status;
> +}
> +
> +media::Player::LoopStatus media::TrackListSkeleton::loop_status() const
> +{
> + return d->loop_status;
> +}
> +
> +void media::TrackListSkeleton::on_shuffle_changed(bool shuffle)
> +{
> + if (shuffle)
> + shuffle_tracks();
> + else
> + unshuffle_tracks();
> }
>
> const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
> {
> - return *d->tracks;
> + return *d->skeleton.properties.tracks;
> }
>
> -const core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced() const
> +const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() const
> {
> - return d->on_track_list_replaced;
> + // Print the TrackList instance
> + std::cout << *this << std::endl;
> + return d->signals.on_track_list_replaced;
> }
>
> const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const
> {
> - return d->on_track_added;
> + return d->signals.on_track_added;
> }
>
> const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
> {
> - return d->on_track_removed;
> + return d->signals.on_track_removed;
> }
>
> const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
> {
> - return d->on_track_changed;
> -}
> -
> -core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced()
> -{
> - return d->on_track_list_replaced;
> + return d->signals.on_track_changed;
> +}
> +
> +const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() const
> +{
> + return d->signals.on_go_to_track;
> +}
> +
> +core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced()
> +{
> + return d->signals.on_track_list_replaced;
> }
>
> core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()
> {
> - return d->on_track_added;
> + return d->signals.on_track_added;
> }
>
> core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
> {
> - return d->on_track_removed;
> + return d->signals.on_track_removed;
> }
>
> core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
> {
> - return d->on_track_changed;
> -}
> + return d->signals.on_track_changed;
> +}
> +
> +core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track()
> +{
> + return d->signals.on_go_to_track;
> +}
> +
> +// operator<< pretty prints the given TrackList to the given output stream.
> +inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist)
> +{
> + auto non_const_tl = const_cast<media::TrackList*>(&tracklist);
> + out << "TrackList\n---------------" << std::endl;
> + for (const media::Track::Id &id : tracklist.tracks().get())
> + {
> + // '*' denotes the current track
> + out << "\t" << ((dynamic_cast<media::TrackListSkeleton*>(non_const_tl)->current() == id) ? "*" : "");
> + out << "Track Id: " << id << std::endl;
> + out << "\t\turi: " << dynamic_cast<media::TrackListImplementation*>(non_const_tl)->query_uri_for_track(id) << std::endl;
> + }
> +
> + out << "---------------\nEnd TrackList" << std::endl;
> + return out;
> +}
> +
>
> === modified file 'src/core/media/track_list_skeleton.h'
> --- src/core/media/track_list_skeleton.h 2014-03-25 14:57:15 +0000
> +++ src/core/media/track_list_skeleton.h 2015-04-15 17:14:17 +0000
> @@ -39,29 +39,42 @@
>
> bool has_next() const;
> const Track::Id& next();
> + const Track::Id& current();
>
> const core::Property<bool>& can_edit_tracks() const;
> const core::Property<Container>& tracks() const;
>
> - const core::Signal<void>& on_track_list_replaced() const;
> + const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
> const core::Signal<Track::Id>& on_track_added() const;
> const core::Signal<Track::Id>& on_track_removed() const;
> const core::Signal<Track::Id>& on_track_changed() const;
> + const core::Signal<Track::Id>& on_go_to_track() const;
> + core::Signal<Track::Id>& on_go_to_track();
> + core::Signal<Track::Id>& on_track_removed();
>
> core::Property<Container>& tracks();
> + void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus& loop_status);
> + core::ubuntu::media::Player::LoopStatus loop_status() const;
> +
> + /** Gets called when the shuffle property on the Player interface is changed
> + * by the client */
> + void on_shuffle_changed(bool shuffle);
>
> protected:
> core::Property<bool>& can_edit_tracks();
>
> - core::Signal<void>& on_track_list_replaced();
> + core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
> core::Signal<Track::Id>& on_track_added();
> - core::Signal<Track::Id>& on_track_removed();
> core::Signal<Track::Id>& on_track_changed();
>
> private:
> struct Private;
> std::unique_ptr<Private> d;
> };
> +
> +// operator<< pretty prints the given TrackList status to the given output stream.
> +std::ostream& operator<<(std::ostream& out, const core::ubuntu::media::TrackList& tracklist);
> +
> }
> }
> }
>
> === modified file 'src/core/media/track_list_stub.cpp'
> --- src/core/media/track_list_stub.cpp 2014-02-12 15:53:57 +0000
> +++ src/core/media/track_list_stub.cpp 2015-04-15 17:14:17 +0000
> @@ -59,10 +59,11 @@
> std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks;
> std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;
>
> - core::Signal<void> on_track_list_replaced;
> + core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced;
> core::Signal<Track::Id> on_track_added;
> core::Signal<Track::Id> on_track_removed;
> core::Signal<Track::Id> on_track_changed;
> + core::Signal<Track::Id> on_go_to_track;
> };
>
> media::TrackListStub::TrackListStub(
> @@ -137,18 +138,36 @@
> throw std::runtime_error("Problem adding track: " + op.error());
> }
>
> -const core::Signal<void>& media::TrackListStub::on_track_list_replaced() const
> -{
> +void media::TrackListStub::shuffle_tracks()
> +{
> + std::cerr << "shuffle_tracks() does nothing from the client side" << std::endl;
> +}
> +
> +void media::TrackListStub::unshuffle_tracks()
> +{
> + std::cerr << "unshuffle_tracks() does nothing from the client side" << std::endl;
> +}
> +
> +void media::TrackListStub::reset()
> +{
> + std::cerr << "reset() does nothing from the client side" << std::endl;
> +}
> +
> +const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListStub::on_track_list_replaced() const
> +{
> + std::cout << "Signal on_track_list_replaced arrived via the bus" << std::endl;
> return d->on_track_list_replaced;
> }
>
> const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const
> {
> + std::cout << "Signal on_track_added arrived via the bus" << std::endl;
> return d->on_track_added;
> }
>
> const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const
> {
> + std::cout << "Signal on_track_removed arrived via the bus" << std::endl;
> return d->on_track_removed;
> }
>
> @@ -156,3 +175,8 @@
> {
> return d->on_track_changed;
> }
> +
> +const core::Signal<media::Track::Id>& media::TrackListStub::on_go_to_track() const
> +{
> + return d->on_go_to_track;
> +}
>
> === modified file 'src/core/media/track_list_stub.h'
> --- src/core/media/track_list_stub.h 2014-02-12 15:53:57 +0000
> +++ src/core/media/track_list_stub.h 2015-04-15 17:14:17 +0000
> @@ -51,10 +51,16 @@
>
> void go_to(const Track::Id& track);
>
> - const core::Signal<void>& on_track_list_replaced() const;
> + void shuffle_tracks();
> + void unshuffle_tracks();
> +
> + void reset();
> +
> + const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
> const core::Signal<Track::Id>& on_track_added() const;
> const core::Signal<Track::Id>& on_track_removed() const;
> const core::Signal<Track::Id>& on_track_changed() const;
> + const core::Signal<Track::Id>& on_go_to_track() const;
>
> private:
> struct Private;
>
> === modified file 'tests/CMakeLists.txt'
> --- tests/CMakeLists.txt 2014-11-26 11:04:57 +0000
> +++ tests/CMakeLists.txt 2015-04-15 17:14:17 +0000
> @@ -51,4 +51,5 @@
> )
>
> # add_subdirectory(acceptance-tests)
> +add_subdirectory(test-track-list)
> add_subdirectory(unit-tests)
>
> === added directory 'tests/test-track-list'
> === added file 'tests/test-track-list/CMakeLists.txt'
> --- tests/test-track-list/CMakeLists.txt 1970-01-01 00:00:00 +0000
> +++ tests/test-track-list/CMakeLists.txt 2015-04-15 17:14:17 +0000
> @@ -0,0 +1,17 @@
> +include_directories(
> + ${CMAKE_SOURCE_DIR}/src)
> +
> +add_executable(
> + test_track_list
> + test_track_list.cpp
> + )
> +
> +target_link_libraries(
> + test_track_list
> +
> + media-hub-client
> +
> + ${CMAKE_THREAD_LIBS_INIT}
> + ${DBUS_LIBRARIES}
> + ${PROCESS_CPP_LDFLAGS}
> +)
>
> === added file 'tests/test-track-list/test_track_list.cpp'
> --- tests/test-track-list/test_track_list.cpp 1970-01-01 00:00:00 +0000
> +++ tests/test-track-list/test_track_list.cpp 2015-04-15 17:14:17 +0000
> @@ -0,0 +1,345 @@
> +/*
> + * Copyright © 2013-2015 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: Jim Hodapp <jim.hodapp at canonical.com>
> + */
> +
> +#include "test_track_list.h"
> +#include "../../src/core/media/util/timeout.h"
> +
> +#include <core/media/service.h>
> +#include <core/media/track_list.h>
> +
> +#include <cassert>
> +#include <future>
> +#include <iostream>
> +#include <thread>
> +
> +namespace media = core::ubuntu::media;
> +using namespace std;
> +
> +media::TestTrackList::TestTrackList()
> +{
> + try {
> + m_hubService = media::Service::Client::instance();
> + }
> + catch (std::runtime_error &e) {
> + cerr << "FATAL: Failed to connect to media-hub service: " << e.what() << endl;
> + }
> +}
> +
> +media::TestTrackList::~TestTrackList()
> +{
> +}
> +
> +void media::TestTrackList::create_new_player_session()
> +{
> + try {
> + m_hubPlayerSession = m_hubService->create_session(media::Player::Client::default_configuration());
> + }
> + catch (std::runtime_error &e) {
> + cerr << "FATAL: Failed to start a new media-hub player session: " << e.what() << endl;
> + }
> +
> + try {
> + m_hubTrackList = m_hubPlayerSession->track_list();
> + }
> + catch (std::runtime_error &e) {
> + cerr << "FATAL: Failed to retrieve the current player's TrackList: " << e.what() << endl;
> + }
> +}
> +
> +void media::TestTrackList::destroy_player_session()
> +{
> + // TODO: explicitly add a destroy session to the Service class after ricmm lands his new creation_session
> + // that returns a session ID. This will allow me to clear the tracklist after each test.
> + m_hubPlayerSession.reset();
> +}
> +
> +void media::TestTrackList::add_track(const string &uri, bool make_current)
> +{
> + assert (m_hubTrackList.get() != nullptr);
> +
> + cout << "Adding " << uri << " to the TrackList for playback." << endl;
> +
> + try {
> + bool can_edit_tracks = m_hubTrackList->can_edit_tracks();
> + cout << "can_edit_tracks: " << (can_edit_tracks ? "yes" : "no") << std::endl;
> + if (can_edit_tracks)
> + m_hubTrackList->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current);
> + else
> + cerr << "Can't add track to TrackList since can_edit_tracks is false" << endl;
> + }
> + catch (std::runtime_error &e) {
> + cerr << "ERROR: Failed to add track " << uri << " to tracklist: " << e.what() << endl;
> + }
> +}
> +
> +#include <unistd.h>
> +void media::TestTrackList::test_basic_playback(const std::string &uri1, const std::string &uri2)
> +{
> + cout << "--> Running test: test_basic_playback" << std::endl;
> +
> + core::Connection c =m_hubTrackList->on_track_added().connect([](const media::Track::Id &new_id)
> + {
> + cout << "Added track to TrackList with Id: " << new_id << endl;
> + });
> +
> + //create_new_player_session();
> +
> + m_hubPlayerSession->open_uri(uri1);
> + if (!uri2.empty())
> + add_track(uri2);
> +
> + //cout << "Waiting for track to be added to TrackList" << endl;
> + //media::Track::Id id = wait_for_on_track_added();
> +
> + m_hubPlayerSession->play();
> + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none;
> +
> + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
> + {
> + cout << "Waiting for first track to finish playing..." << endl;
> + wait_for_about_to_finish();
> + cout << "Basic playback was successful" << endl;
> + }
> +
> + c.disconnect();
> +
> + //destroy_player_session();
> +}
> +
> +void media::TestTrackList::test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2)
> +{
> + cout << "--> Running test: test_ensure_tracklist_is_not_empty" << std::endl;
> +
> + add_track(uri1);
> + if (!uri2.empty())
> + add_track(uri2);
> +
> + if (m_hubTrackList->tracks()->size() == 1 or m_hubTrackList->tracks()->size() == 2)
> + cout << "TrackList is not empty, test success" << endl;
> + else
> + cout << "TrackList is empty, test failure" << endl;
> +}
> +
> +void media::TestTrackList::test_has_next_track(const std::string &uri1, const std::string &uri2)
> +{
> + cout << "--> Running test: test_has_next_track" << std::endl;
> +
> + //create_new_player_session();
> +
> + add_track(uri1);
> + add_track(uri2);
> +
> + m_hubPlayerSession->play();
> + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none;
> +
> + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
> + {
> + cout << "Waiting for first track to finish playing..." << endl;
> + wait_for_about_to_finish();
> + cout << "Waiting for second track to finish playing..." << endl;
> + wait_for_about_to_finish();
> + cout << "Both tracks played successfully" << endl;
> + }
> + else
> + cerr << "Playback did not start successfully" << endl;
> +
> + //destroy_player_session();
> +}
> +
> +void media::TestTrackList::test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3)
> +{
> + cout << "--> Running test: test_shuffle" << std::endl;
> +
> + add_track(uri1);
> + add_track(uri2);
> + add_track(uri3);
> + add_track(uri3);
> + add_track(uri2);
> + add_track(uri1);
> + add_track(uri1);
> + add_track(uri2);
> +
> + m_hubPlayerSession->play();
> + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::playlist;
> + m_hubPlayerSession->shuffle() = true;
> +
> + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
> + {
> + cout << "Waiting for first track to finish playing..." << endl;
> + wait_for_about_to_finish();
> +
> + cout << "Turning off shuffle mode" << endl;
> + m_hubPlayerSession->shuffle() = false;
> +
> + cout << "Waiting for second track to finish playing..." << endl;
> + wait_for_about_to_finish();
> +
> + cout << "Going straight to the Track with Id of '/core/ubuntu/media/Service/sessions/0/TrackList/4'" << std::endl;
> + const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/4"};
> + m_hubTrackList->go_to(id);
> + cout << "Waiting for third track to finish playing..." << endl;
> + wait_for_about_to_finish();
> + }
> + else
> + cerr << "Playback did not start successfully" << endl;
> +}
> +
> +void media::TestTrackList::test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3)
> +{
> + cout << "--> Running test: test_remove_track" << std::endl;
> +
> + core::Connection c =m_hubTrackList->on_track_removed().connect([](const media::Track::Id &new_id)
> + {
> + cout << "Removed track from TrackList with Id: " << new_id << endl;
> + });
> +
> + add_track(uri1);
> + add_track(uri2);
> + add_track(uri3);
> +
> + m_hubPlayerSession->play();
> +
> + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
> + {
> + cout << "Waiting for first track to finish playing..." << endl;
> + wait_for_about_to_finish();
> +
> + const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/1"};
> + m_hubTrackList->remove_track(id);
> +
> + cout << "Waiting for track after removed track to finish playing..." << endl;
> + wait_for_about_to_finish();
> + }
> + else
> + cerr << "Playback did not start successfully" << endl;
> +}
> +
> +template<class T>
> +bool media::TestTrackList::verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout)
> +{
> + bool signal_emitted = false;
> +#if 0
> + static const std::chrono::milliseconds timeout{1000};
> + media::timeout(timeout.count(), true, [wp]()
> + {
> + if (auto sp = wp.lock())
> + sp->on_client_died();
> + });
> + signal.connect([signal_emitted]()
> + {
> + });
> +#endif
> +
> + return false;
> +}
> +
> +void media::TestTrackList::wait_for_about_to_finish()
> +{
> + bool received_about_to_finish = false;
> + core::Connection c = m_hubPlayerSession->about_to_finish().connect([&received_about_to_finish]()
> + {
> + cout << "AboutToFinish signaled" << endl;
> + received_about_to_finish = true;
> + });
> + while (!received_about_to_finish)
> + std::this_thread::yield();
> +
> + c.disconnect();
> +}
> +
> +void media::TestTrackList::wait_for_end_of_stream()
> +{
> + bool reached_end_of_first_track = false;
> + core::Connection c = m_hubPlayerSession->end_of_stream().connect([&reached_end_of_first_track]()
> + {
> + cout << "EndOfStream signaled" << endl;
> + reached_end_of_first_track = true;
> + });
> + while (!reached_end_of_first_track)
> + std::this_thread::yield();
> +
> + c.disconnect();
> +}
> +
> +void media::TestTrackList::wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status)
> +{
> + bool received_playback_status_changed = false;
> + core::Connection c = m_hubPlayerSession->playback_status().changed().connect([&received_playback_status_changed, &status](media::Player::PlaybackStatus new_status)
> + {
> + cout << "PlaybackStatusChanged signaled" << endl;
> + if (new_status == status)
> + received_playback_status_changed = true;
> + });
> + while (!received_playback_status_changed)
> + std::this_thread::yield();
> +
> + c.disconnect();
> +}
> +
> +core::ubuntu::media::Track::Id media::TestTrackList::wait_for_on_track_added()
> +{
> + media::Track::Id id;
> + bool received_on_track_added = false;
> + core::Connection c = m_hubTrackList->on_track_added().connect([&received_on_track_added, &id](const media::Track::Id &new_id)
> + {
> + cout << "OnTrackAdded signaled" << endl;
> + id = new_id;
> + received_on_track_added = true;
> + });
> + while (!received_on_track_added)
> + std::this_thread::yield();
> +
> + c.disconnect();
> +
> + return id;
> +}
> +
> +int main (int argc, char **argv)
> +{
> + shared_ptr<media::TestTrackList> tracklist = make_shared<media::TestTrackList>();
> +
> + if (argc == 2)
> + {
> + tracklist->create_new_player_session();
> + tracklist->test_basic_playback(argv[1]);
> + tracklist->test_ensure_tracklist_is_not_empty(argv[1]);
> + }
> + else if (argc == 3)
> + {
> + tracklist->create_new_player_session();
> + //tracklist->test_basic_playback(argv[1], argv[2]);
> + tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]);
> + //tracklist->test_has_next_track(argv[1], argv[2]);
> + }
> + else if (argc == 4)
> + {
> + tracklist->create_new_player_session();
> + tracklist->test_basic_playback(argv[1]);
> + tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]);
> + tracklist->test_has_next_track(argv[1], argv[2]);
> + tracklist->test_shuffle(argv[1], argv[2], argv[3]);
> + tracklist->test_remove_track(argv[1], argv[2], argv[3]);
> + }
> + else
> + {
> + cout << "Can't test TrackList, no track(s) specified." << endl;
> + cout << argv[0] << " <track_uri_1> [<track_uri_2>] [<track_uri_3>]" << endl;
> + }
> +
> + return 0;
> +}
>
> === added file 'tests/test-track-list/test_track_list.h'
> --- tests/test-track-list/test_track_list.h 1970-01-01 00:00:00 +0000
> +++ tests/test-track-list/test_track_list.h 2015-04-15 17:14:17 +0000
> @@ -0,0 +1,86 @@
> +/*
> + * Copyright © 2013-2015 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: Jim Hodapp <jim.hodapp at canonical.com>
> + */
> +
> +#ifndef TEST_TRACK_LIST_H_
> +#define TEST_TRACK_LIST_H_
> +
> +#include <core/media/player.h>
> +#include <core/media/track.h>
> +
> +#include <core/dbus/signal.h>
> +
> +#include <chrono>
> +#include <memory>
> +#include <string>
> +
> +namespace core
> +{
> +namespace ubuntu
> +{
> +namespace media
> +{
> +
> +class Player;
> +class Service;
> +class TrackList;
> +
> +class TestTrackList
> +{
> +public:
> + TestTrackList();
> + ~TestTrackList();
> +
> + void create_new_player_session();
> + void destroy_player_session();
> +
> + void add_track(const std::string &uri, bool make_current = false);
> +
> + // Takes in one or two files for playback, adds it/them to the TrackList, and plays
> + void test_basic_playback(const std::string &uri1, const std::string &uri2 = std::string{});
> +
> + void test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2 = std::string{});
> +
> + // Takes in one or two files for playback, adds it/them to the TrackList, plays and makes sure
> + // that the Player advances the TrackList
> + void test_has_next_track(const std::string &uri1, const std::string &uri2);
> +
> + void test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3);
> +
> + void test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3);
> +
> +protected:
> + // Synchronously verify that a signal is emitted waiting up to timeout milliseconds
> + template<class T>
> + bool verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout);
> +
> + void wait_for_about_to_finish();
> + void wait_for_end_of_stream();
> + void wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status);
> + core::ubuntu::media::Track::Id wait_for_on_track_added();
> +
> +private:
> + std::shared_ptr<core::ubuntu::media::Service> m_hubService;
> + std::shared_ptr<core::ubuntu::media::Player> m_hubPlayerSession;
> + std::shared_ptr<core::ubuntu::media::TrackList> m_hubTrackList;
> +};
> +
> +} // media
> +} // ubuntu
> +} // core
> +
> +#endif
>
> === modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
> --- tests/unit-tests/test-gstreamer-engine.cpp 2015-03-03 22:41:22 +0000
> +++ tests/unit-tests/test-gstreamer-engine.cpp 2015-04-15 17:14:17 +0000
> @@ -103,7 +103,8 @@
> std::ref(wst),
> std::placeholders::_1));
>
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
> @@ -144,7 +145,8 @@
> std::ref(wst),
> std::placeholders::_1));
>
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
> @@ -234,7 +236,8 @@
> std::ref(wst),
> std::placeholders::_1));
>
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
> @@ -282,7 +285,8 @@
> std::ref(wst),
> std::placeholders::_1));
>
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
> @@ -328,7 +332,8 @@
> std::ref(wst),
> std::placeholders::_1));
>
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
> @@ -363,7 +368,8 @@
> &core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State>::trigger,
> std::ref(wst),
> std::placeholders::_1));
> - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));
> + static const bool do_pipeline_reset = true;
> + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
> EXPECT_TRUE(engine.play());
> EXPECT_TRUE(wst.wait_for_state_for(
> core::ubuntu::media::Engine::State::playing,
>
--
https://code.launchpad.net/~phablet-team/media-hub/tracklist-cli/+merge/255900
Your team Ubuntu Phablet Team is subscribed to branch lp:media-hub.
More information about the Ubuntu-reviews
mailing list