Merge lp:~alfonsosanchezbeato/media-hub/video-desktop-support into lp:media-hub

Proposed by Alfonso Sanchez-Beato
Status: Merged
Approved by: Jim Hodapp
Approved revision: 221
Merged at revision: 212
Proposed branch: lp:~alfonsosanchezbeato/media-hub/video-desktop-support
Merge into: lp:media-hub
Diff against target: 1514 lines (+780/-177)
27 files modified
CMakeLists.txt (+2/-8)
debian/control (+2/-0)
debian/control.in (+2/-0)
debian/rules (+1/-1)
debian/usr.bin.media-hub-server (+8/-0)
include/core/media/player.h (+2/-1)
src/core/media/CMakeLists.txt (+3/-0)
src/core/media/audio/pulse_audio_output_observer.cpp (+1/-1)
src/core/media/backend.cpp (+7/-0)
src/core/media/client_death_observer.cpp (+2/-15)
src/core/media/gstreamer/engine.cpp (+6/-3)
src/core/media/gstreamer/engine.h (+1/-1)
src/core/media/gstreamer/playbin.cpp (+231/-77)
src/core/media/gstreamer/playbin.h (+17/-2)
src/core/media/hybris_client_death_observer.cpp (+2/-15)
src/core/media/hybris_recorder_observer.cpp (+2/-12)
src/core/media/player_implementation.cpp (+1/-1)
src/core/media/player_stub.cpp (+7/-2)
src/core/media/recorder_observer.cpp (+1/-0)
src/core/media/server/server.cpp (+7/-14)
src/core/media/video/egl_sink.cpp (+326/-0)
src/core/media/video/egl_sink.h (+73/-0)
src/core/media/video/hybris_gl_sink.cpp (+0/-2)
src/core/media/video/platform_default_sink.cpp (+5/-9)
src/core/media/video/socket_types.h (+50/-0)
tests/CMakeLists.txt (+11/-3)
tests/unit-tests/test-gstreamer-engine.cpp (+10/-10)
To merge this branch: bzr merge lp:~alfonsosanchezbeato/media-hub/video-desktop-support
Reviewer Review Type Date Requested Status
Jim Hodapp (community) code Approve
Review via email: mp+317181@code.launchpad.net

Commit message

Support for video decoding using non-hybris mirsink.

Description of the change

Support for video decoding using non-hybris mirsink.

To post a comment you must log in.
218. By Alfonso Sanchez-Beato

Make tests compile on zesty (copy over workaround from mir)

219. By Alfonso Sanchez-Beato

Avoid including gmock static libraries

Revision history for this message
Jim Hodapp (jhodapp) wrote :

Looks very good overall. Some comments inline below.

review: Needs Fixing (code)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

Comments addressed & answered, pushing branch now.

220. By Alfonso Sanchez-Beato

Address review comments

Revision history for this message
Jim Hodapp (jhodapp) wrote :

One minor typo to fix.

review: Needs Fixing
221. By Alfonso Sanchez-Beato

Fix typo

Revision history for this message
Jim Hodapp (jhodapp) wrote :

LGTM!

review: Approve (code)

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 2016-08-15 19:27:29 +0000
3+++ CMakeLists.txt 2017-02-17 15:49:36 +0000
4@@ -52,14 +52,8 @@
5 pkg_check_modules(PROCESS_CPP process-cpp REQUIRED)
6 pkg_check_modules(GIO gio-2.0 REQUIRED)
7 pkg_check_modules(HYBRIS_MEDIA libmedia REQUIRED)
8-
9-include(CheckIncludeFiles)
10-check_include_files("hybris/media/media_codec_layer.h" MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
11-if (DEFINED MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
12- add_definitions(-DMEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
13-else()
14- MESSAGE(STATUS "*libhybris not found*")
15-endif()
16+pkg_check_modules(EGL egl REQUIRED)
17+pkg_check_modules(GL gl REQUIRED)
18
19 #####################################################################
20 # Enable code coverage calculation with gcov/gcovr/lcov
21
22=== modified file 'debian/control'
23--- debian/control 2016-05-04 13:11:22 +0000
24+++ debian/control 2017-02-17 15:49:36 +0000
25@@ -33,6 +33,8 @@
26 libpulse-dev,
27 qtbase5-dev,
28 libtelepathy-qt5-dev,
29+ libegl1-mesa-dev,
30+ libgl1-mesa-dev,
31 Standards-Version: 3.9.6
32 Homepage: https://launchpad.net/media-hub
33 # If you aren't a member of ~phablet-team but need to upload packaging changes,
34
35=== modified file 'debian/control.in'
36--- debian/control.in 2016-04-04 18:54:45 +0000
37+++ debian/control.in 2017-02-17 15:49:36 +0000
38@@ -28,6 +28,8 @@
39 libpulse-dev,
40 qtbase5-dev,
41 libtelepathy-qt5-dev,
42+ libegl1-mesa-dev,
43+ libgl1-mesa-dev,
44 Standards-Version: 3.9.6
45 Homepage: https://launchpad.net/media-hub
46 # If you aren't a member of ~phablet-team but need to upload packaging changes,
47
48=== modified file 'debian/rules'
49--- debian/rules 2016-03-29 18:00:09 +0000
50+++ debian/rules 2017-02-17 15:49:36 +0000
51@@ -21,7 +21,7 @@
52 patch=$(shell echo $(full_version) | cut -d'.' -f3)
53
54 %:
55- dh $@ --fail-missing --parallel -- -B build
56+ dh $@ --fail-missing --parallel -Xusr/lib/libgtest -Xusr/lib/libgmock -- -B build
57
58 override_dh_auto_configure:
59 dh_auto_configure -- -DUBUNTU_MEDIA_HUB_VERSION_MAJOR=$(major) -DUBUNTU_MEDIA_HUB_VERSION_MINOR=$(minor) -DUBUNTU_MEDIA_HUB_VERSION_PATCH=$(patch)
60
61=== modified file 'debian/usr.bin.media-hub-server'
62--- debian/usr.bin.media-hub-server 2016-08-23 06:54:43 +0000
63+++ debian/usr.bin.media-hub-server 2017-02-17 15:49:36 +0000
64@@ -126,6 +126,14 @@
65 owner @{HOME}/.local/share/** rk,
66 owner /{,var/}run/user/[0-9]*/** rk,
67
68+ # Permissions for desktop video decoding
69+ unix (bind, send) type=dgram addr="@media-hub-server*",
70+ /sys/devices/**/drm/render** r,
71+ /sys/devices/**/drm/card** r,
72+ /sys/devices/system/node/node*/meminfo r,
73+ /run/user/*/orcexec* rw,
74+ /run/user/**/mir_socket rw,
75+
76 # Site-specific additions and overrides. See local/README for details.
77 #include <local/usr.bin.media-hub-server>
78 }
79
80=== modified file 'include/core/media/player.h'
81--- include/core/media/player.h 2016-08-15 19:27:29 +0000
82+++ include/core/media/player.h 2017-02-17 15:49:36 +0000
83@@ -44,7 +44,8 @@
84 enum Backend
85 {
86 none,
87- hybris
88+ hybris,
89+ mir
90 };
91
92 /**
93
94=== modified file 'src/core/media/CMakeLists.txt'
95--- src/core/media/CMakeLists.txt 2016-08-12 16:08:16 +0000
96+++ src/core/media/CMakeLists.txt 2017-02-17 15:49:36 +0000
97@@ -58,6 +58,7 @@
98 track_list_stub.cpp
99
100 video/hybris_gl_sink.cpp
101+ video/egl_sink.cpp
102 video/platform_default_sink.cpp
103 )
104
105@@ -83,6 +84,8 @@
106 ${GLog_LIBRARY}
107 ${GIO_LIBRARIES}
108 ${HYBRIS_MEDIA_LIBRARIES}
109+ ${EGL_LIBRARIES}
110+ ${GL_LIBRARIES}
111 )
112
113 install(
114
115=== modified file 'src/core/media/audio/pulse_audio_output_observer.cpp'
116--- src/core/media/audio/pulse_audio_output_observer.cpp 2016-04-06 15:28:29 +0000
117+++ src/core/media/audio/pulse_audio_output_observer.cpp 2017-02-17 15:49:36 +0000
118@@ -329,7 +329,7 @@
119 }
120
121 audio::OutputState state;
122- if (info->index == primary_sink_index)
123+ if (info->index == static_cast<std::uint32_t>(primary_sink_index))
124 state = audio::OutputState::Speaker;
125 else
126 state = audio::OutputState::External;
127
128=== modified file 'src/core/media/backend.cpp'
129--- src/core/media/backend.cpp 2016-08-15 19:27:29 +0000
130+++ src/core/media/backend.cpp 2017-02-17 15:49:36 +0000
131@@ -39,5 +39,12 @@
132 return media::AVBackend::Backend::hybris;
133 }
134
135+ plugin = gst_registry_lookup(registry, "libgstmirsink.so");
136+ if (plugin)
137+ {
138+ gst_object_unref(plugin);
139+ return media::AVBackend::Backend::mir;
140+ }
141+
142 return media::AVBackend::Backend::none;
143 }
144
145=== modified file 'src/core/media/client_death_observer.cpp'
146--- src/core/media/client_death_observer.cpp 2016-08-15 19:27:29 +0000
147+++ src/core/media/client_death_observer.cpp 2017-02-17 15:49:36 +0000
148@@ -21,12 +21,10 @@
149 #include <core/media/client_death_observer.h>
150 #include <core/media/hybris_client_death_observer.h>
151 #include <core/media/stub_client_death_observer.h>
152+#include <core/media/hybris_client_death_observer.h>
153
154 namespace media = core::ubuntu::media;
155
156-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
157-#include <core/media/hybris_client_death_observer.h>
158-
159 // Accesses the default client death observer implementation for the platform.
160 media::ClientDeathObserver::Ptr media::platform_default_client_death_observer()
161 {
162@@ -35,6 +33,7 @@
163 {
164 case media::AVBackend::Backend::hybris:
165 return media::HybrisClientDeathObserver::create();
166+ case media::AVBackend::Backend::mir:
167 case media::AVBackend::Backend::none:
168 MH_WARNING(
169 "No video backend selected. Client disconnect functionality won't work."
170@@ -45,15 +44,3 @@
171 return media::HybrisClientDeathObserver::create();
172 }
173 }
174-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
175-// Just throws a std::logic_error as we have not yet defined a default way to
176-// identify client death changes. One possible way of implementing the interface
177-// would be to listen to dbus name changes and react accordingly.
178-media::ClientDeathObserver::Ptr media::platform_default_client_death_observer()
179-{
180- throw std::logic_error
181- {
182- "No platform-specific death observer implementation known."
183- };
184-}
185-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
186
187=== modified file 'src/core/media/gstreamer/engine.cpp'
188--- src/core/media/gstreamer/engine.cpp 2016-08-23 08:26:11 +0000
189+++ src/core/media/gstreamer/engine.cpp 2017-02-17 15:49:36 +0000
190@@ -15,6 +15,7 @@
191 *
192 * Authored by: Thomas Voß <thomas.voss@canonical.com>
193 * Jim Hodapp <jim.hodapp@canonical.com>
194+ * Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
195 */
196
197 #include <stdio.h>
198@@ -272,8 +273,9 @@
199 buffering_changed(value);
200 }
201
202- Private()
203- : meta_data_extractor(new gstreamer::MetaDataExtractor()),
204+ Private(const core::ubuntu::media::Player::PlayerKey key)
205+ : playbin(key),
206+ meta_data_extractor(new gstreamer::MetaDataExtractor()),
207 volume(media::Engine::Volume(1.)),
208 orientation(media::Player::Orientation::rotate0),
209 is_video_source(false),
210@@ -410,7 +412,8 @@
211 core::Signal<int> buffering_changed;
212 };
213
214-gstreamer::Engine::Engine() : d(new Private{})
215+gstreamer::Engine::Engine(const core::ubuntu::media::Player::PlayerKey key)
216+ : d(new Private{key})
217 {
218 d->state = media::Engine::State::no_media;
219 }
220
221=== modified file 'src/core/media/gstreamer/engine.h'
222--- src/core/media/gstreamer/engine.h 2016-08-11 19:09:00 +0000
223+++ src/core/media/gstreamer/engine.h 2017-02-17 15:49:36 +0000
224@@ -26,7 +26,7 @@
225 class Engine : public core::ubuntu::media::Engine
226 {
227 public:
228- Engine();
229+ Engine(const core::ubuntu::media::Player::PlayerKey key);
230 ~Engine();
231
232 const std::shared_ptr<MetaDataExtractor>& meta_data_extractor() const;
233
234=== modified file 'src/core/media/gstreamer/playbin.cpp'
235--- src/core/media/gstreamer/playbin.cpp 2016-07-11 01:21:38 +0000
236+++ src/core/media/gstreamer/playbin.cpp 2017-02-17 15:49:36 +0000
237@@ -14,59 +14,79 @@
238 * along with this program. If not, see <http://www.gnu.org/licenses/>.
239 *
240 * Authored by: Thomas Voß <thomas.voss@canonical.com>
241+ * Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
242 */
243
244 #include <core/media/gstreamer/playbin.h>
245 #include <core/media/gstreamer/engine.h>
246+#include <core/media/video/socket_types.h>
247
248 #include <gst/pbutils/missing-plugins.h>
249
250-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
251 #include <hybris/media/surface_texture_client_hybris.h>
252 #include <hybris/media/media_codec_layer.h>
253
254 #include "core/media/logger/logger.h"
255 #include "core/media/util/uri_check.h"
256
257+#include <sys/socket.h>
258+#include <sys/un.h>
259+
260 #include <utility>
261-
262-namespace
263-{
264-void setup_video_sink_for_buffer_streaming(GstElement* pipeline)
265-{
266- // Get the service-side BufferQueue (IGraphicBufferProducer) and associate it with
267- // the SurfaceTextureClientHybris instance
268- IGBPWrapperHybris igbp = decoding_service_get_igraphicbufferproducer();
269- SurfaceTextureClientHybris stc = surface_texture_client_create_by_igbp(igbp);
270-
271- // Because mirsink is being loaded, we are definitely doing * hardware rendering.
272- surface_texture_client_set_hardware_rendering(stc, TRUE);
273-
274- GstContext *context = gst_context_new("gst.mir.MirContext", TRUE);
275- GstStructure *structure = gst_context_writable_structure(context);
276- gst_structure_set(structure, "gst_mir_context", G_TYPE_POINTER, stc, NULL);
277-
278- /* Propagate context in pipeline (needed by amchybris and mirsink) */
279- gst_element_set_context(pipeline, context);
280-}
281-}
282-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
283-namespace
284-{
285-void setup_video_sink_for_buffer_streaming(GstElement*)
286-{
287- throw core::ubuntu::media::Player::Errors::OutOfProcessBufferStreamingNotSupported{};
288-}
289-}
290-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
291-
292-namespace
293-{
294-bool is_mir_video_sink()
295-{
296- return g_strcmp0(::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), "mirsink") == 0;
297-}
298-}
299+#include <cstring>
300+
301+static const char *PULSE_SINK = "pulsesink";
302+static const char *HYBRIS_SINK = "hybrissink";
303+static const char *MIR_SINK = "mirsink";
304+
305+using namespace std;
306+
307+void gstreamer::Playbin::setup_video_sink_for_buffer_streaming()
308+{
309+ IGBPWrapperHybris igbp;
310+ SurfaceTextureClientHybris stc;
311+ GstContext *context;
312+ GstStructure *structure;
313+
314+ switch (backend) {
315+ case core::ubuntu::media::AVBackend::Backend::hybris:
316+ // Get the service-side BufferQueue (IGraphicBufferProducer) and
317+ // associate with it the SurfaceTextureClientHybris instance.
318+ igbp = decoding_service_get_igraphicbufferproducer();
319+ stc = surface_texture_client_create_by_igbp(igbp);
320+
321+ // Because mirsink is being loaded, we are definitely doing * hardware rendering.
322+ surface_texture_client_set_hardware_rendering(stc, TRUE);
323+
324+ context = gst_context_new("gst.mir.MirContext", TRUE);
325+ structure = gst_context_writable_structure(context);
326+ gst_structure_set(structure, "gst_mir_context", G_TYPE_POINTER, stc, NULL);
327+
328+ /* Propagate context in pipeline (needed by amchybris and mirsink) */
329+ gst_element_set_context(pipeline, context);
330+ break;
331+ case core::ubuntu::media::AVBackend::Backend::mir:
332+ // Connect to buffer consumer socket
333+ connect_to_consumer();
334+ // Configure mirsink so it exports buffers (otherwise it would create
335+ // its own window).
336+ g_object_set (G_OBJECT (video_sink), "export-buffers", TRUE, nullptr);
337+ break;
338+ case core::ubuntu::media::AVBackend::Backend::none:
339+ default:
340+ throw core::ubuntu::media::Player::Errors::
341+ OutOfProcessBufferStreamingNotSupported{};
342+ }
343+}
344+
345+bool gstreamer::Playbin::is_supported_video_sink(void) const
346+{
347+ if (video_sink_name == HYBRIS_SINK || video_sink_name == MIR_SINK)
348+ return TRUE;
349+
350+ return FALSE;
351+}
352+
353 // Uncomment to generate a dot file at the time that the pipeline
354 // goes to the PLAYING state. Make sure to export GST_DEBUG_DUMP_DOT_DIR
355 // before starting media-hub-server. To convert the dot file to something
356@@ -98,7 +118,7 @@
357 static_cast<Playbin*>(user_data)->setup_source(source);
358 }
359
360-gstreamer::Playbin::Playbin()
361+gstreamer::Playbin::Playbin(const core::ubuntu::media::Player::PlayerKey key_in)
362 : pipeline(gst_element_factory_make("playbin", pipeline_name().c_str())),
363 bus{gst_element_get_bus(pipeline)},
364 file_type(MEDIA_FILE_TYPE_NONE),
365@@ -122,7 +142,10 @@
366 is_missing_video_codec(false),
367 audio_stream_id(-1),
368 video_stream_id(-1),
369- current_new_state(GST_STATE_NULL)
370+ current_new_state(GST_STATE_NULL),
371+ key(key_in),
372+ backend(core::ubuntu::media::AVBackend::get_backend_type()),
373+ sock_consumer(-1)
374 {
375 if (!pipeline)
376 throw std::runtime_error("Could not create pipeline for playbin.");
377@@ -156,11 +179,11 @@
378
379 MH_DEBUG("%s", func);
380 if (pb.pipeline)
381- MH_DEBUG("pipeline: %d", GST_OBJECT_REFCOUNT(pb.pipeline));
382+ MH_DEBUG("pipeline: %d", (const void *) GST_OBJECT_REFCOUNT(pb.pipeline));
383 if (pb.video_sink)
384- MH_DEBUG("video_sink: %d", GST_OBJECT_REFCOUNT(pb.video_sink));
385+ MH_DEBUG("video_sink: %d", (const void *) GST_OBJECT_REFCOUNT(pb.video_sink));
386 if (pb.audio_sink)
387- MH_DEBUG("audio_sink: %d", GST_OBJECT_REFCOUNT(pb.audio_sink));
388+ MH_DEBUG("audio_sink: %d", (const void *) GST_OBJECT_REFCOUNT(pb.audio_sink));
389 }
390 #endif
391
392@@ -176,6 +199,11 @@
393 if (pipeline)
394 gst_object_unref(pipeline);
395
396+ if (sock_consumer != -1) {
397+ close(sock_consumer);
398+ sock_consumer = -1;
399+ }
400+
401 #ifdef DEBUG_REFS
402 print_refs(*this, "gstreamer::Playbin::~Playbin pipeline");
403 #endif
404@@ -217,13 +245,14 @@
405 is_missing_video_codec = false;
406 audio_stream_id = -1;
407 video_stream_id = -1;
408+ if (sock_consumer != -1) {
409+ close(sock_consumer);
410+ sock_consumer = -1;
411+ }
412 }
413
414-void gstreamer::Playbin::process_message_element(GstMessage *message)
415+void gstreamer::Playbin::process_missing_plugin_message(GstMessage *message)
416 {
417- if (!gst_is_missing_plugin_message(message))
418- return;
419-
420 gchar *desc = gst_missing_plugin_message_get_description(message);
421 MH_WARNING("Missing plugin: %s", desc);
422 g_free(desc);
423@@ -253,6 +282,40 @@
424 MH_ERROR("Missing decoder for %s", mime);
425 }
426
427+void gstreamer::Playbin::process_message_element(GstMessage *message)
428+{
429+ const GstStructure *msg_data = gst_message_get_structure(message);
430+ const gchar *struct_name = gst_structure_get_name(msg_data);
431+
432+ if (g_strcmp0("buffer-export-data", struct_name) == 0)
433+ {
434+ int fd;
435+ core::ubuntu::media::video::BufferMetadata meta;
436+ if (!gst_structure_get(msg_data,
437+ "fd", G_TYPE_INT, &fd,
438+ "width", G_TYPE_INT, &meta.width,
439+ "height", G_TYPE_INT, &meta.height,
440+ "fourcc", G_TYPE_INT, &meta.fourcc,
441+ "stride", G_TYPE_INT, &meta.stride,
442+ "offset", G_TYPE_INT, &meta.offset,
443+ NULL))
444+ {
445+ MH_ERROR("Bad buffer-export-data message: mirsink version mismatch?");
446+ return;
447+ }
448+ MH_DEBUG("Exporting %dx%d buffer (fd %d)", meta.width, meta.height, fd);
449+ send_buffer_data(fd, &meta, sizeof meta);
450+ }
451+ else if (g_strcmp0("frame-ready", struct_name) == 0)
452+ {
453+ send_frame_ready();
454+ }
455+ else
456+ {
457+ MH_ERROR("Unknown GST_MESSAGE_ELEMENT with struct %s", struct_name);
458+ }
459+}
460+
461 void gstreamer::Playbin::on_new_message_async(const Bus::Message& message)
462 {
463 switch (message.type)
464@@ -274,7 +337,10 @@
465 signals.on_state_changed(std::make_pair(message.detail.state_changed, message.source));
466 break;
467 case GST_MESSAGE_ELEMENT:
468- process_message_element(message.message);
469+ if (gst_is_missing_plugin_message(message.message))
470+ process_missing_plugin_message(message.message);
471+ else
472+ process_message_element(message.message);
473 break;
474 case GST_MESSAGE_TAG:
475 {
476@@ -322,34 +388,33 @@
477 flags &= ~GST_PLAY_FLAG_TEXT;
478 g_object_set (pipeline, "flags", flags, nullptr);
479
480- if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") != nullptr)
481- {
482- audio_sink = gst_element_factory_make (
483- ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"),
484- "audio-sink");
485-
486- MH_INFO("audio_sink: %s", ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"));
487-
488- g_object_set (
489- pipeline,
490- "audio-sink",
491- audio_sink,
492- NULL);
493+ const char *asink_name = ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME");
494+
495+ if (asink_name == nullptr)
496+ asink_name = PULSE_SINK;
497+
498+ audio_sink = gst_element_factory_make (asink_name, "audio-sink");
499+ if (audio_sink)
500+ g_object_set (pipeline, "audio-sink", audio_sink, NULL);
501+ else
502+ MH_ERROR("Error trying to create audio sink %s", asink_name);
503+
504+ const char *vsink_name = ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME");
505+
506+ if (vsink_name == nullptr) {
507+ if (backend == core::ubuntu::media::AVBackend::Backend::hybris)
508+ vsink_name = HYBRIS_SINK;
509+ else if (backend == core::ubuntu::media::AVBackend::Backend::mir)
510+ vsink_name = MIR_SINK;
511 }
512
513- if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr)
514- {
515- video_sink = gst_element_factory_make (
516- ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"),
517- "video-sink");
518-
519- MH_INFO("video_sink: %s", ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"));
520-
521- g_object_set (
522- pipeline,
523- "video-sink",
524- video_sink,
525- NULL);
526+ if (vsink_name) {
527+ video_sink_name = vsink_name;
528+ video_sink = gst_element_factory_make (vsink_name, "video-sink");
529+ if (video_sink)
530+ g_object_set (pipeline, "video-sink", video_sink, NULL);
531+ else
532+ MH_ERROR("Error trying to create video sink %s", vsink_name);
533 }
534 }
535
536@@ -360,7 +425,7 @@
537 "No video sink configured for the current pipeline"
538 };
539
540- setup_video_sink_for_buffer_streaming(pipeline);
541+ setup_video_sink_for_buffer_streaming();
542 }
543
544 void gstreamer::Playbin::set_volume(double new_volume)
545@@ -630,7 +695,7 @@
546
547 core::ubuntu::media::video::Dimensions gstreamer::Playbin::get_video_dimensions() const
548 {
549- if (not video_sink || not is_mir_video_sink())
550+ if (not video_sink || not is_supported_video_sink())
551 throw std::runtime_error
552 {
553 "Missing video sink or video sink does not support query of width and height."
554@@ -846,3 +911,92 @@
555 else
556 return true;
557 }
558+
559+bool gstreamer::Playbin::connect_to_consumer(void)
560+{
561+ static const char *local_socket = "media-hub-server";
562+ static const char *consumer_socket = "media-consumer";
563+
564+ int len;
565+ struct sockaddr_un local, remote;
566+
567+ if (sock_consumer != -1) {
568+ MH_DEBUG("Resetting socket");
569+ close(sock_consumer);
570+ }
571+
572+ if ((sock_consumer = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
573+ {
574+ MH_ERROR("Cannot create socket: %s (%d)", strerror(errno), errno);
575+ return false;
576+ }
577+
578+ // Bind client to local -abstract- socket (media-hub-server<session>)
579+ ostringstream local_ss;
580+ local_ss << local_socket << key;
581+ local.sun_family = AF_UNIX;
582+ local.sun_path[0] = '\0';
583+ strcpy(local.sun_path + 1, local_ss.str().c_str());
584+ len = sizeof(local.sun_family) + local_ss.str().length() + 1;
585+ if (bind(sock_consumer, (struct sockaddr *) &local, len) == -1)
586+ {
587+ MH_ERROR("Cannot bind socket: %s (%d)", strerror(errno), errno);
588+ close(sock_consumer);
589+ sock_consumer = -1;
590+ return false;
591+ }
592+
593+ // Connect to buffer consumer (media-consumer<session>)
594+ ostringstream remote_ss;
595+ remote_ss << consumer_socket << key;
596+ remote.sun_family = AF_UNIX;
597+ remote.sun_path[0] = '\0';
598+ strcpy(remote.sun_path + 1, remote_ss.str().c_str());
599+ len = sizeof(remote.sun_family) + remote_ss.str().length() + 1;
600+ if (connect(sock_consumer, (struct sockaddr *) &remote, len) == -1)
601+ {
602+ MH_ERROR("Cannot connect to consumer: %s (%d)", strerror(errno), errno);
603+ close(sock_consumer);
604+ sock_consumer = -1;
605+ return false;
606+ }
607+
608+ MH_DEBUG("Connected to buffer consumer socket");
609+
610+ return true;
611+}
612+
613+void gstreamer::Playbin::send_buffer_data(int fd, void *data, size_t len)
614+{
615+ struct msghdr msg{};
616+ char buf[CMSG_SPACE(sizeof fd)]{};
617+ struct cmsghdr *cmsg;
618+ struct iovec io = { .iov_base = data, .iov_len = len };
619+
620+ msg.msg_iov = &io;
621+ msg.msg_iovlen = 1;
622+ msg.msg_control = buf;
623+ msg.msg_controllen = sizeof buf;
624+
625+ cmsg = CMSG_FIRSTHDR(&msg);
626+ cmsg->cmsg_level = SOL_SOCKET;
627+ cmsg->cmsg_type = SCM_RIGHTS;
628+ cmsg->cmsg_len = CMSG_LEN(sizeof fd);
629+
630+ memmove(CMSG_DATA(cmsg), &fd, sizeof fd);
631+
632+ msg.msg_controllen = cmsg->cmsg_len;
633+
634+ if (sendmsg(sock_consumer, &msg, 0) < 0)
635+ MH_ERROR("Failed to send dma_buf fd to consumer: %s (%d)",
636+ strerror(errno), errno);
637+}
638+
639+void gstreamer::Playbin::send_frame_ready(void)
640+{
641+ const char ready = 'r';
642+
643+ if (send (sock_consumer, &ready, sizeof ready, 0) == -1)
644+ MH_ERROR("Error when sending frame ready flag to client: %s (%d)",
645+ strerror(errno), errno);
646+}
647
648=== modified file 'src/core/media/gstreamer/playbin.h'
649--- src/core/media/gstreamer/playbin.h 2016-07-11 01:21:38 +0000
650+++ src/core/media/gstreamer/playbin.h 2017-02-17 15:49:36 +0000
651@@ -30,9 +30,11 @@
652 #include <chrono>
653 #include <string>
654
655+#include "core/media/player.h"
656+
657 // Uncomment to generate a dot file at the time that the pipeline
658 // goes to the PLAYING state. Make sure to export GST_DEBUG_DUMP_DOT_DIR
659-// before starting media-hub-server. To convert the dot file to something
660+// before starting media-hub-server. To convert the dot file to some
661 // other image format, use: dot pipeline.dot -Tpng -o pipeline.png
662 //#define DEBUG_GST_PIPELINE
663
664@@ -64,7 +66,7 @@
665 GstElement *source,
666 gpointer user_data);
667
668- Playbin();
669+ Playbin(const core::ubuntu::media::Player::PlayerKey key);
670 ~Playbin();
671
672 void reset();
673@@ -155,6 +157,19 @@
674 gint audio_stream_id;
675 gint video_stream_id;
676 GstState current_new_state;
677+
678+private:
679+ void setup_video_sink_for_buffer_streaming(void);
680+ bool is_supported_video_sink(void) const;
681+ bool connect_to_consumer(void);
682+ void send_buffer_data(int fd, void *data, size_t len);
683+ void send_frame_ready(void);
684+ void process_missing_plugin_message(GstMessage *message);
685+
686+ const core::ubuntu::media::Player::PlayerKey key;
687+ const core::ubuntu::media::AVBackend::Backend backend;
688+ std::string video_sink_name;
689+ int sock_consumer;
690 };
691 }
692
693
694=== modified file 'src/core/media/hybris_client_death_observer.cpp'
695--- src/core/media/hybris_client_death_observer.cpp 2014-11-26 09:18:33 +0000
696+++ src/core/media/hybris_client_death_observer.cpp 2017-02-17 15:49:36 +0000
697@@ -18,10 +18,10 @@
698
699 #include <core/media/hybris_client_death_observer.h>
700
701+#include <hybris/media/media_codec_layer.h>
702+
703 namespace media = core::ubuntu::media;
704
705-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
706-#include <hybris/media/media_codec_layer.h>
707
708 namespace
709 {
710@@ -69,16 +69,3 @@
711 {
712 return client_with_key_died;
713 }
714-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
715-// Creates an instance of the HybrisClientDeathObserver or throws
716-// if the underlying platform does not support it.
717-media::ClientDeathObserver::Ptr media::HybrisClientDeathObserver::create()
718-{
719- throw std::logic_error
720- {
721- "Hybris-based death observer implementation not supported on this platform."
722- };
723-}
724-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
725-
726-
727
728=== modified file 'src/core/media/hybris_recorder_observer.cpp'
729--- src/core/media/hybris_recorder_observer.cpp 2014-11-26 09:36:44 +0000
730+++ src/core/media/hybris_recorder_observer.cpp 2017-02-17 15:49:36 +0000
731@@ -18,11 +18,10 @@
732
733 #include <core/media/hybris_recorder_observer.h>
734
735+#include <hybris/media/media_recorder_layer.h>
736+
737 namespace media = core::ubuntu::media;
738
739-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
740-#include <hybris/media/media_recorder_layer.h>
741-
742 struct media::HybrisRecorderObserver::Private
743 {
744 struct Holder
745@@ -88,12 +87,3 @@
746 {
747 return media::RecorderObserver::Ptr{new media::HybrisRecorderObserver{}};
748 }
749-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
750-media::RecorderObserver::Ptr media::HybrisRecorderObserver::create()
751-{
752- throw std::logic_error
753- {
754- "Hybris-based recorder observer implementation not supported on this platform."
755- };
756-}
757-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
758
759=== modified file 'src/core/media/player_implementation.cpp'
760--- src/core/media/player_implementation.cpp 2016-08-15 19:27:29 +0000
761+++ src/core/media/player_implementation.cpp 2017-02-17 15:49:36 +0000
762@@ -63,7 +63,7 @@
763 config(config),
764 display_state_lock(config.power_state_controller->display_state_lock()),
765 system_state_lock(config.power_state_controller->system_state_lock()),
766- engine(std::make_shared<gstreamer::Engine>()),
767+ engine(std::make_shared<gstreamer::Engine>(config.key)),
768 track_list(std::make_shared<TrackListImplementation>(
769 config.parent.bus,
770 config.parent.service->add_object_for_path(
771
772=== modified file 'src/core/media/player_stub.cpp'
773--- src/core/media/player_stub.cpp 2016-08-15 19:27:29 +0000
774+++ src/core/media/player_stub.cpp 2017-02-17 15:49:36 +0000
775@@ -316,17 +316,22 @@
776
777 media::video::Sink::Ptr media::PlayerStub::create_gl_texture_video_sink(std::uint32_t texture_id)
778 {
779+ // Create first local stub so media-hub can rely on an existing socket
780+ // for the mir/desktop case.
781+ const auto sink = d->sink_factory(texture_id);
782+
783 auto op = d->object->transact_method<mpris::Player::CreateVideoSink, void>(texture_id);
784
785 if (op.is_error())
786 {
787- if (op.error().name() == mpris::Player::Error::OutOfProcessBufferStreamingNotSupported::name)
788+ if (op.error().name() ==
789+ mpris::Player::Error::OutOfProcessBufferStreamingNotSupported::name)
790 throw media::Player::Errors::OutOfProcessBufferStreamingNotSupported{};
791 else
792 throw std::runtime_error{op.error().print()};
793 }
794
795- return d->sink_factory(texture_id);
796+ return sink;
797 }
798
799 void media::PlayerStub::next()
800
801=== modified file 'src/core/media/recorder_observer.cpp'
802--- src/core/media/recorder_observer.cpp 2016-08-15 19:27:29 +0000
803+++ src/core/media/recorder_observer.cpp 2017-02-17 15:49:36 +0000
804@@ -33,6 +33,7 @@
805 {
806 case media::AVBackend::Backend::hybris:
807 return media::HybrisRecorderObserver::create();
808+ case media::AVBackend::Backend::mir:
809 case media::AVBackend::Backend::none:
810 MH_WARNING(
811 "No video backend selected. Video recording functionality won't work."
812
813=== modified file 'src/core/media/server/server.cpp'
814--- src/core/media/server/server.cpp 2016-08-15 19:27:29 +0000
815+++ src/core/media/server/server.cpp 2017-02-17 15:49:36 +0000
816@@ -24,6 +24,8 @@
817 #include "core/media/logger/logger.h"
818 #include "core/media/service_implementation.h"
819
820+#include <hybris/media/media_codec_layer.h>
821+
822 #include <core/posix/signal.h>
823
824 #include <iostream>
825@@ -32,9 +34,6 @@
826
827 using namespace std;
828
829-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
830-#include <hybris/media/media_codec_layer.h>
831-
832 namespace
833 {
834 void logger_init()
835@@ -78,6 +77,9 @@
836 MH_DEBUG("Found hybris backend");
837 decoding_service_init();
838 break;
839+ case media::AVBackend::Backend::mir:
840+ MH_DEBUG("Found mir backend");
841+ break;
842 case media::AVBackend::Backend::none:
843 MH_WARNING("No video backend selected. Video functionality won't work.");
844 break;
845@@ -87,16 +89,6 @@
846 }
847 }
848 }
849-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
850-namespace
851-{
852-// All platform-specific initialization routines go here.
853-void platform_init()
854-{
855- // Consciously left empty
856-}
857-}
858-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
859
860 int main()
861 {
862@@ -170,7 +162,8 @@
863 {
864 impl,
865 player_store,
866- external_services
867+ external_services,
868+ nullptr
869 });
870
871 std::thread service_worker
872
873=== added file 'src/core/media/video/egl_sink.cpp'
874--- src/core/media/video/egl_sink.cpp 1970-01-01 00:00:00 +0000
875+++ src/core/media/video/egl_sink.cpp 2017-02-17 15:49:36 +0000
876@@ -0,0 +1,326 @@
877+/*
878+ * Copyright © 2017 Canonical Ltd.
879+ *
880+ * This program is free software: you can redistribute it and/or modify it
881+ * under the terms of the GNU Lesser General Public License version 3,
882+ * as published by the Free Software Foundation.
883+ *
884+ * This program is distributed in the hope that it will be useful,
885+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
886+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
887+ * GNU Lesser General Public License for more details.
888+ *
889+ * You should have received a copy of the GNU Lesser General Public License
890+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
891+ *
892+ * Authored by: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
893+ */
894+
895+#include "core/media/logger/logger.h"
896+
897+#include <core/media/video/egl_sink.h>
898+#include <core/media/video/socket_types.h>
899+
900+#include <EGL/egl.h>
901+#include <EGL/eglext.h>
902+#include <GLES2/gl2.h>
903+#include <GLES2/gl2ext.h>
904+
905+#include <sys/types.h>
906+#include <sys/socket.h>
907+#include <sys/un.h>
908+
909+#include <sstream>
910+#include <thread>
911+#include <future>
912+#include <cstring>
913+#include <unistd.h>
914+
915+namespace media = core::ubuntu::media;
916+namespace video = core::ubuntu::media::video;
917+
918+using namespace std;
919+
920+struct video::EglSink::Private
921+{
922+
923+ static bool receive_buff(int socket, BufferData *data)
924+ {
925+ struct msghdr msg{};
926+ struct iovec io = { .iov_base = &data->meta,
927+ .iov_len = sizeof data->meta };
928+ char c_buffer[256];
929+ ssize_t res;
930+
931+ msg.msg_iov = &io;
932+ msg.msg_iovlen = 1;
933+
934+ msg.msg_control = c_buffer;
935+ msg.msg_controllen = sizeof c_buffer;
936+
937+ if ((res = recvmsg(socket, &msg, 0)) == -1) {
938+ MH_ERROR("Failed to receive message");
939+ return false;
940+ } else if (res == 0) {
941+ MH_ERROR("Socket shutdown while receiving buffer data");
942+ return false;
943+ }
944+
945+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
946+
947+ memmove(&data->fd, CMSG_DATA(cmsg), sizeof data->fd);
948+
949+ MH_DEBUG("Extracted fd %d", data->fd);
950+ MH_DEBUG("width %d", data->meta.width);
951+ MH_DEBUG("height %d", data->meta.height);
952+ MH_DEBUG("fourcc 0x%X", data->meta.fourcc);
953+ MH_DEBUG("stride %d", data->meta.stride);
954+ MH_DEBUG("offset %d", data->meta.offset);
955+
956+ return true;
957+ }
958+
959+ static void read_sock_events(const media::Player::PlayerKey key,
960+ int sock_fd,
961+ promise<BufferData>& prom_buff,
962+ core::Signal<void>& frame_available)
963+ {
964+ static const char *consumer_socket = "media-consumer";
965+
966+ struct sockaddr_un local;
967+ int len;
968+ BufferData buff_data;
969+
970+ if (sock_fd == -1) {
971+ MH_ERROR("Cannot create buffer consumer socket: %s (%d)",
972+ strerror(errno), errno);
973+ return;
974+ }
975+
976+ ostringstream sock_name_ss;
977+ sock_name_ss << consumer_socket << key;
978+ local.sun_family = AF_UNIX;
979+ local.sun_path[0] = '\0';
980+ strcpy(local.sun_path + 1, sock_name_ss.str().c_str());
981+ len = sizeof(local.sun_family) + sock_name_ss.str().length() + 1;
982+ if (bind(sock_fd, (struct sockaddr *) &local, len) == -1) {
983+ MH_ERROR("Cannot bind consumer socket: %s (%d)",
984+ strerror(errno), errno);
985+ return;
986+ }
987+
988+ // Wait for buffer descriptions, pass them to rendering thread
989+ if (!receive_buff(sock_fd, &buff_data))
990+ return;
991+
992+ prom_buff.set_value(buff_data);
993+
994+ // Now signal frame syncs
995+ while(true) {
996+ ssize_t res;
997+ char c;
998+
999+ res = recv(sock_fd, &c, sizeof c, 0);
1000+ if (res == -1) {
1001+ MH_ERROR("while waiting sync: %s (%d)",
1002+ strerror(errno), errno);
1003+ return;
1004+ } else if (res == 0) {
1005+ MH_DEBUG("Socket shutdown");
1006+ return;
1007+ }
1008+
1009+ frame_available();
1010+ }
1011+ }
1012+
1013+ bool find_extension(const string& extensions, const string& ext)
1014+ {
1015+ size_t len_all = extensions.length();
1016+ size_t len = ext.length();
1017+ size_t pos = 0;
1018+
1019+ while ((pos = extensions.find(ext, pos)) != string::npos) {
1020+ if (pos + len == len_all || extensions[pos + len] == ' ')
1021+ return true;
1022+
1023+ pos = pos + len;
1024+ }
1025+
1026+ return false;
1027+ }
1028+
1029+ Private(uint32_t gl_texture, const media::Player::PlayerKey key)
1030+ : gl_texture{gl_texture},
1031+ prom_buff{},
1032+ fut_buff{prom_buff.get_future()},
1033+ sock_fd{socket(AF_UNIX, SOCK_DGRAM, 0)},
1034+ sock_thread{read_sock_events, key, sock_fd,
1035+ ref(prom_buff), ref(frame_available)},
1036+ egl_image{EGL_NO_IMAGE_KHR},
1037+ buf_fd{-1}
1038+ {
1039+ const char *extensions;
1040+ const char *egl_needed[] = {"EGL_KHR_image_base",
1041+ "EGL_EXT_image_dma_buf_import"};
1042+ EGLDisplay egl_display = eglGetCurrentDisplay();
1043+ size_t i;
1044+
1045+ // Retrieve EGL extensions from current display, then make sure the ones
1046+ // we need are present.
1047+ extensions = eglQueryString (egl_display, EGL_EXTENSIONS);
1048+ if (!extensions)
1049+ throw runtime_error {"Error querying EGL extensions"};
1050+
1051+ for (i = 0; i < sizeof(egl_needed)/sizeof(egl_needed[0]); ++i) {
1052+ if (!find_extension(extensions, egl_needed[i])) {
1053+ MH_DEBUG("%s not supported", egl_needed[i]);
1054+ //ostringstream oss;
1055+ //oss << egl_needed[i] << " not supported";
1056+ // TODO: The returned extensions do not really reflect what is
1057+ // supported by the system, and do not include the ones we need.
1058+ // It is probably related to how qt initializes EGL, because
1059+ // mirsink does not show this problem. So we need to
1060+ // check why extensions is different from es2_info output.
1061+ //throw runtime_error {oss.str().c_str()};
1062+ }
1063+ }
1064+
1065+ // TODO this returns a NULL pointer, probably same issue as with eglQueryString
1066+ // extensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
1067+ // if (!extensions)
1068+ // throw runtime_error {"Error querying OpenGL ES extensions"};
1069+
1070+ // if (!find_extension(extensions, "GL_OES_EGL_image_external"))
1071+ // throw runtime_error {"GL_OES_EGL_image_external is not supported"};
1072+
1073+ // Dynamically load functions from extensions (they are not
1074+ // automatically exported by EGL library).
1075+ _eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)
1076+ eglGetProcAddress("eglCreateImageKHR");
1077+ _eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)
1078+ eglGetProcAddress("eglDestroyImageKHR");
1079+ _glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
1080+ eglGetProcAddress("glEGLImageTargetTexture2DOES");
1081+
1082+ if (_eglCreateImageKHR == nullptr || _eglDestroyImageKHR == nullptr ||
1083+ _glEGLImageTargetTexture2DOES == nullptr)
1084+ throw runtime_error {"Error when loading extensions"};
1085+ }
1086+
1087+ ~Private()
1088+ {
1089+ if (sock_fd != -1) {
1090+ shutdown(sock_fd, SHUT_RDWR);
1091+ sock_thread.join();
1092+ close(sock_fd);
1093+ }
1094+
1095+ if (buf_fd != -1)
1096+ close(buf_fd);
1097+
1098+ if (egl_image != EGL_NO_IMAGE_KHR)
1099+ _eglDestroyImageKHR(eglGetCurrentDisplay(), egl_image);
1100+ }
1101+
1102+ // This imports dma_buf buffers by using the EGL_EXT_image_dma_buf_import
1103+ // extension. The buffers have been previously exported in mirsink, using
1104+ // EGL_MESA_image_dma_buf_export extension. After that, we bind the buffer
1105+ // to the app texture by using GL_OES_EGL_image_external extension.
1106+ bool import_buffer(const BufferData *buf_data)
1107+ {
1108+ GLenum err;
1109+ EGLDisplay egl_display = eglGetCurrentDisplay();
1110+ EGLint image_attrs[] = {
1111+ EGL_WIDTH, buf_data->meta.width,
1112+ EGL_HEIGHT, buf_data->meta.height,
1113+ EGL_LINUX_DRM_FOURCC_EXT, buf_data->meta.fourcc,
1114+ EGL_DMA_BUF_PLANE0_FD_EXT, buf_data->fd,
1115+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, buf_data->meta.offset,
1116+ EGL_DMA_BUF_PLANE0_PITCH_EXT, buf_data->meta.stride,
1117+ EGL_NONE
1118+ };
1119+
1120+ buf_fd = buf_data->fd;
1121+ egl_image = _eglCreateImageKHR(egl_display, EGL_NO_CONTEXT,
1122+ EGL_LINUX_DMA_BUF_EXT, NULL, image_attrs);
1123+ if (egl_image == EGL_NO_IMAGE_KHR) {
1124+ MH_ERROR("eglCreateImageKHR error 0x%X", eglGetError());
1125+ return false;
1126+ }
1127+
1128+ // TODO Do this when swapping if we end up importing more than one buffer
1129+ glBindTexture(GL_TEXTURE_2D, gl_texture);
1130+ _glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image);
1131+
1132+ while((err = glGetError()) != GL_NO_ERROR)
1133+ MH_WARNING("OpenGL error 0x%X", err);
1134+
1135+ MH_DEBUG("Image successfully imported");
1136+
1137+ return true;
1138+ }
1139+
1140+ uint32_t gl_texture;
1141+ promise<BufferData> prom_buff;
1142+ future<BufferData> fut_buff;
1143+ core::Signal<void> frame_available;
1144+ int sock_fd;
1145+ thread sock_thread;
1146+ EGLImageKHR egl_image;
1147+ int buf_fd;
1148+ PFNEGLCREATEIMAGEKHRPROC _eglCreateImageKHR;
1149+ PFNEGLDESTROYIMAGEKHRPROC _eglDestroyImageKHR;
1150+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC _glEGLImageTargetTexture2DOES;
1151+};
1152+
1153+function<video::Sink::Ptr(uint32_t)>
1154+video::EglSink::factory_for_key(const media::Player::PlayerKey& key)
1155+{
1156+ return [key](uint32_t texture)
1157+ {
1158+ return video::Sink::Ptr{new video::EglSink{texture, key}};
1159+ };
1160+}
1161+
1162+video::EglSink::EglSink(uint32_t gl_texture,
1163+ const media::Player::PlayerKey key)
1164+ : d{new Private{gl_texture, key}}
1165+{
1166+}
1167+
1168+video::EglSink::~EglSink()
1169+{
1170+}
1171+
1172+const core::Signal<void>& video::EglSink::frame_available() const
1173+{
1174+ return d->frame_available;
1175+}
1176+
1177+bool video::EglSink::transformation_matrix(float *matrix) const
1178+{
1179+ // TODO: Can we get orientation on unity8 desktop somehow?
1180+ static const float identity_4x4[] = { 1, 0, 0, 0,
1181+ 0, 1, 0, 0,
1182+ 0, 0, 1, 0,
1183+ 0, 0, 0, 1 };
1184+
1185+ memcpy(matrix, identity_4x4, sizeof identity_4x4);
1186+ return true;
1187+}
1188+
1189+bool video::EglSink::swap_buffers() const
1190+{
1191+ // First time called, import buffers
1192+ if (d->egl_image == EGL_NO_IMAGE_KHR) {
1193+ BufferData buf_data = d->fut_buff.get();
1194+ if (!d->import_buffer(&buf_data))
1195+ return false;
1196+ }
1197+
1198+ // We need to do nothing here, as the only buffer has already been mapped.
1199+ // TODO Change when we implement a buffer queue.
1200+
1201+ return true;
1202+}
1203
1204=== added file 'src/core/media/video/egl_sink.h'
1205--- src/core/media/video/egl_sink.h 1970-01-01 00:00:00 +0000
1206+++ src/core/media/video/egl_sink.h 2017-02-17 15:49:36 +0000
1207@@ -0,0 +1,73 @@
1208+/*
1209+ * Copyright © 2017 Canonical Ltd.
1210+ *
1211+ * This program is free software: you can redistribute it and/or modify it
1212+ * under the terms of the GNU Lesser General Public License version 3,
1213+ * as published by the Free Software Foundation.
1214+ *
1215+ * This program is distributed in the hope that it will be useful,
1216+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1217+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1218+ * GNU Lesser General Public License for more details.
1219+ *
1220+ * You should have received a copy of the GNU Lesser General Public License
1221+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1222+ *
1223+ * Authored by: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
1224+ */
1225+#ifndef CORE_UBUNTU_MEDIA_VIDEO_EGL_SINK_H_
1226+#define CORE_UBUNTU_MEDIA_VIDEO_EGL_SINK_H_
1227+
1228+#include <core/media/video/sink.h>
1229+
1230+#include <core/media/player.h>
1231+
1232+#include <functional>
1233+
1234+namespace core
1235+{
1236+namespace ubuntu
1237+{
1238+namespace media
1239+{
1240+namespace video
1241+{
1242+class EglSink : public video::Sink
1243+{
1244+public:
1245+ // Returns a factory functor that allows for creating actual sink instances.
1246+ static std::function<video::Sink::Ptr(std::uint32_t)>
1247+ factory_for_key(const media::Player::PlayerKey&);
1248+
1249+ // Need this to avoid std::unique_ptr complaining about forward-declared Private.
1250+ ~EglSink();
1251+
1252+ // The signal is emitted whenever a new frame is available and a subsequent
1253+ // call to swap_buffers will not block and return true.
1254+ const core::Signal<void>& frame_available() const override;
1255+
1256+ // Queries the transformation matrix for the current frame, placing the data into 'matrix'
1257+ // and returns true or returns false and leaves 'matrix' unchanged in case
1258+ // of issues.
1259+ bool transformation_matrix(float* matrix) const override;
1260+
1261+ // Releases the current buffer, and consumes the next buffer in the queue,
1262+ // making it available for consumption by consumers of this API in an
1263+ // implementation-specific way. Clients will usually rely on a GL texture
1264+ // to receive the latest buffer.
1265+ bool swap_buffers() const override;
1266+
1267+private:
1268+ // Creates a new instance for the given gl texture, connecting to the remote part known
1269+ // under the given key or throw in case of issues.
1270+ EglSink(std::uint32_t gl_texture, const media::Player::PlayerKey key);
1271+
1272+ struct Private;
1273+ std::unique_ptr<Private> d;
1274+};
1275+}
1276+}
1277+}
1278+}
1279+
1280+#endif // CORE_UBUNTU_MEDIA_VIDEO_EGL_SINK_H_
1281
1282=== modified file 'src/core/media/video/hybris_gl_sink.cpp'
1283--- src/core/media/video/hybris_gl_sink.cpp 2015-01-29 12:12:43 +0000
1284+++ src/core/media/video/hybris_gl_sink.cpp 2017-02-17 15:49:36 +0000
1285@@ -18,7 +18,6 @@
1286
1287 #include <core/media/video/hybris_gl_sink.h>
1288
1289-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
1290 // Hybris
1291 #include <hybris/media/media_codec_layer.h>
1292 #include <hybris/media/surface_texture_client_hybris.h>
1293@@ -113,4 +112,3 @@
1294 gl_consumer_update_texture(d->gl_texture_consumer);
1295 return true;
1296 }
1297-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
1298
1299=== modified file 'src/core/media/video/platform_default_sink.cpp'
1300--- src/core/media/video/platform_default_sink.cpp 2016-08-15 19:27:29 +0000
1301+++ src/core/media/video/platform_default_sink.cpp 2017-02-17 15:49:36 +0000
1302@@ -19,6 +19,8 @@
1303 #include "core/media/logger/logger.h"
1304
1305 #include <core/media/video/platform_default_sink.h>
1306+#include <core/media/video/egl_sink.h>
1307+#include <core/media/video/hybris_gl_sink.h>
1308
1309 namespace media = core::ubuntu::media;
1310 namespace video = core::ubuntu::media::video;
1311@@ -55,9 +57,6 @@
1312 };
1313 }
1314
1315-#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
1316-#include <core/media/video/hybris_gl_sink.h>
1317-
1318 video::SinkFactory video::make_platform_default_sink_factory(const media::Player::PlayerKey& key,
1319 const media::AVBackend::Backend b)
1320 {
1321@@ -66,6 +65,9 @@
1322 case media::AVBackend::Backend::hybris:
1323 MH_DEBUG("Using hybris video sink");
1324 return video::HybrisGlSink::factory_for_key(key);
1325+ case media::AVBackend::Backend::mir:
1326+ MH_DEBUG("Using mir/egl video sink");
1327+ return video::EglSink::factory_for_key(key);
1328 case media::AVBackend::Backend::none:
1329 MH_WARNING(
1330 "No video backend selected. Video rendering functionality won't work."
1331@@ -76,9 +78,3 @@
1332 return video::HybrisGlSink::factory_for_key(key);
1333 }
1334 }
1335-#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
1336-video::SinkFactory video::make_platform_default_sink_factory(const media::Player::PlayerKey&)
1337-{
1338- return [](std::uint32_t) { return video::Sink::Ptr{}; };
1339-}
1340-#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
1341
1342=== added file 'src/core/media/video/socket_types.h'
1343--- src/core/media/video/socket_types.h 1970-01-01 00:00:00 +0000
1344+++ src/core/media/video/socket_types.h 2017-02-17 15:49:36 +0000
1345@@ -0,0 +1,50 @@
1346+/*
1347+ * Copyright © 2017 Canonical Ltd.
1348+ *
1349+ * This program is free software: you can redistribute it and/or modify it
1350+ * under the terms of the GNU Lesser General Public License version 3,
1351+ * as published by the Free Software Foundation.
1352+ *
1353+ * This program is distributed in the hope that it will be useful,
1354+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1355+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1356+ * GNU Lesser General Public License for more details.
1357+ *
1358+ * You should have received a copy of the GNU Lesser General Public License
1359+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1360+ *
1361+ * Authored by: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
1362+ */
1363+#ifndef CORE_UBUNTU_MEDIA_VIDEO_SOCKET_TYPES_H_
1364+#define CORE_UBUNTU_MEDIA_VIDEO_SOCKET_TYPES_H_
1365+
1366+namespace core
1367+{
1368+namespace ubuntu
1369+{
1370+namespace media
1371+{
1372+namespace video
1373+{
1374+
1375+struct BufferMetadata
1376+{
1377+ int width;
1378+ int height;
1379+ int fourcc;
1380+ int stride;
1381+ int offset;
1382+};
1383+
1384+struct BufferData
1385+{
1386+ int fd;
1387+ BufferMetadata meta;
1388+};
1389+
1390+}
1391+}
1392+}
1393+}
1394+
1395+#endif // CORE_UBUNTU_MEDIA_VIDEO_SOCKET_TYPES_H_
1396
1397=== modified file 'tests/CMakeLists.txt'
1398--- tests/CMakeLists.txt 2015-11-02 22:38:06 +0000
1399+++ tests/CMakeLists.txt 2017-02-17 15:49:36 +0000
1400@@ -16,9 +16,17 @@
1401 endif (MEDIA_HUB_ENABLE_DBUS_TEST_RUNNER)
1402
1403 # Build with system gmock and embedded gtest
1404-set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
1405-set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
1406-set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
1407+if (EXISTS "/usr/src/googletest")
1408+ # As of version 1.8.0
1409+ set (GMOCK_SOURCE_DIR "/usr/src/googletest/googlemock" CACHE PATH "gmock source directory")
1410+ set (GMOCK_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/include" CACHE PATH "gmock source include directory")
1411+ set (GTEST_INCLUDE_DIRS "/usr/src/googletest/googletest/include" CACHE PATH "gtest source include directory")
1412+else()
1413+ set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
1414+ set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
1415+ set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
1416+endif()
1417+
1418
1419 add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
1420
1421
1422=== modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
1423--- tests/unit-tests/test-gstreamer-engine.cpp 2016-08-11 19:09:00 +0000
1424+++ tests/unit-tests/test-gstreamer-engine.cpp 2017-02-17 15:49:36 +0000
1425@@ -65,7 +65,7 @@
1426
1427 TEST(GStreamerEngine, construction_and_deconstruction_works)
1428 {
1429- gstreamer::Engine engine;
1430+ gstreamer::Engine engine{0};
1431 }
1432
1433 TEST(GStreamerEngine, DISABLED_setting_uri_and_starting_audio_only_playback_works)
1434@@ -78,7 +78,7 @@
1435 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1436 core::ubuntu::media::Engine::State::ready);
1437
1438- gstreamer::Engine engine;
1439+ gstreamer::Engine engine{0};
1440
1441 engine.track_meta_data().changed().connect(
1442 [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
1443@@ -127,7 +127,7 @@
1444 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1445 core::ubuntu::media::Engine::State::ready);
1446
1447- gstreamer::Engine engine;
1448+ gstreamer::Engine engine{0};
1449
1450 engine.track_meta_data().changed().connect(
1451 [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
1452@@ -200,7 +200,7 @@
1453 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1454 core::ubuntu::media::Engine::State::ready);
1455
1456- gstreamer::Engine engine;
1457+ gstreamer::Engine engine{0};
1458
1459 engine.state().changed().connect(
1460 std::bind(
1461@@ -231,7 +231,7 @@
1462 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1463 core::ubuntu::media::Engine::State::ready);
1464
1465- gstreamer::Engine engine;
1466+ gstreamer::Engine engine{0};
1467
1468 engine.state().changed().connect(
1469 std::bind(
1470@@ -280,7 +280,7 @@
1471 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1472 core::ubuntu::media::Engine::State::ready);
1473
1474- gstreamer::Engine engine;
1475+ gstreamer::Engine engine{0};
1476
1477 engine.state().changed().connect(
1478 std::bind(
1479@@ -327,7 +327,7 @@
1480 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1481 core::ubuntu::media::Engine::State::ready);
1482
1483- gstreamer::Engine engine;
1484+ gstreamer::Engine engine{0};
1485
1486 engine.state().changed().connect(
1487 std::bind(
1488@@ -365,7 +365,7 @@
1489 core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst(
1490 core::ubuntu::media::Engine::State::ready);
1491
1492- gstreamer::Engine engine;
1493+ gstreamer::Engine engine{0};
1494 engine.state().changed().connect(
1495 std::bind(
1496 &core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State>::trigger,
1497@@ -402,7 +402,7 @@
1498
1499 TEST(GStreamerEngine, provides_non_null_meta_data_extractor)
1500 {
1501- gstreamer::Engine engine;
1502+ gstreamer::Engine engine{0};
1503 EXPECT_NE(nullptr, engine.meta_data_extractor());
1504 }
1505
1506@@ -413,7 +413,7 @@
1507 std::remove(test_file.c_str());
1508 ASSERT_TRUE(test::copy_test_media_file_to("test.mp3", test_file));
1509
1510- gstreamer::Engine engine;
1511+ gstreamer::Engine engine{0};
1512
1513 core::ubuntu::media::Track::MetaData md;
1514 ASSERT_NO_THROW({

Subscribers

People subscribed via source and target branches