Merge lp:~alfonsosanchezbeato/media-hub/video-desktop-support into lp:media-hub
- video-desktop-support
- Merge into trunk
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 |
Related bugs: |
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
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
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({ |
Looks very good overall. Some comments inline below.