Merge lp:~phablet-team/media-hub/media-hub-condensed into lp:media-hub

Proposed by Jim Hodapp
Status: Merged
Merged at revision: 28
Proposed branch: lp:~phablet-team/media-hub/media-hub-condensed
Merge into: lp:media-hub
Diff against target: 2681 lines (+1434/-95)
39 files modified
CMakeLists.txt (+2/-0)
debian/control (+3/-0)
debian/media-hub.conf (+4/-1)
debian/media-hub.install (+1/-0)
debian/rules (+4/-0)
debian/usr.bin.media-hub-server (+98/-0)
include/core/media/player.h (+18/-3)
include/core/media/service.h (+1/-0)
src/core/media/CMakeLists.txt (+6/-2)
src/core/media/apparmor.h (+51/-0)
src/core/media/engine.h (+10/-0)
src/core/media/gstreamer/engine.cpp (+105/-8)
src/core/media/gstreamer/engine.h (+9/-0)
src/core/media/gstreamer/playbin.h (+184/-20)
src/core/media/mpris/player.h (+6/-0)
src/core/media/mpris/service.h (+1/-0)
src/core/media/player_implementation.cpp (+172/-17)
src/core/media/player_implementation.h (+7/-1)
src/core/media/player_skeleton.cpp (+221/-7)
src/core/media/player_skeleton.h (+11/-0)
src/core/media/player_stub.cpp (+197/-3)
src/core/media/player_stub.h (+11/-0)
src/core/media/powerd_service.h (+99/-0)
src/core/media/server/server.cpp (+28/-0)
src/core/media/service.cpp (+1/-1)
src/core/media/service_implementation.cpp (+57/-10)
src/core/media/service_implementation.h (+5/-0)
src/core/media/service_skeleton.cpp (+17/-1)
src/core/media/service_skeleton.h (+1/-1)
src/core/media/service_stub.cpp (+13/-5)
src/core/media/service_stub.h (+2/-1)
src/core/media/track_list_implementation.cpp (+13/-0)
src/core/media/track_list_implementation.h (+1/-0)
src/core/media/track_list_skeleton.cpp (+15/-3)
src/core/media/track_list_skeleton.h (+2/-1)
tests/acceptance-tests/service.cpp (+1/-1)
tests/unit-tests/CMakeLists.txt (+15/-1)
tests/unit-tests/libmedia-mock.cpp (+26/-0)
tests/unit-tests/test-gstreamer-engine.cpp (+16/-8)
To merge this branch: bzr merge lp:~phablet-team/media-hub/media-hub-condensed
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+217509@code.launchpad.net

Commit message

* Merged with EOS branch from tvoss
* Added on_frame_available listener and callback
* Create a decoding session and pass it to the hybris layer
* Allow playback to work again after a client quits or dies
* Added two new properties to see if the opened media is video or audio
* Background playlist support

* Add powerd control interface

* One Engine instance per PlayerImplementation instance instead of one global Engine instance
* Pause other playing sessions when starting playback of a new foreground player
* Emit a PlaybackStatusChanged signal so that qtubuntu-media knows when PlaybackStatus changes from underneath it
* Use GST_SEEK_FLAG_KEY_UNIT instead because seeking doesn't perform well enough with GST_SEEK_FLAG_ACCURATE

Description of the change

* Merged with EOS branch from tvoss
* Added on_frame_available listener and callback
* Create a decoding session and pass it to the hybris layer
* Allow playback to work again after a client quits or dies
* Added two new properties to see if the opened media is video or audio
* Background playlist support

* Add powerd control interface

* One Engine instance per PlayerImplementation instance instead of one global Engine instance
* Pause other playing sessions when starting playback of a new foreground player
* Emit a PlaybackStatusChanged signal so that qtubuntu-media knows when PlaybackStatus changes from underneath it
* Use GST_SEEK_FLAG_KEY_UNIT instead because seeking doesn't perform well enough with GST_SEEK_FLAG_ACCURATE

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
31. By Jim Hodapp

Make sure media-hub respawns if it crashes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-02-14 07:19:31 +0000
3+++ CMakeLists.txt 2014-04-30 01:13:03 +0000
4@@ -25,6 +25,8 @@
5 pkg_check_modules(DBUS dbus-1 REQUIRED)
6 pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
7 pkg_check_modules(PROPERTIES_CPP properties-cpp REQUIRED)
8+pkg_check_modules(GIO gio-2.0 REQUIRED)
9+pkg_check_modules(HYBRIS_MEDIA libmedia REQUIRED)
10
11 #####################################################################
12 # Enable code coverage calculation with gcov/gcovr/lcov
13
14=== modified file 'debian/control'
15--- debian/control 2014-03-26 20:18:20 +0000
16+++ debian/control 2014-04-30 01:13:03 +0000
17@@ -5,6 +5,7 @@
18 Build-Depends: cmake,
19 dbus-test-runner,
20 debhelper (>= 9),
21+ dh-apparmor,
22 doxygen,
23 gcc-4.7,
24 g++-4.7,
25@@ -39,6 +40,7 @@
26 Depends: libmedia-hub-common0 (= ${binary:Version}),
27 libmedia-hub-client0 (= ${binary:Version}),
28 ${misc:Depends},
29+ libproperties-cpp-dev,
30 Suggests: libmedia-hub-doc
31 Description: Simple and lightweight media playback service - development files
32 Media Hub is a simple and lightweight service for media playback using
33@@ -51,6 +53,7 @@
34 Pre-Depends: dpkg (>= 1.15.6~)
35 Depends: ${misc:Depends},
36 ${shlibs:Depends},
37+Suggests: apparmor (>= 2.8.95~2430-0ubuntu4~)
38 Description: Simple and lightweight media playback service
39 Media Hub is a simple and lightweight service for media playback using
40 DBus.
41
42=== modified file 'debian/media-hub.conf'
43--- debian/media-hub.conf 2014-03-07 14:38:28 +0000
44+++ debian/media-hub.conf 2014-04-30 01:13:03 +0000
45@@ -1,6 +1,9 @@
46 description "Starts the media-hub service"
47
48-start on dbus
49+start on started dbus
50 stop on runlevel [06]
51
52+respawn
53+respawn limit unlimited
54+
55 exec media-hub-server
56
57=== modified file 'debian/media-hub.install'
58--- debian/media-hub.install 2014-03-06 22:51:51 +0000
59+++ debian/media-hub.install 2014-04-30 01:13:03 +0000
60@@ -1,2 +1,3 @@
61 debian/media-hub.conf /usr/share/upstart/sessions/
62+debian/usr.bin.media-hub-server etc/apparmor.d
63 usr/bin
64
65=== modified file 'debian/rules'
66--- debian/rules 2014-03-07 22:49:23 +0000
67+++ debian/rules 2014-04-30 01:13:03 +0000
68@@ -14,3 +14,7 @@
69
70 override_dh_auto_test:
71 env -u LD_PRELOAD dh_auto_test
72+
73+override_dh_installdeb:
74+ dh_apparmor --profile-name=usr.bin.media-hub-server -pmedia-hub
75+ dh_installdeb
76
77=== added file 'debian/usr.bin.media-hub-server'
78--- debian/usr.bin.media-hub-server 1970-01-01 00:00:00 +0000
79+++ debian/usr.bin.media-hub-server 2014-04-30 01:13:03 +0000
80@@ -0,0 +1,98 @@
81+#include <tunables/global>
82+
83+/usr/bin/media-hub-server (attach_disconnected) {
84+ #include <abstractions/base>
85+ #include <abstractions/audio>
86+ #include <abstractions/nameservice>
87+ #include <abstractions/dbus-session>
88+ #include <abstractions/dbus-strict>
89+ #include <abstractions/user-tmp>
90+ #include "/usr/share/apparmor/hardware/audio.d"
91+ #include "/usr/share/apparmor/hardware/graphics.d"
92+ #include "/usr/share/apparmor/hardware/video.d"
93+
94+ deny /dev/cpuctl/apps/tasks w,
95+ deny /dev/cpuctl/apps/bg_non_interactive/tasks w,
96+
97+ @{PROC}/interrupts r,
98+ owner @{PROC}/cmdline r,
99+ owner @{PROC}/[0-9]*/auxv r,
100+ owner @{PROC}/[0-9]*/fd/ r,
101+ owner @{PROC}/[0-9]*/status r,
102+ owner @{PROC}/[0-9]*/task/ r,
103+ owner @{PROC}/[0-9]*/task/[0-9]*/ r,
104+ owner @{PROC}/[0-9]*/cmdline r,
105+
106+ /sys/kernel/debug/tracing/trace_marker w,
107+ /dev/ashmem rw,
108+
109+ # Explicitly deny this-- it is not needed
110+ /dev/fb0 rw,
111+
112+ # libhybris
113+ /{,var/}run/shm/hybris_shm_data rw,
114+ /usr/lib/@{multiarch}/libhybris/*.so mr,
115+ /{,android/}system/build.prop r,
116+ # These libraries can be in any of:
117+ # /vendor/lib
118+ # /system/lib
119+ # /system/vendor/lib
120+ # /android/vendor/lib
121+ # /android/system/lib
122+ # /android/system/vendor/lib
123+ /{,android/}vendor/lib/** r,
124+ /{,android/}vendor/lib/**.so m,
125+ /{,android/}system/lib/** r,
126+ /{,android/}system/lib/**.so m,
127+ /{,android/}system/vendor/lib/** r,
128+ /{,android/}system/vendor/lib/**.so m,
129+
130+ # attach_disconnected path
131+ /dev/socket/property_service rw,
132+
133+ # Android logging triggered by platform. Can safely deny
134+ deny /dev/log_main w,
135+ deny /dev/log_radio w,
136+ deny /dev/log_events w,
137+ deny /dev/log_system w,
138+
139+ # Allow all access to powerd for now, but we can fine-tune this if needed
140+ dbus (receive, send)
141+ bus=system
142+ path=/com/canonical/powerd
143+ interface=com.canonical.powerd,
144+
145+ # GStreamer binary registry - hybris pulls this in for everything now, not
146+ # just audio
147+ owner @{HOME}/.gstreamer*/registry.*.bin* rw,
148+ owner @{HOME}/.gstreamer*/ rw,
149+ owner @{HOME}/.cache/gstreamer*/registry.*.bin* rw,
150+
151+ /{,android/}system/etc/media_codecs.xml r,
152+ /etc/wildmidi/wildmidi.cfg r,
153+
154+ audit deny owner /** m,
155+
156+ # Allow read on all directories
157+ /**/ r,
158+
159+ # Allow read on click install directories, removable media and files in
160+ # /usr/local/share.
161+ /usr/share/** r,
162+ /usr/local/share/** r,
163+ /{media,mnt,opt,srv}/** r,
164+
165+ # Allow reading any files in non-hidden directories
166+ owner @{HOME}/[^.]* rk,
167+ owner @{HOME}/[^.]*/ rk,
168+ owner @{HOME}/[^.]*/** rk,
169+
170+ # Allow reading files in XDG directories (ie, where apps are allowed to
171+ # write)
172+ owner @{HOME}/.cache/** rk,
173+ owner @{HOME}/.local/share/** rk,
174+ owner /{,var/}run/user/[0-9]*/** rk,
175+
176+ # Site-specific additions and overrides. See local/README for details.
177+ #include <local/usr.bin.media-hub-server>
178+}
179
180=== modified file 'include/core/media/player.h'
181--- include/core/media/player.h 2014-02-18 19:27:01 +0000
182+++ include/core/media/player.h 2014-04-30 01:13:03 +0000
183@@ -39,6 +39,13 @@
184 public:
185 typedef double PlaybackRate;
186 typedef double Volume;
187+ typedef uint32_t PlayerKey;
188+ typedef void* GLConsumerWrapperHybris;
189+
190+ /** Used to set a callback function to be called when a frame is ready to be rendered **/
191+ typedef void (*FrameAvailableCbHybris)(GLConsumerWrapperHybris wrapper, void *context);
192+ typedef void (*FrameAvailableCb)(void *context);
193+ typedef void (*PlaybackCompleteCb)(void *context);
194
195 struct Configuration;
196
197@@ -72,8 +79,11 @@
198 bool operator==(const Player&) const = delete;
199
200 virtual std::shared_ptr<TrackList> track_list() = 0;
201+ virtual PlayerKey key() const = 0;
202
203 virtual bool open_uri(const Track::UriType& uri) = 0;
204+ virtual void create_video_sink(uint32_t texture_id) = 0;
205+ virtual GLConsumerWrapperHybris gl_consumer() const = 0;
206 virtual void next() = 0;
207 virtual void previous() = 0;
208 virtual void play() = 0;
209@@ -81,11 +91,17 @@
210 virtual void stop() = 0;
211 virtual void seek_to(const std::chrono::microseconds& offset) = 0;
212
213+ // TODO: Convert this to be a signal
214+ virtual void set_frame_available_callback(FrameAvailableCb cb, void *context) = 0;
215+ virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context) = 0;
216+
217 virtual const core::Property<bool>& can_play() const = 0;
218 virtual const core::Property<bool>& can_pause() const = 0;
219 virtual const core::Property<bool>& can_seek() const = 0;
220 virtual const core::Property<bool>& can_go_previous() const = 0;
221 virtual const core::Property<bool>& can_go_next() const = 0;
222+ virtual const core::Property<bool>& is_video_source() const = 0;
223+ virtual const core::Property<bool>& is_audio_source() const = 0;
224 virtual const core::Property<PlaybackStatus>& playback_status() const = 0;
225 virtual const core::Property<LoopStatus>& loop_status() const = 0;
226 virtual const core::Property<PlaybackRate>& playback_rate() const = 0;
227@@ -97,15 +113,14 @@
228 virtual const core::Property<uint64_t>& position() const = 0;
229 virtual const core::Property<uint64_t>& duration() const = 0;
230
231-
232 virtual core::Property<LoopStatus>& loop_status() = 0;
233 virtual core::Property<PlaybackRate>& playback_rate() = 0;
234 virtual core::Property<bool>& is_shuffle() = 0;
235 virtual core::Property<Volume>& volume() = 0;
236
237-
238 virtual const core::Signal<uint64_t>& seeked_to() const = 0;
239-
240+ virtual const core::Signal<void>& end_of_stream() const = 0;
241+ virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;
242 protected:
243 Player();
244
245
246=== modified file 'include/core/media/service.h'
247--- include/core/media/service.h 2014-01-13 21:51:14 +0000
248+++ include/core/media/service.h 2014-04-30 01:13:03 +0000
249@@ -43,6 +43,7 @@
250 bool operator==(const Service&) const = delete;
251
252 virtual std::shared_ptr<Player> create_session(const Player::Configuration&) = 0;
253+ virtual void pause_other_sessions(Player::PlayerKey) = 0;
254
255 protected:
256 Service() = default;
257
258=== modified file 'src/core/media/CMakeLists.txt'
259--- src/core/media/CMakeLists.txt 2014-02-26 14:58:36 +0000
260+++ src/core/media/CMakeLists.txt 2014-04-30 01:13:03 +0000
261@@ -1,6 +1,6 @@
262 pkg_check_modules(PC_GSTREAMER_1_0 REQUIRED gstreamer-1.0)
263
264-include_directories(${PC_GSTREAMER_1_0_INCLUDE_DIRS})
265+include_directories(${PC_GSTREAMER_1_0_INCLUDE_DIRS} ${HYBRIS_MEDIA_CFLAGS})
266
267 add_library(
268 media-hub-common SHARED
269@@ -52,6 +52,7 @@
270 ${DBUS_LIBRARIES}
271 ${DBUS_CPP_LDFLAGS}
272 ${GLog_LIBRARY}
273+ ${HYBRIS_MEDIA_LIBRARIES}
274 )
275
276 install(
277@@ -82,9 +83,11 @@
278 ${DBUS_CPP_LDFLAGS}
279 ${GLog_LIBRARY}
280 ${PC_GSTREAMER_1_0_LIBRARIES}
281+ ${GIO_LIBRARIES}
282+ ${HYBRIS_MEDIA_LIBRARIES}
283 )
284
285-include_directories(${PROJECT_SOURCE_DIR}/src/)
286+include_directories(${PROJECT_SOURCE_DIR}/src/ ${HYBRIS_MEDIA_CFLAGS})
287
288 add_executable(
289 media-hub-server
290@@ -102,6 +105,7 @@
291 ${DBUS_CPP_LDFLAGS}
292 ${GLog_LIBRARY}
293 ${PC_GSTREAMER_1_0_LIBRARIES}
294+ ${HYBRIS_MEDIA_LIBRARIES}
295 )
296
297 install(
298
299=== added file 'src/core/media/apparmor.h'
300--- src/core/media/apparmor.h 1970-01-01 00:00:00 +0000
301+++ src/core/media/apparmor.h 2014-04-30 01:13:03 +0000
302@@ -0,0 +1,51 @@
303+/*
304+ * Copyright (C) 2013-2014 Canonical Ltd
305+ *
306+ * This program is free software: you can redistribute it and/or modify
307+ * it under the terms of the GNU Lesser General Public License version 3 as
308+ * published by the Free Software Foundation.
309+ *
310+ * This program is distributed in the hope that it will be useful,
311+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
312+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
313+ * GNU Lesser General Public License for more details.
314+ *
315+ * You should have received a copy of the GNU Lesser General Public License
316+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
317+ *
318+ * Author: Jim Hodapp <jim.hodapp@canonical.com>
319+ */
320+
321+#ifndef APPARMOR_H_DBUS_
322+#define APPARMOR_H_DBUS_
323+
324+#include <string>
325+#include <chrono>
326+
327+namespace core
328+{
329+
330+struct Apparmor
331+{
332+ static std::string& name()
333+ {
334+ static std::string s = "org.freedesktop.DBus";
335+ return s;
336+ }
337+
338+ struct getConnectionAppArmorSecurityContext
339+ {
340+ static std::string name()
341+ {
342+ static std::string s = "GetConnectionAppArmorSecurityContext";
343+ return s;
344+ }
345+
346+ static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
347+
348+ typedef Apparmor Interface;
349+ };
350+};
351+}
352+
353+#endif
354
355=== modified file 'src/core/media/engine.h'
356--- src/core/media/engine.h 2014-02-18 19:27:01 +0000
357+++ src/core/media/engine.h 2014-04-30 01:13:03 +0000
358@@ -18,6 +18,7 @@
359 #ifndef CORE_UBUNTU_MEDIA_ENGINE_H_
360 #define CORE_UBUNTU_MEDIA_ENGINE_H_
361
362+#include <core/media/player.h>
363 #include <core/media/track.h>
364
365 #include <core/property.h>
366@@ -97,12 +98,16 @@
367 virtual const core::Property<State>& state() const = 0;
368
369 virtual bool open_resource_for_uri(const Track::UriType& uri) = 0;
370+ virtual void create_video_sink(uint32_t texture_id) = 0;
371
372 virtual bool play() = 0;
373 virtual bool stop() = 0;
374 virtual bool pause() = 0;
375 virtual bool seek_to(const std::chrono::microseconds& ts) = 0;
376
377+ virtual const core::Property<bool>& is_video_source() const = 0;
378+ virtual const core::Property<bool>& is_audio_source() const = 0;
379+
380 virtual const core::Property<uint64_t>& position() const = 0;
381 virtual const core::Property<uint64_t>& duration() const = 0;
382
383@@ -110,6 +115,11 @@
384 virtual core::Property<Volume>& volume() = 0;
385
386 virtual const core::Property<std::tuple<Track::UriType, Track::MetaData>>& track_meta_data() const = 0;
387+
388+ virtual const core::Signal<void>& about_to_finish_signal() const = 0;
389+ virtual const core::Signal<uint64_t>& seeked_to_signal() const = 0;
390+ virtual const core::Signal<void>& end_of_stream_signal() const = 0;
391+ virtual const core::Signal<core::ubuntu::media::Player::PlaybackStatus>& playback_status_changed_signal() const = 0;
392 };
393 }
394 }
395
396=== modified file 'src/core/media/gstreamer/engine.cpp'
397--- src/core/media/gstreamer/engine.cpp 2014-02-20 19:05:17 +0000
398+++ src/core/media/gstreamer/engine.cpp 2014-04-30 01:13:03 +0000
399@@ -16,6 +16,9 @@
400 * Authored by: Thomas Voß <thomas.voss@canonical.com>
401 */
402
403+#include <stdio.h>
404+#include <stdlib.h>
405+
406 #include "bus.h"
407 #include "engine.h"
408 #include "meta_data_extractor.h"
409@@ -25,6 +28,8 @@
410
411 namespace media = core::ubuntu::media;
412
413+using namespace std;
414+
415 namespace gstreamer
416 {
417 struct Init
418@@ -43,14 +48,10 @@
419
420 struct gstreamer::Engine::Private
421 {
422- void about_to_finish()
423- {
424- state = Engine::State::ready;
425- }
426-
427 void on_playbin_state_changed(
428- const gstreamer::Bus::Message::Detail::StateChanged&)
429+ const gstreamer::Bus::Message::Detail::StateChanged& state_change)
430 {
431+ (void) state_change;
432 }
433
434 void on_tag_available(const gstreamer::Bus::Message::Detail::Tag& tag)
435@@ -163,13 +164,31 @@
436 playbin.set_volume(new_volume.value);
437 }
438
439+ void on_about_to_finish()
440+ {
441+ state = Engine::State::ready;
442+ about_to_finish();
443+ }
444+
445+ void on_seeked_to(uint64_t value)
446+ {
447+ seeked_to(value);
448+ }
449+
450+ void on_end_of_stream()
451+ {
452+ end_of_stream();
453+ }
454+
455 Private()
456 : meta_data_extractor(new gstreamer::MetaDataExtractor()),
457 volume(media::Engine::Volume(1.)),
458+ is_video_source(false),
459+ is_audio_source(false),
460 about_to_finish_connection(
461 playbin.signals.about_to_finish.connect(
462 std::bind(
463- &Private::about_to_finish,
464+ &Private::on_about_to_finish,
465 this))),
466 on_state_changed_connection(
467 playbin.signals.on_state_changed.connect(
468@@ -188,7 +207,18 @@
469 std::bind(
470 &Private::on_volume_changed,
471 this,
472- std::placeholders::_1)))
473+ std::placeholders::_1))),
474+ on_seeked_to_connection(
475+ playbin.signals.on_seeked_to.connect(
476+ std::bind(
477+ &Private::on_seeked_to,
478+ this,
479+ std::placeholders::_1))),
480+ on_end_of_stream_connection(
481+ playbin.signals.on_end_of_stream.connect(
482+ std::bind(
483+ &Private::on_end_of_stream,
484+ this)))
485 {
486 }
487
488@@ -198,15 +228,25 @@
489 core::Property<uint64_t> position;
490 core::Property<uint64_t> duration;
491 core::Property<media::Engine::Volume> volume;
492+ core::Property<bool> is_video_source;
493+ core::Property<bool> is_audio_source;
494 gstreamer::Playbin playbin;
495 core::ScopedConnection about_to_finish_connection;
496 core::ScopedConnection on_state_changed_connection;
497 core::ScopedConnection on_tag_available_connection;
498 core::ScopedConnection on_volume_changed_connection;
499+ core::ScopedConnection on_seeked_to_connection;
500+ core::ScopedConnection on_end_of_stream_connection;
501+
502+ core::Signal<void> about_to_finish;
503+ core::Signal<uint64_t> seeked_to;
504+ core::Signal<void> end_of_stream;
505+ core::Signal<media::Player::PlaybackStatus> playback_status_changed;
506 };
507
508 gstreamer::Engine::Engine() : d(new Private{})
509 {
510+ cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;
511 d->state = media::Engine::State::ready;
512 }
513
514@@ -231,6 +271,11 @@
515 return true;
516 }
517
518+void gstreamer::Engine::create_video_sink(uint32_t texture_id)
519+{
520+ d->playbin.create_video_sink(texture_id);
521+}
522+
523 bool gstreamer::Engine::play()
524 {
525 auto result = d->playbin.set_state_and_wait(GST_STATE_PLAYING);
526@@ -238,8 +283,11 @@
527 if (result)
528 {
529 d->state = media::Engine::State::playing;
530+ d->playback_status_changed(media::Player::PlaybackStatus::playing);
531 }
532
533+ cout << "Engine: " << this << endl;
534+
535 return result;
536 }
537
538@@ -248,7 +296,10 @@
539 auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
540
541 if (result)
542+ {
543 d->state = media::Engine::State::stopped;
544+ d->playback_status_changed(media::Player::PlaybackStatus::stopped);
545+ }
546
547 return result;
548 }
549@@ -258,7 +309,11 @@
550 auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED);
551
552 if (result)
553+ {
554 d->state = media::Engine::State::paused;
555+ cout << "pause" << endl;
556+ d->playback_status_changed(media::Player::PlaybackStatus::paused);
557+ }
558
559 return result;
560 }
561@@ -268,6 +323,28 @@
562 return d->playbin.seek(ts);
563 }
564
565+const core::Property<bool>& gstreamer::Engine::is_video_source() const
566+{
567+ gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
568+ if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_VIDEO)
569+ d->is_video_source.set(true);
570+ else
571+ d->is_video_source.set(false);
572+
573+ return d->is_video_source;
574+}
575+
576+const core::Property<bool>& gstreamer::Engine::is_audio_source() const
577+{
578+ gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
579+ if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_AUDIO)
580+ d->is_audio_source.set(true);
581+ else
582+ d->is_audio_source.set(false);
583+
584+ return d->is_audio_source;
585+}
586+
587 const core::Property<uint64_t>& gstreamer::Engine::position() const
588 {
589 d->position.set(d->playbin.position());
590@@ -295,3 +372,23 @@
591 {
592 return d->track_meta_data;
593 }
594+
595+const core::Signal<void>& gstreamer::Engine::about_to_finish_signal() const
596+{
597+ return d->about_to_finish;
598+}
599+
600+const core::Signal<uint64_t>& gstreamer::Engine::seeked_to_signal() const
601+{
602+ return d->seeked_to;
603+}
604+
605+const core::Signal<void>& gstreamer::Engine::end_of_stream_signal() const
606+{
607+ return d->end_of_stream;
608+}
609+
610+const core::Signal<media::Player::PlaybackStatus>& gstreamer::Engine::playback_status_changed_signal() const
611+{
612+ return d->playback_status_changed;
613+}
614
615=== modified file 'src/core/media/gstreamer/engine.h'
616--- src/core/media/gstreamer/engine.h 2014-02-18 19:27:01 +0000
617+++ src/core/media/gstreamer/engine.h 2014-04-30 01:13:03 +0000
618@@ -33,12 +33,16 @@
619 const core::Property<State>& state() const;
620
621 bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri);
622+ void create_video_sink(uint32_t texture_id);
623
624 bool play();
625 bool stop();
626 bool pause();
627 bool seek_to(const std::chrono::microseconds& ts);
628
629+ const core::Property<bool>& is_video_source() const;
630+ const core::Property<bool>& is_audio_source() const;
631+
632 const core::Property<uint64_t>& position() const;
633 const core::Property<uint64_t>& duration() const;
634
635@@ -47,6 +51,11 @@
636
637 const core::Property<std::tuple<core::ubuntu::media::Track::UriType, core::ubuntu::media::Track::MetaData>>& track_meta_data() const;
638
639+ const core::Signal<void>& about_to_finish_signal() const;
640+ const core::Signal<uint64_t>& seeked_to_signal() const;
641+ const core::Signal<void>& end_of_stream_signal() const;
642+ const core::Signal<core::ubuntu::media::Player::PlaybackStatus>& playback_status_changed_signal() const;
643+
644 private:
645 struct Private;
646 std::unique_ptr<Private> d;
647
648=== modified file 'src/core/media/gstreamer/playbin.h'
649--- src/core/media/gstreamer/playbin.h 2014-02-20 19:05:17 +0000
650+++ src/core/media/gstreamer/playbin.h 2014-04-30 01:13:03 +0000
651@@ -20,12 +20,19 @@
652 #define GSTREAMER_PLAYBIN_H_
653
654 #include "bus.h"
655-
656+#include "../mpris/player.h"
657+
658+#include <hybris/media/media_codec_layer.h>
659+#include <hybris/media/surface_texture_client_hybris.h>
660+
661+#include <gio/gio.h>
662 #include <gst/gst.h>
663
664 #include <chrono>
665 #include <string>
666
667+namespace media = core::ubuntu::media;
668+
669 namespace gstreamer
670 {
671 struct Playbin
672@@ -37,6 +44,13 @@
673 GST_PLAY_FLAG_TEXT = (1 << 2)
674 };
675
676+ enum MediaFileType
677+ {
678+ MEDIA_FILE_TYPE_NONE,
679+ MEDIA_FILE_TYPE_AUDIO,
680+ MEDIA_FILE_TYPE_VIDEO
681+ };
682+
683 static const std::string& pipeline_name()
684 {
685 static const std::string s{"playbin"};
686@@ -45,20 +59,22 @@
687
688 static void about_to_finish(GstElement*,
689 gpointer user_data)
690- { auto thiz = static_cast<Playbin*>(user_data);
691-
692+ {
693+ auto thiz = static_cast<Playbin*>(user_data);
694 thiz->signals.about_to_finish();
695 }
696
697 Playbin()
698 : pipeline(gst_element_factory_make("playbin", pipeline_name().c_str())),
699 bus{gst_element_get_bus(pipeline)},
700+ file_type(MEDIA_FILE_TYPE_NONE),
701 on_new_message_connection(
702 bus.on_new_message.connect(
703 std::bind(
704 &Playbin::on_new_message,
705 this,
706- std::placeholders::_1)))
707+ std::placeholders::_1))),
708+ is_seeking(false)
709 {
710 if (!pipeline)
711 throw std::runtime_error("Could not create pipeline for playbin.");
712@@ -74,6 +90,8 @@
713 this
714 );
715
716+ // When a client of media-hub dies, call on_client_died
717+ decoding_service_set_client_death_cb(&Playbin::on_client_died_cb, static_cast<void*>(this));
718 }
719
720 ~Playbin()
721@@ -82,6 +100,43 @@
722 gst_object_unref(pipeline);
723 }
724
725+ static void on_client_died_cb(void *context)
726+ {
727+ if (context)
728+ {
729+ Playbin *pb = static_cast<Playbin*>(context);
730+ pb->on_client_died();
731+ }
732+ }
733+
734+ void on_client_died()
735+ {
736+ std::cout << "Client died, resetting pipeline" << std::endl;
737+ // When the client dies, tear down the current pipeline and get it
738+ // in a state that is ready for the next client that connects to the
739+ // service
740+ reset_pipeline();
741+ }
742+
743+ void reset_pipeline()
744+ {
745+ std::cout << __PRETTY_FUNCTION__ << std::endl;
746+ auto ret = gst_element_set_state(pipeline, GST_STATE_NULL);
747+ switch(ret)
748+ {
749+ case GST_STATE_CHANGE_FAILURE:
750+ std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl;
751+ break;
752+ case GST_STATE_CHANGE_NO_PREROLL:
753+ case GST_STATE_CHANGE_SUCCESS:
754+ case GST_STATE_CHANGE_ASYNC:
755+ break;
756+ default:
757+ std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl;
758+ }
759+ file_type = MEDIA_FILE_TYPE_NONE;
760+ }
761+
762 void on_new_message(const Bus::Message& message)
763 {
764 switch(message.type)
765@@ -101,6 +156,16 @@
766 case GST_MESSAGE_STATE_CHANGED:
767 signals.on_state_changed(message.detail.state_changed);
768 break;
769+ case GST_MESSAGE_ASYNC_DONE:
770+ if (is_seeking)
771+ {
772+ // FIXME: Pass the actual playback time position to the signal call
773+ signals.on_seeked_to(0);
774+ is_seeking = false;
775+ }
776+ break;
777+ case GST_MESSAGE_EOS:
778+ signals.on_end_of_stream();
779 default:
780 break;
781 }
782@@ -126,6 +191,8 @@
783 ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"),
784 "audio-sink");
785
786+ std::cout << "audio_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") << std::endl;
787+
788 g_object_set (
789 pipeline,
790 "audio-sink",
791@@ -136,26 +203,41 @@
792 if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr)
793 {
794 auto video_sink = gst_element_factory_make (
795- ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"),
796- "video-sink");
797+ ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"),
798+ "video-sink");
799
800 std::cout << "video_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") << std::endl;
801
802 g_object_set (
803- pipeline,
804- "video-sink",
805- video_sink,
806- NULL);
807+ pipeline,
808+ "video-sink",
809+ video_sink,
810+ NULL);
811+ }
812+ }
813+
814+ void create_video_sink(uint32_t texture_id)
815+ {
816+ std::cout << "Creating video sink for texture_id: " << texture_id << std::endl;
817+
818+ if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr)
819+ {
820+ GstElement *video_sink = NULL;
821+ g_object_get (pipeline, "video_sink", &video_sink, NULL);
822+
823+ // Get the service-side BufferQueue (IGraphicBufferProducer) and associate it with
824+ // the SurfaceTextureClientHybris instance
825+ IGBPWrapperHybris igbp = decoding_service_get_igraphicbufferproducer();
826+ SurfaceTextureClientHybris stc = surface_texture_client_create_by_igbp(igbp);
827+ // Because mirsink is being loaded, we are definitely doing * hardware rendering.
828+ surface_texture_client_set_hardware_rendering (stc, TRUE);
829+ g_object_set (G_OBJECT (video_sink), "surface", static_cast<gpointer>(stc), static_cast<char*>(NULL));
830 }
831 }
832
833 void set_volume(double new_volume)
834 {
835- g_object_set(
836- pipeline,
837- "volume",
838- new_volume,
839- NULL);
840+ g_object_set(pipeline, "volume", new_volume, NULL);
841 }
842
843 uint64_t position() const
844@@ -178,11 +260,11 @@
845
846 void set_uri(const std::string& uri)
847 {
848- g_object_set(
849- pipeline,
850- "uri",
851- uri.c_str(),
852- NULL);
853+ g_object_set(pipeline, "uri", uri.c_str(), NULL);
854+ if (is_video_file(uri))
855+ file_type = MEDIA_FILE_TYPE_VIDEO;
856+ else if (is_audio_file(uri))
857+ file_type = MEDIA_FILE_TYPE_AUDIO;
858 }
859
860 std::string uri() const
861@@ -229,6 +311,7 @@
862
863 bool seek(const std::chrono::microseconds& ms)
864 {
865+ is_seeking = true;
866 return gst_element_seek_simple(
867 pipeline,
868 GST_FORMAT_TIME,
869@@ -236,9 +319,87 @@
870 ms.count() * 1000);
871 }
872
873+ std::string get_file_content_type(const std::string& uri) const
874+ {
875+ if (uri.empty())
876+ return std::string();
877+
878+ std::string filename(uri);
879+ std::cout << "filename: " << filename << std::endl;
880+ size_t pos = uri.find("file://");
881+ if (pos != std::string::npos)
882+ filename = uri.substr(pos + 7, std::string::npos);
883+ else
884+ // Anything other than a file, for now claim that the type
885+ // is both audio and video.
886+ // FIXME: implement true net stream sampling and get the type from GstCaps
887+ return std::string("audio/video/");
888+
889+
890+ GError *error = nullptr;
891+ std::unique_ptr<GFile, void(*)(void *)> file(
892+ g_file_new_for_path(filename.c_str()), g_object_unref);
893+ std::unique_ptr<GFileInfo, void(*)(void *)> info(
894+ g_file_query_info(
895+ file.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE ","
896+ G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_QUERY_INFO_NONE,
897+ /* cancellable */ NULL, &error),
898+ g_object_unref);
899+ if (!info)
900+ {
901+ std::string error_str(error->message);
902+ g_error_free(error);
903+
904+ std::cout << "Failed to query the URI for the presence of video content: "
905+ << error_str << std::endl;
906+ return std::string();
907+ }
908+
909+ std::string content_type(g_file_info_get_attribute_string(
910+ info.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE));
911+
912+ return content_type;
913+ }
914+
915+ bool is_audio_file(const std::string& uri) const
916+ {
917+ if (uri.empty())
918+ return false;
919+
920+ if (get_file_content_type(uri).find("audio/") == 0)
921+ {
922+ std::cout << "Found audio content" << std::endl;
923+ return true;
924+ }
925+
926+ return false;
927+ }
928+
929+ bool is_video_file(const std::string& uri) const
930+ {
931+ if (uri.empty())
932+ return false;
933+
934+ if (get_file_content_type(uri).find("video/") == 0)
935+ {
936+ std::cout << "Found video content" << std::endl;
937+ return true;
938+ }
939+
940+ return false;
941+ }
942+
943+ MediaFileType media_file_type() const
944+ {
945+ return file_type;
946+ }
947+
948 GstElement* pipeline;
949 gstreamer::Bus bus;
950+ MediaFileType file_type;
951+ SurfaceTextureClientHybris stc_hybris;
952 core::Connection on_new_message_connection;
953+ bool is_seeking;
954 struct
955 {
956 core::Signal<void> about_to_finish;
957@@ -247,6 +408,9 @@
958 core::Signal<Bus::Message::Detail::ErrorWarningInfo> on_info;
959 core::Signal<Bus::Message::Detail::Tag> on_tag_available;
960 core::Signal<Bus::Message::Detail::StateChanged> on_state_changed;
961+ core::Signal<uint64_t> on_seeked_to;
962+ core::Signal<void> on_end_of_stream;
963+ core::Signal<media::Player::PlaybackStatus> on_playback_status_changed;
964 } signals;
965 };
966 }
967
968=== modified file 'src/core/media/mpris/player.h'
969--- src/core/media/mpris/player.h 2014-02-18 19:27:01 +0000
970+++ src/core/media/mpris/player.h 2014-04-30 01:13:03 +0000
971@@ -54,11 +54,15 @@
972 METHOD(Play, Player, std::chrono::seconds(1))
973 METHOD(Seek, Player, std::chrono::seconds(1))
974 METHOD(SetPosition, Player, std::chrono::seconds(1))
975+ METHOD(CreateVideoSink, Player, std::chrono::seconds(1))
976+ METHOD(Key, Player, std::chrono::seconds(1))
977 METHOD(OpenUri, Player, std::chrono::seconds(1))
978
979 struct Signals
980 {
981 SIGNAL(Seeked, Player, uint64_t)
982+ SIGNAL(EndOfStream, Player, void)
983+ SIGNAL(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)
984 };
985
986 struct Properties
987@@ -74,6 +78,8 @@
988 READABLE_PROPERTY(Duration, Player, uint64_t)
989 READABLE_PROPERTY(MinimumRate, Player, double)
990 READABLE_PROPERTY(MaximumRate, Player, double)
991+ READABLE_PROPERTY(IsVideoSource, Player, bool)
992+ READABLE_PROPERTY(IsAudioSource, Player, bool)
993 READABLE_PROPERTY(CanGoNext, Player, bool)
994 READABLE_PROPERTY(CanGoPrevious, Player, bool)
995 READABLE_PROPERTY(CanPlay, Player, bool)
996
997=== modified file 'src/core/media/mpris/service.h'
998--- src/core/media/mpris/service.h 2014-01-13 21:51:14 +0000
999+++ src/core/media/mpris/service.h 2014-04-30 01:13:03 +0000
1000@@ -50,6 +50,7 @@
1001 };
1002
1003 METHOD(CreateSession, Service, std::chrono::seconds(1))
1004+ METHOD(PauseOtherSessions, Service, std::chrono::seconds(1))
1005 };
1006 }
1007
1008
1009=== modified file 'src/core/media/player_implementation.cpp'
1010--- src/core/media/player_implementation.cpp 2014-02-18 20:25:21 +0000
1011+++ src/core/media/player_implementation.cpp 2014-04-30 01:13:03 +0000
1012@@ -17,39 +17,125 @@
1013
1014 #include "player_implementation.h"
1015
1016+#include <unistd.h>
1017+
1018 #include "engine.h"
1019 #include "track_list_implementation.h"
1020
1021+#include "powerd_service.h"
1022+#include "gstreamer/engine.h"
1023+
1024+#define UNUSED __attribute__((unused))
1025+
1026 namespace media = core::ubuntu::media;
1027+namespace dbus = core::dbus;
1028+
1029+using namespace std;
1030
1031 struct media::PlayerImplementation::Private
1032 {
1033 Private(PlayerImplementation* parent,
1034 const dbus::types::ObjectPath& session_path,
1035 const std::shared_ptr<media::Service>& service,
1036- const std::shared_ptr<media::Engine>& engine)
1037+ PlayerImplementation::PlayerKey key)
1038 : parent(parent),
1039 service(service),
1040- engine(engine),
1041+ engine(std::make_shared<gstreamer::Engine>()),
1042 session_path(session_path),
1043 track_list(
1044 new media::TrackListImplementation(
1045 session_path.as_string() + "/TrackList",
1046- engine->meta_data_extractor()))
1047+ engine->meta_data_extractor())),
1048+ disp_lock_name("media-hub-video-playback"),
1049+ sys_lock_name("media-hub-music-playback"),
1050+ key(key)
1051 {
1052+ auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
1053+ bus->install_executor(dbus::asio::make_executor(bus));
1054+
1055+ auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Powerd>::interface_name());
1056+ powerd_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/powerd"));
1057+
1058 engine->state().changed().connect(
1059- [parent](const Engine::State& state)
1060- {
1061+ [parent, this](const Engine::State& state)
1062+ {
1063 switch(state)
1064 {
1065- case Engine::State::ready: parent->playback_status().set(media::Player::ready); break;
1066- case Engine::State::playing: parent->playback_status().set(media::Player::playing); break;
1067- case Engine::State::stopped: parent->playback_status().set(media::Player::stopped); break;
1068- case Engine::State::paused: parent->playback_status().set(media::Player::paused); break;
1069+ case Engine::State::ready:
1070+ {
1071+ parent->playback_status().set(media::Player::ready);
1072+ clear_power_state();
1073+ break;
1074+ }
1075+ case Engine::State::playing:
1076+ {
1077+ parent->playback_status().set(media::Player::playing);
1078+ request_power_state();
1079+ break;
1080+ }
1081+ case Engine::State::stopped:
1082+ {
1083+ parent->playback_status().set(media::Player::stopped);
1084+ clear_power_state();
1085+ break;
1086+ }
1087+ case Engine::State::paused:
1088+ {
1089+ parent->playback_status().set(media::Player::paused);
1090+ clear_power_state();
1091+ break;
1092+ }
1093 default:
1094 break;
1095 };
1096- });
1097+ });
1098+
1099+ }
1100+
1101+ void request_power_state()
1102+ {
1103+ if (parent->is_video_source())
1104+ {
1105+ if (disp_cookie.empty())
1106+ {
1107+ auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestDisplayState, std::string>(disp_lock_name, static_cast<int>(1), static_cast<unsigned int>(4));
1108+ if (result.is_error())
1109+ throw std::runtime_error(result.error().print());
1110+
1111+ disp_cookie = result.value();
1112+ }
1113+ }
1114+ else
1115+ {
1116+ if (sys_cookie.empty())
1117+ {
1118+ auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestSysState, std::string>(sys_lock_name, static_cast<int>(1));
1119+ if (result.is_error())
1120+ throw std::runtime_error(result.error().print());
1121+
1122+ sys_cookie = result.value();
1123+ }
1124+ }
1125+ }
1126+
1127+ void clear_power_state()
1128+ {
1129+ if (parent->is_video_source())
1130+ {
1131+ if (!disp_cookie.empty())
1132+ {
1133+ powerd_session->invoke_method_synchronously<core::Powerd::clearDisplayState, void>(disp_cookie);
1134+ disp_cookie.clear();
1135+ }
1136+ }
1137+ else
1138+ {
1139+ if (!sys_cookie.empty())
1140+ {
1141+ powerd_session->invoke_method_synchronously<core::Powerd::clearSysState, void>(sys_cookie);
1142+ sys_cookie.clear();
1143+ }
1144+ }
1145 }
1146
1147 PlayerImplementation* parent;
1148@@ -57,18 +143,24 @@
1149 std::shared_ptr<Engine> engine;
1150 dbus::types::ObjectPath session_path;
1151 std::shared_ptr<TrackListImplementation> track_list;
1152+ std::shared_ptr<dbus::Object> powerd_session;
1153+ std::string disp_lock_name;
1154+ std::string sys_lock_name;
1155+ std::string disp_cookie;
1156+ std::string sys_cookie;
1157+ PlayerImplementation::PlayerKey key;
1158 };
1159
1160 media::PlayerImplementation::PlayerImplementation(
1161 const dbus::types::ObjectPath& session_path,
1162 const std::shared_ptr<Service>& service,
1163- const std::shared_ptr<Engine>& engine)
1164+ PlayerKey key)
1165 : media::PlayerSkeleton(session_path),
1166 d(new Private(
1167 this,
1168 session_path,
1169 service,
1170- engine))
1171+ key))
1172 {
1173 // Initializing default values for properties
1174 can_play().set(true);
1175@@ -76,6 +168,8 @@
1176 can_seek().set(true);
1177 can_go_previous().set(true);
1178 can_go_next().set(true);
1179+ is_video_source().set(false);
1180+ is_audio_source().set(false);
1181 is_shuffle().set(true);
1182 playback_rate().set(1.f);
1183 playback_status().set(Player::PlaybackStatus::null);
1184@@ -98,6 +192,43 @@
1185 return d->engine->duration().get();
1186 };
1187 duration().install(duration_getter);
1188+
1189+ std::function<bool()> video_type_getter = [this]()
1190+ {
1191+ return d->engine->is_video_source().get();
1192+ };
1193+ is_video_source().install(video_type_getter);
1194+
1195+ std::function<bool()> audio_type_getter = [this]()
1196+ {
1197+ return d->engine->is_audio_source().get();
1198+ };
1199+ is_audio_source().install(audio_type_getter);
1200+
1201+ d->engine->about_to_finish_signal().connect([this]()
1202+ {
1203+ if (d->track_list->has_next())
1204+ {
1205+ Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
1206+ if (!uri.empty())
1207+ d->parent->open_uri(uri);
1208+ }
1209+ });
1210+
1211+ d->engine->seeked_to_signal().connect([this](uint64_t value)
1212+ {
1213+ seeked_to()(value);
1214+ });
1215+
1216+ d->engine->end_of_stream_signal().connect([this]()
1217+ {
1218+ end_of_stream()();
1219+ });
1220+
1221+ d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status)
1222+ {
1223+ playback_status_changed()(status);
1224+ });
1225 }
1226
1227 media::PlayerImplementation::~PlayerImplementation()
1228@@ -109,11 +240,28 @@
1229 return d->track_list;
1230 }
1231
1232+// TODO: Convert this to be a property instead of sync call
1233+media::Player::PlayerKey media::PlayerImplementation::key() const
1234+{
1235+ return d->key;
1236+}
1237+
1238 bool media::PlayerImplementation::open_uri(const Track::UriType& uri)
1239 {
1240 return d->engine->open_resource_for_uri(uri);
1241 }
1242
1243+void media::PlayerImplementation::create_video_sink(uint32_t texture_id)
1244+{
1245+ d->engine->create_video_sink(texture_id);
1246+}
1247+
1248+media::Player::GLConsumerWrapperHybris media::PlayerImplementation::gl_consumer() const
1249+{
1250+ // This method is client-side only and is simply a no-op for the service side
1251+ return NULL;
1252+}
1253+
1254 void media::PlayerImplementation::next()
1255 {
1256 }
1257@@ -124,11 +272,6 @@
1258
1259 void media::PlayerImplementation::play()
1260 {
1261- /*if (playback_status() == media::Player::null)
1262- {
1263- if (d->track_list->has_next())
1264- if (open_uri(d->track_list->next()->))
1265- }*/
1266 d->engine->play();
1267 }
1268
1269@@ -142,6 +285,18 @@
1270 d->engine->stop();
1271 }
1272
1273+void media::PlayerImplementation::set_frame_available_callback(
1274+ UNUSED FrameAvailableCb cb, UNUSED void *context)
1275+{
1276+ // This method is client-side only and is simply a no-op for the service side
1277+}
1278+
1279+void media::PlayerImplementation::set_playback_complete_callback(
1280+ UNUSED PlaybackCompleteCb cb, UNUSED void *context)
1281+{
1282+ // This method is client-side only and is simply a no-op for the service side
1283+}
1284+
1285 void media::PlayerImplementation::seek_to(const std::chrono::microseconds& ms)
1286 {
1287 d->engine->seek_to(ms);
1288
1289=== modified file 'src/core/media/player_implementation.h'
1290--- src/core/media/player_implementation.h 2014-02-18 20:25:21 +0000
1291+++ src/core/media/player_implementation.h 2014-04-30 01:13:03 +0000
1292@@ -1,4 +1,5 @@
1293 /*
1294+ * Copyright © 2013-2014 Canonical Ltd.
1295 *
1296 * This program is free software: you can redistribute it and/or modify it
1297 * under the terms of the GNU Lesser General Public License version 3,
1298@@ -37,17 +38,22 @@
1299 PlayerImplementation(
1300 const core::dbus::types::ObjectPath& session_path,
1301 const std::shared_ptr<Service>& service,
1302- const std::shared_ptr<Engine>& engine);
1303+ PlayerKey key);
1304 ~PlayerImplementation();
1305
1306 virtual std::shared_ptr<TrackList> track_list();
1307+ virtual PlayerKey key() const;
1308
1309 virtual bool open_uri(const Track::UriType& uri);
1310+ virtual void create_video_sink(uint32_t texture_id);
1311+ virtual GLConsumerWrapperHybris gl_consumer() const;
1312 virtual void next();
1313 virtual void previous();
1314 virtual void play();
1315 virtual void pause();
1316 virtual void stop();
1317+ virtual void set_frame_available_callback(FrameAvailableCb cb, void *context);
1318+ virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context);
1319 virtual void seek_to(const std::chrono::microseconds& offset);
1320
1321 private:
1322
1323=== modified file 'src/core/media/player_skeleton.cpp'
1324--- src/core/media/player_skeleton.cpp 2014-02-20 19:05:17 +0000
1325+++ src/core/media/player_skeleton.cpp 2014-04-30 01:13:03 +0000
1326@@ -1,5 +1,5 @@
1327 /*
1328- * Copyright © 2013 Canonical Ltd.
1329+ * Copyright © 2013-2014 Canonical Ltd.
1330 *
1331 * This program is free software: you can redistribute it and/or modify it
1332 * under the terms of the GNU Lesser General Public License version 3,
1333@@ -16,6 +16,7 @@
1334 * Authored by: Thomas Voß <thomas.voss@canonical.com>
1335 */
1336
1337+#include "apparmor.h"
1338 #include "codec.h"
1339 #include "player_skeleton.h"
1340 #include "player_traits.h"
1341@@ -27,6 +28,7 @@
1342 #include <core/dbus/object.h>
1343 #include <core/dbus/property.h>
1344 #include <core/dbus/stub.h>
1345+#include <core/dbus/asio/executor.h>
1346
1347 namespace dbus = core::dbus;
1348 namespace media = core::ubuntu::media;
1349@@ -36,6 +38,7 @@
1350 Private(media::PlayerSkeleton* player, const dbus::types::ObjectPath& session)
1351 : impl(player),
1352 object(impl->access_service()->add_object_for_path(session)),
1353+ apparmor_session(nullptr),
1354 properties
1355 {
1356 object->get_property<mpris::Player::Properties::CanPlay>(),
1357@@ -44,6 +47,8 @@
1358 object->get_property<mpris::Player::Properties::CanControl>(),
1359 object->get_property<mpris::Player::Properties::CanGoNext>(),
1360 object->get_property<mpris::Player::Properties::CanGoPrevious>(),
1361+ object->get_property<mpris::Player::Properties::IsVideoSource>(),
1362+ object->get_property<mpris::Player::Properties::IsAudioSource>(),
1363 object->get_property<mpris::Player::Properties::PlaybackStatus>(),
1364 object->get_property<mpris::Player::Properties::LoopStatus>(),
1365 object->get_property<mpris::Player::Properties::PlaybackRate>(),
1366@@ -54,6 +59,12 @@
1367 object->get_property<mpris::Player::Properties::Duration>(),
1368 object->get_property<mpris::Player::Properties::MinimumRate>(),
1369 object->get_property<mpris::Player::Properties::MaximumRate>()
1370+ },
1371+ signals
1372+ {
1373+ object->get_signal<mpris::Player::Signals::Seeked>(),
1374+ object->get_signal<mpris::Player::Signals::EndOfStream>(),
1375+ object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
1376 }
1377 {
1378 }
1379@@ -111,18 +122,130 @@
1380 {
1381 }
1382
1383+ void handle_create_video_sink(const core::dbus::Message::Ptr& in)
1384+ {
1385+ uint32_t texture_id;
1386+ in->reader() >> texture_id;
1387+ impl->create_video_sink(texture_id);
1388+
1389+ auto reply = dbus::Message::make_method_return(in);
1390+ impl->access_bus()->send(reply);
1391+ }
1392+
1393+ std::string get_client_apparmor_context(const core::dbus::Message::Ptr& msg)
1394+ {
1395+ auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
1396+ bus->install_executor(dbus::asio::make_executor(bus));
1397+
1398+ auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Apparmor>::interface_name());
1399+ apparmor_session = stub_service->object_for_path(dbus::types::ObjectPath("/org/freedesktop/DBus"));
1400+ // Get the AppArmor security context for the client
1401+ auto result = apparmor_session->invoke_method_synchronously<core::Apparmor::getConnectionAppArmorSecurityContext, std::string>(msg->sender());
1402+ if (result.is_error())
1403+ {
1404+ std::cout << "Error getting apparmor profile: " << result.error().print() << std::endl;
1405+ return std::string();
1406+ }
1407+
1408+ return result.value();
1409+ }
1410+
1411+ bool does_client_have_access(const std::string& context, const std::string& uri)
1412+ {
1413+ if (context.empty() || uri.empty())
1414+ {
1415+ std::cout << "Client denied access since context or uri are empty" << std::endl;
1416+ return false;
1417+ }
1418+
1419+ if (context == "unconfined")
1420+ {
1421+ std::cout << "Client allowed access since it's unconfined" << std::endl;
1422+ return true;
1423+ }
1424+
1425+ size_t pos = context.find_first_of('_');
1426+ if (pos == std::string::npos)
1427+ {
1428+ std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl;
1429+ return false;
1430+ }
1431+
1432+ const std::string pkgname = context.substr(0, pos);
1433+ std::cout << "client pkgname: " << pkgname << std::endl;
1434+ std::cout << "uri: " << uri << std::endl;
1435+
1436+ // All confined apps can access their own files
1437+ if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos
1438+ || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos)
1439+ {
1440+ std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl;
1441+ return true;
1442+ }
1443+ else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos
1444+ && uri.find(pkgname) != std::string::npos)
1445+ {
1446+ std::cout << "Client can access content in own opt directory" << std::endl;
1447+ return true;
1448+ }
1449+ else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos
1450+ || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos)
1451+ && pkgname == "com.ubuntu.camera")
1452+ {
1453+ std::cout << "Camera app can access ui sounds" << std::endl;
1454+ return true;
1455+ }
1456+ // TODO: Check if the trust store previously allowed direct access to uri
1457+
1458+ // Check in ~/Music and ~/Videos
1459+ // TODO: when the trust store lands, check it to see if this app can access the dirs and
1460+ // then remove the explicit whitelist of the music-app
1461+ else if (pkgname == "com.ubuntu.music" &&
1462+ (uri.find(std::string("Music/")) != std::string::npos
1463+ || uri.find(std::string("Videos/")) != std::string::npos))
1464+ {
1465+ std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl;
1466+ return true;
1467+ }
1468+ else if (uri.find(std::string("http://")) != std::string::npos
1469+ || uri.find(std::string("rtsp://")) != std::string::npos)
1470+ {
1471+ std::cout << "Client can access streaming content" << std::endl;
1472+ return true;
1473+ }
1474+ else
1475+ {
1476+ std::cout << "Client denied access to open_uri()" << std::endl;
1477+ return false;
1478+ }
1479+ }
1480+
1481+ void handle_key(const core::dbus::Message::Ptr& in)
1482+ {
1483+ auto reply = dbus::Message::make_method_return(in);
1484+ reply->writer() << impl->key();
1485+ impl->access_bus()->send(reply);
1486+ }
1487+
1488 void handle_open_uri(const core::dbus::Message::Ptr& in)
1489 {
1490 Track::UriType uri;
1491 in->reader() >> uri;
1492
1493+ std::string context = get_client_apparmor_context(in);
1494+ bool have_access = does_client_have_access(context, uri);
1495+
1496 auto reply = dbus::Message::make_method_return(in);
1497- reply->writer() << impl->open_uri(uri);
1498+ if (have_access)
1499+ reply->writer() << impl->open_uri(uri);
1500+ else
1501+ reply->writer() << false;
1502 impl->access_bus()->send(reply);
1503 }
1504
1505 media::PlayerSkeleton* impl;
1506 dbus::Object::Ptr object;
1507+ dbus::Object::Ptr apparmor_session;
1508 struct
1509 {
1510 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
1511@@ -131,6 +254,8 @@
1512 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
1513 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
1514 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
1515+ std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
1516+ std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
1517
1518 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status;
1519 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status;
1520@@ -144,10 +269,51 @@
1521 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
1522 } properties;
1523
1524- /*struct
1525+ struct Signals
1526 {
1527- std::shared_ptr<dbus::Signal<mpris::Player::Signals::Seeked, uint64_t>> seeked;
1528- } signals;*/
1529+ typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
1530+ typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
1531+ typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
1532+
1533+ Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
1534+ const std::shared_ptr<DBusEndOfStreamSignal>& eos,
1535+ const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status)
1536+ : dbus
1537+ {
1538+ seeked,
1539+ eos,
1540+ status
1541+ },
1542+ seeked_to(),
1543+ end_of_stream(),
1544+ playback_status_changed()
1545+ {
1546+ seeked_to.connect([this](std::uint64_t value)
1547+ {
1548+ dbus.seeked_to->emit(value);
1549+ });
1550+
1551+ end_of_stream.connect([this]()
1552+ {
1553+ dbus.end_of_stream->emit();
1554+ });
1555+
1556+ playback_status_changed.connect([this](const media::Player::PlaybackStatus& status)
1557+ {
1558+ dbus.playback_status_changed->emit(status);
1559+ });
1560+ }
1561+
1562+ struct DBus
1563+ {
1564+ std::shared_ptr<DBusSeekedToSignal> seeked_to;
1565+ std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
1566+ std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
1567+ } dbus;
1568+ core::Signal<uint64_t> seeked_to;
1569+ core::Signal<void> end_of_stream;
1570+ core::Signal<media::Player::PlaybackStatus> playback_status_changed;
1571+ } signals;
1572
1573 };
1574
1575@@ -184,6 +350,14 @@
1576 std::bind(&Private::handle_set_position,
1577 std::ref(d),
1578 std::placeholders::_1));
1579+ d->object->install_method_handler<mpris::Player::CreateVideoSink>(
1580+ std::bind(&Private::handle_create_video_sink,
1581+ std::ref(d),
1582+ std::placeholders::_1));
1583+ d->object->install_method_handler<mpris::Player::Key>(
1584+ std::bind(&Private::handle_key,
1585+ std::ref(d),
1586+ std::placeholders::_1));
1587 d->object->install_method_handler<mpris::Player::OpenUri>(
1588 std::bind(&Private::handle_open_uri,
1589 std::ref(d),
1590@@ -219,6 +393,16 @@
1591 return *d->properties.can_go_next;
1592 }
1593
1594+const core::Property<bool>& media::PlayerSkeleton::is_video_source() const
1595+{
1596+ return *d->properties.is_video_source;
1597+}
1598+
1599+const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const
1600+{
1601+ return *d->properties.is_audio_source;
1602+}
1603+
1604 const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const
1605 {
1606 return *d->properties.playback_status;
1607@@ -329,6 +513,17 @@
1608 return *d->properties.can_go_next;
1609 }
1610
1611+core::Property<bool>& media::PlayerSkeleton::is_video_source()
1612+{
1613+ return *d->properties.is_video_source;
1614+}
1615+
1616+core::Property<bool>& media::PlayerSkeleton::is_audio_source()
1617+{
1618+ return *d->properties.is_audio_source;
1619+}
1620+
1621+
1622 core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
1623 {
1624 return *d->properties.meta_data_for_current_track;
1625@@ -346,6 +541,25 @@
1626
1627 const core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to() const
1628 {
1629- static const core::Signal<uint64_t> signal;
1630- return signal;
1631+ return d->signals.seeked_to;
1632+}
1633+
1634+core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to()
1635+{
1636+ return d->signals.seeked_to;
1637+}
1638+
1639+const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const
1640+{
1641+ return d->signals.end_of_stream;
1642+}
1643+
1644+core::Signal<void>& media::PlayerSkeleton::end_of_stream()
1645+{
1646+ return d->signals.end_of_stream;
1647+}
1648+
1649+core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed()
1650+{
1651+ return d->signals.playback_status_changed;
1652 }
1653
1654=== modified file 'src/core/media/player_skeleton.h'
1655--- src/core/media/player_skeleton.h 2014-02-18 19:27:01 +0000
1656+++ src/core/media/player_skeleton.h 2014-04-30 01:13:03 +0000
1657@@ -1,4 +1,6 @@
1658 /**
1659+ * Copyright (C) 2013-2014 Canonical Ltd
1660+ *
1661 * This program is free software: you can redistribute it and/or modify it
1662 * under the terms of the GNU Lesser General Public License version 3,
1663 * as published by the Free Software Foundation.
1664@@ -46,6 +48,8 @@
1665 virtual const core::Property<bool>& can_seek() const;
1666 virtual const core::Property<bool>& can_go_previous() const;
1667 virtual const core::Property<bool>& can_go_next() const;
1668+ virtual const core::Property<bool>& is_video_source() const;
1669+ virtual const core::Property<bool>& is_audio_source() const;
1670 virtual const core::Property<PlaybackStatus>& playback_status() const;
1671 virtual const core::Property<LoopStatus>& loop_status() const;
1672 virtual const core::Property<PlaybackRate>& playback_rate() const;
1673@@ -63,6 +67,8 @@
1674 virtual core::Property<Volume>& volume();
1675
1676 virtual const core::Signal<uint64_t>& seeked_to() const;
1677+ virtual const core::Signal<void>& end_of_stream() const;
1678+ virtual core::Signal<PlaybackStatus>& playback_status_changed();
1679
1680 protected:
1681 PlayerSkeleton(const core::dbus::types::ObjectPath& session_path);
1682@@ -73,12 +79,17 @@
1683 virtual core::Property<bool>& can_seek();
1684 virtual core::Property<bool>& can_go_previous();
1685 virtual core::Property<bool>& can_go_next();
1686+ virtual core::Property<bool>& is_video_source();
1687+ virtual core::Property<bool>& is_audio_source();
1688 virtual core::Property<Track::MetaData>& meta_data_for_current_track();
1689 virtual core::Property<PlaybackRate>& minimum_playback_rate();
1690 virtual core::Property<PlaybackRate>& maximum_playback_rate();
1691 virtual core::Property<uint64_t>& position();
1692 virtual core::Property<uint64_t>& duration();
1693
1694+ virtual core::Signal<uint64_t>& seeked_to();
1695+ virtual core::Signal<void>& end_of_stream();
1696+
1697 private:
1698 struct Private;
1699 std::unique_ptr<Private> d;
1700
1701=== modified file 'src/core/media/player_stub.cpp'
1702--- src/core/media/player_stub.cpp 2014-02-20 19:05:17 +0000
1703+++ src/core/media/player_stub.cpp 2014-04-30 01:13:03 +0000
1704@@ -1,5 +1,5 @@
1705 /*
1706- * Copyright © 2013 Canonical Ltd.
1707+ * Copyright © 2013-2014 Canonical Ltd.
1708 *
1709 * This program is free software: you can redistribute it and/or modify it
1710 * under the terms of the GNU Lesser General Public License version 3,
1711@@ -31,8 +31,14 @@
1712 #include <core/dbus/property.h>
1713 #include <core/dbus/types/object_path.h>
1714
1715+// Hybris
1716+#include <hybris/media/media_codec_layer.h>
1717+#include <hybris/media/surface_texture_client_hybris.h>
1718+
1719 #include <limits>
1720
1721+#define UNUSED __attribute__((unused))
1722+
1723 namespace dbus = core::dbus;
1724 namespace media = core::ubuntu::media;
1725
1726@@ -42,6 +48,12 @@
1727 const std::shared_ptr<dbus::Service>& remote,
1728 const dbus::types::ObjectPath& path
1729 ) : parent(parent),
1730+ texture_id(0),
1731+ igbc_wrapper(nullptr),
1732+ glc_wrapper(nullptr),
1733+ decoding_session(decoding_service_create_session()),
1734+ frame_available_cb(nullptr),
1735+ frame_available_context(nullptr),
1736 path(path),
1737 object(remote->object_for_path(path)),
1738 properties
1739@@ -52,6 +64,8 @@
1740 object->get_property<mpris::Player::Properties::CanControl>(),
1741 object->get_property<mpris::Player::Properties::CanGoNext>(),
1742 object->get_property<mpris::Player::Properties::CanGoPrevious>(),
1743+ object->get_property<mpris::Player::Properties::IsVideoSource>(),
1744+ object->get_property<mpris::Player::Properties::IsAudioSource>(),
1745 object->get_property<mpris::Player::Properties::PlaybackStatus>(),
1746 object->get_property<mpris::Player::Properties::LoopStatus>(),
1747 object->get_property<mpris::Player::Properties::PlaybackRate>(),
1748@@ -62,13 +76,68 @@
1749 object->get_property<mpris::Player::Properties::Duration>(),
1750 object->get_property<mpris::Player::Properties::MinimumRate>(),
1751 object->get_property<mpris::Player::Properties::MaximumRate>()
1752+ },
1753+ signals
1754+ {
1755+ object->get_signal<mpris::Player::Signals::Seeked>(),
1756+ object->get_signal<mpris::Player::Signals::EndOfStream>(),
1757+ object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
1758 }
1759 {
1760 }
1761
1762+ ~Private()
1763+ {
1764+ }
1765+
1766+ static void on_frame_available_cb(UNUSED GLConsumerWrapperHybris wrapper, void *context)
1767+ {
1768+ if (context != nullptr) {
1769+ Private *p = static_cast<Private*>(context);
1770+ p->on_frame_available();
1771+ }
1772+ else
1773+ std::cout << "context is nullptr, can't call on_frame_available()" << std::endl;
1774+ }
1775+
1776+ void on_frame_available()
1777+ {
1778+ if (frame_available_cb != nullptr) {
1779+ frame_available_cb(frame_available_context);
1780+ }
1781+ else
1782+ std::cout << "frame_available_cb is nullptr, can't call frame_available_cb()" << std::endl;
1783+ }
1784+
1785+ void set_frame_available_cb(FrameAvailableCb cb, void *context)
1786+ {
1787+ frame_available_cb = cb;
1788+ frame_available_context = context;
1789+
1790+ gl_consumer_set_frame_available_cb(glc_wrapper, &Private::on_frame_available_cb, static_cast<void*>(this));
1791+ }
1792+
1793+ /** We need a GLConsumerHybris instance for doing texture streaming over the
1794+ * process boundary **/
1795+ void get_gl_consumer()
1796+ {
1797+ igbc_wrapper = decoding_service_get_igraphicbufferconsumer();
1798+ glc_wrapper = gl_consumer_create_by_id_with_igbc(texture_id, igbc_wrapper);
1799+
1800+ }
1801+
1802 std::shared_ptr<Service> parent;
1803 std::shared_ptr<TrackList> track_list;
1804
1805+ uint32_t texture_id;
1806+ IGBCWrapperHybris igbc_wrapper;
1807+ GLConsumerWrapperHybris glc_wrapper;
1808+
1809+ DSSessionWrapperHybris decoding_session;
1810+
1811+ FrameAvailableCb frame_available_cb;
1812+ void *frame_available_context;
1813+
1814 dbus::Bus::Ptr bus;
1815 dbus::types::ObjectPath path;
1816 dbus::Object::Ptr object;
1817@@ -81,6 +150,8 @@
1818 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
1819 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
1820 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
1821+ std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
1822+ std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
1823
1824 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status;
1825 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status;
1826@@ -93,6 +164,68 @@
1827 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
1828 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
1829 } properties;
1830+
1831+ struct Signals
1832+ {
1833+ typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
1834+ typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
1835+ typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
1836+
1837+ Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
1838+ const std::shared_ptr<DBusEndOfStreamSignal>& eos,
1839+ const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status)
1840+ : dbus
1841+ {
1842+ seeked,
1843+ eos,
1844+ status
1845+ },
1846+ playback_complete_cb(nullptr),
1847+ playback_complete_context(nullptr),
1848+ seeked_to(),
1849+ end_of_stream(),
1850+ playback_status_changed()
1851+ {
1852+ dbus.seeked_to->connect([this](std::uint64_t value)
1853+ {
1854+ std::cout << "seeked_to signal arrived via the bus." << std::endl;
1855+ seeked_to(value);
1856+ });
1857+
1858+ dbus.end_of_stream->connect([this]()
1859+ {
1860+ std::cout << "EndOfStream signal arrived via the bus." << std::endl;
1861+ if (playback_complete_cb)
1862+ playback_complete_cb(playback_complete_context);
1863+ end_of_stream();
1864+ });
1865+
1866+ dbus.playback_status_changed->connect([this](const media::Player::PlaybackStatus& status)
1867+ {
1868+ std::cout << "PlaybackStatusChanged signal arrived via the bus." << std::endl;
1869+ playback_status_changed(status);
1870+ });
1871+ }
1872+
1873+ struct DBus
1874+ {
1875+ std::shared_ptr<DBusSeekedToSignal> seeked_to;
1876+ std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
1877+ std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
1878+ } dbus;
1879+
1880+ void set_playback_complete_cb(PlaybackCompleteCb cb, void *context)
1881+ {
1882+ playback_complete_cb = cb;
1883+ playback_complete_context = context;
1884+ }
1885+
1886+ PlaybackCompleteCb playback_complete_cb;
1887+ void *playback_complete_context;
1888+ core::Signal<uint64_t> seeked_to;
1889+ core::Signal<void> end_of_stream;
1890+ core::Signal<media::Player::PlaybackStatus> playback_status_changed;
1891+ } signals;
1892 };
1893
1894 media::PlayerStub::PlayerStub(
1895@@ -101,10 +234,20 @@
1896 : dbus::Stub<Player>(the_session_bus()),
1897 d(new Private{parent, access_service(), object_path})
1898 {
1899+ auto bus = the_session_bus();
1900+ worker = std::move(std::thread([bus]()
1901+ {
1902+ bus->run();
1903+ }));
1904 }
1905
1906 media::PlayerStub::~PlayerStub()
1907 {
1908+ auto bus = the_session_bus();
1909+ bus->stop();
1910+
1911+ if (worker.joinable())
1912+ worker.join();
1913 }
1914
1915 std::shared_ptr<media::TrackList> media::PlayerStub::track_list()
1916@@ -118,6 +261,13 @@
1917 return d->track_list;
1918 }
1919
1920+media::Player::PlayerKey media::PlayerStub::key() const
1921+{
1922+ auto op = d->object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>();
1923+
1924+ return op.value();
1925+}
1926+
1927 bool media::PlayerStub::open_uri(const media::Track::UriType& uri)
1928 {
1929 auto op = d->object->invoke_method_synchronously<mpris::Player::OpenUri, bool>(uri);
1930@@ -125,6 +275,21 @@
1931 return op.value();
1932 }
1933
1934+void media::PlayerStub::create_video_sink(uint32_t texture_id)
1935+{
1936+ auto op = d->object->invoke_method_synchronously<mpris::Player::CreateVideoSink, void>(texture_id);
1937+ d->texture_id = texture_id;
1938+ d->get_gl_consumer();
1939+
1940+ if (op.is_error())
1941+ throw std::runtime_error("Problem creating new video sink instance on remote object");
1942+}
1943+
1944+GLConsumerWrapperHybris media::PlayerStub::gl_consumer() const
1945+{
1946+ return d->glc_wrapper;
1947+}
1948+
1949 void media::PlayerStub::next()
1950 {
1951 auto op = d->object->invoke_method_synchronously<mpris::Player::Next, void>();
1952@@ -173,6 +338,16 @@
1953 throw std::runtime_error("Problem stopping playback on remote object");
1954 }
1955
1956+void media::PlayerStub::set_frame_available_callback(FrameAvailableCb cb, void *context)
1957+{
1958+ d->set_frame_available_cb(cb, context);
1959+}
1960+
1961+void media::PlayerStub::set_playback_complete_callback(PlaybackCompleteCb cb, void *context)
1962+{
1963+ d->signals.set_playback_complete_cb(cb, context);
1964+}
1965+
1966 const core::Property<bool>& media::PlayerStub::can_play() const
1967 {
1968 return *d->properties.can_play;
1969@@ -198,6 +373,16 @@
1970 return *d->properties.can_go_next;
1971 }
1972
1973+const core::Property<bool>& media::PlayerStub::is_video_source() const
1974+{
1975+ return *d->properties.is_video_source;
1976+}
1977+
1978+const core::Property<bool>& media::PlayerStub::is_audio_source() const
1979+{
1980+ return *d->properties.is_audio_source;
1981+}
1982+
1983 const core::Property<media::Player::PlaybackStatus>& media::PlayerStub::playback_status() const
1984 {
1985 return *d->properties.playback_status;
1986@@ -270,6 +455,15 @@
1987
1988 const core::Signal<uint64_t>& media::PlayerStub::seeked_to() const
1989 {
1990- static core::Signal<uint64_t> signal;
1991- return signal;
1992+ return d->signals.seeked_to;
1993+}
1994+
1995+const core::Signal<void>& media::PlayerStub::end_of_stream() const
1996+{
1997+ return d->signals.end_of_stream;
1998+}
1999+
2000+core::Signal<media::Player::PlaybackStatus>& media::PlayerStub::playback_status_changed()
2001+{
2002+ return d->signals.playback_status_changed;
2003 }
2004
2005=== modified file 'src/core/media/player_stub.h'
2006--- src/core/media/player_stub.h 2014-02-18 19:27:01 +0000
2007+++ src/core/media/player_stub.h 2014-04-30 01:13:03 +0000
2008@@ -43,8 +43,11 @@
2009 ~PlayerStub();
2010
2011 virtual std::shared_ptr<TrackList> track_list();
2012+ virtual PlayerKey key() const;
2013
2014 virtual bool open_uri(const Track::UriType& uri);
2015+ virtual void create_video_sink(uint32_t texture_id);
2016+ virtual GLConsumerWrapperHybris gl_consumer() const;
2017 virtual void next();
2018 virtual void previous();
2019 virtual void play();
2020@@ -52,11 +55,16 @@
2021 virtual void seek_to(const std::chrono::microseconds& offset);
2022 virtual void stop();
2023
2024+ virtual void set_frame_available_callback(FrameAvailableCb cb, void *context);
2025+ virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context);
2026+
2027 virtual const core::Property<bool>& can_play() const;
2028 virtual const core::Property<bool>& can_pause() const;
2029 virtual const core::Property<bool>& can_seek() const;
2030 virtual const core::Property<bool>& can_go_previous() const;
2031 virtual const core::Property<bool>& can_go_next() const;
2032+ virtual const core::Property<bool>& is_video_source() const;
2033+ virtual const core::Property<bool>& is_audio_source() const;
2034 virtual const core::Property<PlaybackStatus>& playback_status() const;
2035 virtual const core::Property<LoopStatus>& loop_status() const;
2036 virtual const core::Property<PlaybackRate>& playback_rate() const;
2037@@ -74,10 +82,13 @@
2038 virtual core::Property<Volume>& volume();
2039
2040 virtual const core::Signal<uint64_t>& seeked_to() const;
2041+ virtual const core::Signal<void>& end_of_stream() const;
2042+ virtual core::Signal<PlaybackStatus>& playback_status_changed();
2043
2044 private:
2045 struct Private;
2046 std::unique_ptr<Private> d;
2047+ std::thread worker;
2048 };
2049 }
2050 }
2051
2052=== added file 'src/core/media/powerd_service.h'
2053--- src/core/media/powerd_service.h 1970-01-01 00:00:00 +0000
2054+++ src/core/media/powerd_service.h 2014-04-30 01:13:03 +0000
2055@@ -0,0 +1,99 @@
2056+/*
2057+ * Copyright (C) 2014 Canonical Ltd
2058+ *
2059+ * This program is free software: you can redistribute it and/or modify
2060+ * it under the terms of the GNU Lesser General Public License version 3 as
2061+ * published by the Free Software Foundation.
2062+ *
2063+ * This program is distributed in the hope that it will be useful,
2064+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2065+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2066+ * GNU Lesser General Public License for more details.
2067+ *
2068+ * You should have received a copy of the GNU Lesser General Public License
2069+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2070+ *
2071+ * Author: Ricardo Mendoza <ricardo.mendoza@canonical.com>
2072+ */
2073+
2074+#include <core/dbus/dbus.h>
2075+#include <core/dbus/fixture.h>
2076+#include <core/dbus/object.h>
2077+#include <core/dbus/property.h>
2078+#include <core/dbus/service.h>
2079+#include <core/dbus/interfaces/properties.h>
2080+#include <core/dbus/types/stl/tuple.h>
2081+#include <core/dbus/types/stl/vector.h>
2082+
2083+#include <core/dbus/asio/executor.h>
2084+
2085+#include <string>
2086+#include <vector>
2087+#include <chrono>
2088+
2089+namespace core
2090+{
2091+
2092+struct Powerd
2093+{
2094+ static std::string& name()
2095+ {
2096+ static std::string s = "com.canonical.powerd";
2097+ return s;
2098+ }
2099+
2100+ struct requestDisplayState
2101+ {
2102+ static std::string name()
2103+ {
2104+ static std::string s = "requestDisplayState";
2105+ return s;
2106+ }
2107+
2108+ static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2109+
2110+ typedef Powerd Interface;
2111+ };
2112+
2113+ struct clearDisplayState
2114+ {
2115+ static std::string name()
2116+ {
2117+ static std::string s = "clearDisplayState";
2118+ return s;
2119+ }
2120+
2121+ static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2122+
2123+ typedef Powerd Interface;
2124+ };
2125+
2126+ struct requestSysState
2127+ {
2128+ static std::string name()
2129+ {
2130+ static std::string s = "requestSysState";
2131+ return s;
2132+ }
2133+
2134+ static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2135+
2136+ typedef Powerd Interface;
2137+ };
2138+
2139+ struct clearSysState
2140+ {
2141+ static std::string name()
2142+ {
2143+ static std::string s = "clearSysState";
2144+ return s;
2145+ }
2146+
2147+ static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2148+
2149+ typedef Powerd Interface;
2150+ };
2151+
2152+};
2153+
2154+}
2155
2156=== modified file 'src/core/media/server/server.cpp'
2157--- src/core/media/server/server.cpp 2014-02-12 15:53:57 +0000
2158+++ src/core/media/server/server.cpp 2014-04-30 01:13:03 +0000
2159@@ -1,13 +1,41 @@
2160+/*
2161+ * Copyright (C) 2014 Canonical Ltd
2162+ *
2163+ * Licensed under the Apache License, Version 2.0 (the "License");
2164+ * you may not use this file except in compliance with the License.
2165+ * You may obtain a copy of the License at
2166+ *
2167+ * http://www.apache.org/licenses/LICENSE-2.0
2168+ *
2169+ * Unless required by applicable law or agreed to in writing, software
2170+ * distributed under the License is distributed on an "AS IS" BASIS,
2171+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2172+ * See the License for the specific language governing permissions and
2173+ * limitations under the License.
2174+ *
2175+ * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
2176+ */
2177+
2178 #include <core/media/service.h>
2179 #include <core/media/player.h>
2180 #include <core/media/track_list.h>
2181
2182+#include <hybris/media/media_codec_layer.h>
2183+
2184 #include "core/media/service_implementation.h"
2185
2186+#include <iostream>
2187+
2188 namespace media = core::ubuntu::media;
2189
2190+using namespace std;
2191+
2192 int main()
2193 {
2194+ // Init hybris-level DecodingService
2195+ decoding_service_init();
2196+ cout << "Starting DecodingService..." << endl;
2197+
2198 auto service = std::make_shared<media::ServiceImplementation>();
2199 service->run();
2200
2201
2202=== modified file 'src/core/media/service.cpp'
2203--- src/core/media/service.cpp 2014-01-17 20:58:31 +0000
2204+++ src/core/media/service.cpp 2014-04-30 01:13:03 +0000
2205@@ -24,7 +24,7 @@
2206
2207 const std::shared_ptr<media::Service> media::Service::Client::instance()
2208 {
2209- std::cout << "Creating a new Service instance" << std::endl;
2210+ std::cout << "Creating a new static Service instance" << std::endl;
2211 static std::shared_ptr<media::Service> instance{new media::ServiceStub()};
2212 return instance;
2213 }
2214
2215=== modified file 'src/core/media/service_implementation.cpp'
2216--- src/core/media/service_implementation.cpp 2014-01-13 21:51:14 +0000
2217+++ src/core/media/service_implementation.cpp 2014-04-30 01:13:03 +0000
2218@@ -21,21 +21,63 @@
2219 #include "player_configuration.h"
2220 #include "player_implementation.h"
2221
2222-#include "gstreamer/engine.h"
2223+#include <map>
2224
2225 namespace media = core::ubuntu::media;
2226
2227+using namespace std;
2228+
2229 struct media::ServiceImplementation::Private
2230 {
2231- Private() : engine(std::make_shared<gstreamer::Engine>())
2232- {
2233- }
2234- std::shared_ptr<media::Engine> engine;
2235+ Private()
2236+ : key_(0)
2237+ {
2238+ }
2239+
2240+ void track_player(const std::shared_ptr<media::Player>& player)
2241+ {
2242+ cout << __PRETTY_FUNCTION__ << endl;
2243+ cout << "key: " << key_ << endl;
2244+ player_map.insert(
2245+ std::pair<media::Player::PlayerKey,
2246+ std::shared_ptr<media::Player>>(key_, player));
2247+
2248+ ++key_;
2249+ }
2250+
2251+ inline media::Player::PlayerKey key() const
2252+ {
2253+ return key_;
2254+ }
2255+
2256+ void pause_other_sessions(media::Player::PlayerKey key)
2257+ {
2258+ cout << __PRETTY_FUNCTION__ << endl;
2259+ cout << "key: " << key << endl;
2260+
2261+ // TODO: Add a field to Player that is the type of player so that certain
2262+ // types of playback aren't paused below. E.g. a camera click sound shouldn't
2263+ // pause, nor should it pause background music sessions
2264+ for (auto& player_pair : player_map)
2265+ {
2266+ if (player_pair.second->playback_status() == Player::playing
2267+ && player_pair.first != key)
2268+ {
2269+ cout << "Pausing player with key: " << player_pair.first << endl;
2270+ player_pair.second->pause();
2271+ }
2272+ }
2273+ }
2274+
2275+ // Used for Player instance management
2276+ std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> player_map;
2277+ media::Player::PlayerKey key_;
2278+
2279 };
2280
2281 media::ServiceImplementation::ServiceImplementation() : d(new Private())
2282 {
2283-
2284+ cout << __PRETTY_FUNCTION__ << endl;
2285 }
2286
2287 media::ServiceImplementation::~ServiceImplementation()
2288@@ -45,8 +87,13 @@
2289 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
2290 const media::Player::Configuration& conf)
2291 {
2292- return std::make_shared<media::PlayerImplementation>(
2293- conf.object_path,
2294- shared_from_this(),
2295- d->engine);
2296+ std::shared_ptr<media::Player> player = std::make_shared<media::PlayerImplementation>(
2297+ conf.object_path, shared_from_this(), d->key());
2298+ d->track_player(player);
2299+ return player;
2300+}
2301+
2302+void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
2303+{
2304+ d->pause_other_sessions(key);
2305 }
2306
2307=== modified file 'src/core/media/service_implementation.h'
2308--- src/core/media/service_implementation.h 2014-01-13 21:51:14 +0000
2309+++ src/core/media/service_implementation.h 2014-04-30 01:13:03 +0000
2310@@ -27,6 +27,9 @@
2311 {
2312 namespace media
2313 {
2314+
2315+class Player;
2316+
2317 class ServiceImplementation : public ServiceSkeleton
2318 {
2319 public:
2320@@ -35,6 +38,8 @@
2321
2322 std::shared_ptr<Player> create_session(const Player::Configuration&);
2323
2324+ void pause_other_sessions(Player::PlayerKey key);
2325+
2326 private:
2327 struct Private;
2328 std::shared_ptr<Private> d;
2329
2330=== modified file 'src/core/media/service_skeleton.cpp'
2331--- src/core/media/service_skeleton.cpp 2014-02-12 15:53:57 +0000
2332+++ src/core/media/service_skeleton.cpp 2014-04-30 01:13:03 +0000
2333@@ -1,5 +1,5 @@
2334 /*
2335- * Copyright © 2013 Canonical Ltd.
2336+ * Copyright © 2013-2014 Canonical Ltd.
2337 *
2338 * This program is free software: you can redistribute it and/or modify it
2339 * under the terms of the GNU Lesser General Public License version 3,
2340@@ -49,6 +49,11 @@
2341 &Private::handle_create_session,
2342 this,
2343 std::placeholders::_1));
2344+ object->install_method_handler<mpris::Service::PauseOtherSessions>(
2345+ std::bind(
2346+ &Private::handle_pause_other_sessions,
2347+ this,
2348+ std::placeholders::_1));
2349 }
2350
2351 void handle_create_session(const core::dbus::Message::Ptr& msg)
2352@@ -86,6 +91,17 @@
2353 }
2354 }
2355
2356+ void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
2357+ {
2358+ std::cout << __PRETTY_FUNCTION__ << std::endl;
2359+ Player::PlayerKey key;
2360+ msg->reader() >> key;
2361+ impl->pause_other_sessions(key);
2362+
2363+ auto reply = dbus::Message::make_method_return(msg);
2364+ impl->access_bus()->send(reply);
2365+ }
2366+
2367 media::ServiceSkeleton* impl;
2368 dbus::Object::Ptr object;
2369
2370
2371=== modified file 'src/core/media/service_skeleton.h'
2372--- src/core/media/service_skeleton.h 2014-02-12 15:53:57 +0000
2373+++ src/core/media/service_skeleton.h 2014-04-30 01:13:03 +0000
2374@@ -1,5 +1,5 @@
2375 /*
2376- * Copyright © 2013 Canonical Ltd.
2377+ * Copyright © 2013-2014 Canonical Ltd.
2378 *
2379 * This program is free software: you can redistribute it and/or modify it
2380 * under the terms of the GNU Lesser General Public License version 3,
2381
2382=== modified file 'src/core/media/service_stub.cpp'
2383--- src/core/media/service_stub.cpp 2014-02-12 15:53:57 +0000
2384+++ src/core/media/service_stub.cpp 2014-04-30 01:13:03 +0000
2385@@ -1,5 +1,5 @@
2386 /*
2387- * Copyright © 2013 Canonical Ltd.
2388+ * Copyright © 2013-2014 Canonical Ltd.
2389 *
2390 * This program is free software: you can redistribute it and/or modify it
2391 * under the terms of the GNU Lesser General Public License version 3,
2392@@ -47,13 +47,21 @@
2393
2394 std::shared_ptr<media::Player> media::ServiceStub::create_session(const media::Player::Configuration&)
2395 {
2396- auto op
2397- = d->object->invoke_method_synchronously<
2398- mpris::Service::CreateSession,
2399- dbus::types::ObjectPath>();
2400+ auto op = d->object->invoke_method_synchronously<mpris::Service::CreateSession,
2401+ dbus::types::ObjectPath>();
2402
2403 if (op.is_error())
2404 throw std::runtime_error("Problem creating session: " + op.error());
2405
2406 return std::shared_ptr<media::Player>(new media::PlayerStub(shared_from_this(), op.value()));
2407 }
2408+
2409+void media::ServiceStub::pause_other_sessions(media::Player::PlayerKey key)
2410+{
2411+ std::cout << __PRETTY_FUNCTION__ << std::endl;
2412+ auto op = d->object->invoke_method_synchronously<mpris::Service::PauseOtherSessions,
2413+ void>(key);
2414+
2415+ if (op.is_error())
2416+ throw std::runtime_error("Problem pausing other sessions: " + op.error());
2417+}
2418
2419=== modified file 'src/core/media/service_stub.h'
2420--- src/core/media/service_stub.h 2014-02-12 15:53:57 +0000
2421+++ src/core/media/service_stub.h 2014-04-30 01:13:03 +0000
2422@@ -1,5 +1,5 @@
2423 /*
2424- * Copyright © 2013 Canonical Ltd.
2425+ * Copyright © 2013-2014 Canonical Ltd.
2426 *
2427 * This program is free software: you can redistribute it and/or modify it
2428 * under the terms of the GNU Lesser General Public License version 3,
2429@@ -40,6 +40,7 @@
2430 ~ServiceStub();
2431
2432 std::shared_ptr<Player> create_session(const Player::Configuration&);
2433+ void pause_other_sessions(Player::PlayerKey key);
2434
2435 private:
2436 struct Private;
2437
2438=== modified file 'src/core/media/track_list_implementation.cpp'
2439--- src/core/media/track_list_implementation.cpp 2014-02-12 15:53:57 +0000
2440+++ src/core/media/track_list_implementation.cpp 2014-04-30 01:13:03 +0000
2441@@ -16,6 +16,9 @@
2442 * Authored by: Thomas Voß <thomas.voss@canonical.com>
2443 */
2444
2445+#include <stdio.h>
2446+#include <stdlib.h>
2447+
2448 #include "track_list_implementation.h"
2449
2450 #include "engine.h"
2451@@ -45,6 +48,16 @@
2452 {
2453 }
2454
2455+media::Track::UriType media::TrackListImplementation::query_uri_for_track(const media::Track::Id& id)
2456+{
2457+ auto it = d->meta_data_cache.find(id);
2458+
2459+ if (it == d->meta_data_cache.end())
2460+ return Track::UriType{};
2461+
2462+ return std::get<0>(it->second);
2463+}
2464+
2465 media::Track::MetaData media::TrackListImplementation::query_meta_data_for_track(const media::Track::Id& id)
2466 {
2467 auto it = d->meta_data_cache.find(id);
2468
2469=== modified file 'src/core/media/track_list_implementation.h'
2470--- src/core/media/track_list_implementation.h 2014-02-12 15:53:57 +0000
2471+++ src/core/media/track_list_implementation.h 2014-04-30 01:13:03 +0000
2472@@ -36,6 +36,7 @@
2473 const std::shared_ptr<Engine::MetaDataExtractor>& extractor);
2474 ~TrackListImplementation();
2475
2476+ Track::UriType query_uri_for_track(const Track::Id& id);
2477 Track::MetaData query_meta_data_for_track(const Track::Id& id);
2478
2479 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
2480
2481=== modified file 'src/core/media/track_list_skeleton.cpp'
2482--- src/core/media/track_list_skeleton.cpp 2014-02-12 15:53:57 +0000
2483+++ src/core/media/track_list_skeleton.cpp 2014-04-30 01:13:03 +0000
2484@@ -48,7 +48,8 @@
2485 object(object),
2486 can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()),
2487 tracks(object->get_property<mpris::TrackList::Properties::Tracks>()),
2488- current_track(tracks->get().begin())
2489+ current_track(tracks->get().begin()),
2490+ empty_iterator(tracks->get().begin())
2491 {
2492 }
2493
2494@@ -103,6 +104,7 @@
2495 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks;
2496 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;
2497 TrackList::ConstIterator current_track;
2498+ TrackList::ConstIterator empty_iterator;
2499
2500 core::Signal<void> on_track_list_replaced;
2501 core::Signal<Track::Id> on_track_added;
2502@@ -142,12 +144,22 @@
2503
2504 bool media::TrackListSkeleton::has_next() const
2505 {
2506- return std::next(d->current_track) != d->tracks->get().end();
2507+ return d->current_track != d->tracks->get().end();
2508 }
2509
2510 const media::Track::Id& media::TrackListSkeleton::next()
2511 {
2512- return *(d->current_track = std::next(d->current_track));
2513+ if (d->tracks->get().empty())
2514+ return *(d->current_track);
2515+
2516+ if (d->tracks->get().size() && (d->current_track == d->empty_iterator))
2517+ {
2518+ d->current_track = d->tracks->get().begin();
2519+ return *(d->current_track = std::next(d->current_track));
2520+ }
2521+
2522+ d->current_track = std::next(d->current_track);
2523+ return *(d->current_track);
2524 }
2525
2526 const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
2527
2528=== modified file 'src/core/media/track_list_skeleton.h'
2529--- src/core/media/track_list_skeleton.h 2014-02-12 15:53:57 +0000
2530+++ src/core/media/track_list_skeleton.h 2014-04-30 01:13:03 +0000
2531@@ -48,9 +48,10 @@
2532 const core::Signal<Track::Id>& on_track_removed() const;
2533 const core::Signal<Track::Id>& on_track_changed() const;
2534
2535+ core::Property<Container>& tracks();
2536+
2537 protected:
2538 core::Property<bool>& can_edit_tracks();
2539- core::Property<Container>& tracks();
2540
2541 core::Signal<void>& on_track_list_replaced();
2542 core::Signal<Track::Id>& on_track_added();
2543
2544=== modified file 'tests/acceptance-tests/service.cpp'
2545--- tests/acceptance-tests/service.cpp 2014-03-06 19:23:19 +0000
2546+++ tests/acceptance-tests/service.cpp 2014-04-30 01:13:03 +0000
2547@@ -1,5 +1,5 @@
2548 /*
2549- * Copyright © 2013 Canonical Ltd.
2550+ * Copyright © 2013-2014 Canonical Ltd.
2551 *
2552 * This program is free software: you can redistribute it and/or modify it
2553 * under the terms of the GNU Lesser General Public License version 3,
2554
2555=== modified file 'tests/unit-tests/CMakeLists.txt'
2556--- tests/unit-tests/CMakeLists.txt 2014-01-17 20:58:31 +0000
2557+++ tests/unit-tests/CMakeLists.txt 2014-04-30 01:13:03 +0000
2558@@ -1,10 +1,21 @@
2559 include_directories(
2560 .
2561 ${CMAKE_SOURCE_DIR}/src
2562+ ${PC_GSTREAMER_1_0_INCLUDE_DIRS}
2563 )
2564
2565 add_executable(
2566 test-gstreamer-engine
2567+
2568+ libmedia-mock.cpp
2569+ ${CMAKE_SOURCE_DIR}/src/core/media/engine.cpp
2570+ ${CMAKE_SOURCE_DIR}/src/core/media/gstreamer/engine.cpp
2571+ ${CMAKE_SOURCE_DIR}/src/core/media/player_skeleton.cpp
2572+ ${CMAKE_SOURCE_DIR}/src/core/media/player_implementation.cpp
2573+ ${CMAKE_SOURCE_DIR}/src/core/media/service_skeleton.cpp
2574+ ${CMAKE_SOURCE_DIR}/src/core/media/service_implementation.cpp
2575+ ${CMAKE_SOURCE_DIR}/src/core/media/track_list_skeleton.cpp
2576+ ${CMAKE_SOURCE_DIR}/src/core/media/track_list_implementation.cpp
2577 test-gstreamer-engine.cpp
2578 )
2579
2580@@ -13,12 +24,15 @@
2581
2582 media-hub-common
2583 media-hub-client
2584- media-hub-service
2585 media-hub-test-framework
2586
2587 ${CMAKE_THREAD_LIBS_INIT}
2588 ${Boost_LIBRARIES}
2589 ${DBUS_LIBRARIES}
2590+ ${DBUS_CPP_LDFLAGS}
2591+ ${GLog_LIBRARY}
2592+ ${PC_GSTREAMER_1_0_LIBRARIES}
2593+ ${GIO_LIBRARIES}
2594
2595 gmock
2596 gmock_main
2597
2598=== added file 'tests/unit-tests/libmedia-mock.cpp'
2599--- tests/unit-tests/libmedia-mock.cpp 1970-01-01 00:00:00 +0000
2600+++ tests/unit-tests/libmedia-mock.cpp 2014-04-30 01:13:03 +0000
2601@@ -0,0 +1,26 @@
2602+/*
2603+ * Copyright © 2013-2014 Canonical Ltd.
2604+ *
2605+ * This program is free software: you can redistribute it and/or modify it
2606+ * under the terms of the GNU Lesser General Public License version 3,
2607+ * as published by the Free Software Foundation.
2608+ *
2609+ * This program is distributed in the hope that it will be useful,
2610+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2611+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2612+ * GNU Lesser General Public License for more details.
2613+ *
2614+ * You should have received a copy of the GNU Lesser General Public License
2615+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2616+ *
2617+ * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
2618+ */
2619+
2620+#include <hybris/media/media_codec_layer.h>
2621+#include <hybris/media/surface_texture_client_hybris.h>
2622+
2623+#define UNUSED __attribute__((unused))
2624+
2625+void decoding_service_set_client_death_cb(UNUSED DecodingClientDeathCbHybris callback, UNUSED void *context)
2626+{
2627+}
2628
2629=== modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
2630--- tests/unit-tests/test-gstreamer-engine.cpp 2014-02-26 14:58:36 +0000
2631+++ tests/unit-tests/test-gstreamer-engine.cpp 2014-04-30 01:13:03 +0000
2632@@ -1,5 +1,5 @@
2633 /*
2634- * Copyright © 2013 Canonical Ltd.
2635+ * Copyright © 2013-2014 Canonical Ltd.
2636 *
2637 * This program is free software: you can redistribute it and/or modify it
2638 * under the terms of the GNU Lesser General Public License version 3,
2639@@ -198,7 +198,7 @@
2640 std::chrono::seconds{40}));
2641 }
2642
2643-TEST(GStreamerEngine, DISABLED_stop_pause_play_seek_video_works)
2644+TEST(GStreamerEngine, stop_pause_play_seek_video_works)
2645 {
2646 const std::string test_file{"/tmp/h264.avi"};
2647 const std::string test_file_uri{"file:///tmp/h264.avi"};
2648@@ -248,8 +248,8 @@
2649
2650 TEST(GStreamerEngine, get_position_duration_work)
2651 {
2652- const std::string test_file{"/tmp/test.ogg"};
2653- const std::string test_file_uri{"file:///tmp/test.ogg"};
2654+ const std::string test_file{"/tmp/h264.avi"};
2655+ const std::string test_file_uri{"file:///tmp/h264.avi"};
2656 std::remove(test_file.c_str());
2657 ASSERT_TRUE(test::copy_test_ogg_file_to(test_file));
2658
2659@@ -269,10 +269,18 @@
2660 EXPECT_TRUE(wst.wait_for_state_for(
2661 core::ubuntu::media::Engine::State::playing,
2662 std::chrono::milliseconds{4000}));
2663- sleep(1);
2664-
2665- // FIXME: Ideally we want to seek_to and measure the position there, but seek_to seems
2666- // broken from within this unit test
2667+
2668+ EXPECT_TRUE(engine.seek_to(std::chrono::seconds{10}));
2669+ EXPECT_TRUE(engine.play());
2670+ EXPECT_TRUE(wst.wait_for_state_for(
2671+ core::ubuntu::media::Engine::State::playing,
2672+ std::chrono::milliseconds{4000}));
2673+
2674+ std::cout << "position: " << engine.position() << std::endl;
2675+ std::cout << "duration: " << engine.duration() << std::endl;
2676+
2677+ // FIXME: This should be 10e9, but seek_to seems to be broken from within this unit test
2678+ // and I haven't been able to figure out why
2679 EXPECT_TRUE(engine.position() > 1e9);
2680
2681 EXPECT_TRUE(engine.duration() > 1e9);

Subscribers

People subscribed via source and target branches

to all changes: