Merge lp:~thomas-voss/media-hub/move-playbin-implementation-to-cpp-file into lp:media-hub
- move-playbin-implementation-to-cpp-file
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ricardo Mendoza |
Approved revision: | 109 |
Merged at revision: | 118 |
Proposed branch: | lp:~thomas-voss/media-hub/move-playbin-implementation-to-cpp-file |
Merge into: | lp:media-hub |
Prerequisite: | lp:~thomas-voss/media-hub/introduce-recorder-observer-interface |
Diff against target: |
1177 lines (+608/-495) 4 files modified
src/core/media/CMakeLists.txt (+2/-0) src/core/media/gstreamer/engine.cpp (+1/-1) src/core/media/gstreamer/playbin.cpp (+556/-0) src/core/media/gstreamer/playbin.h (+49/-494) |
To merge this branch: | bzr merge lp:~thomas-voss/media-hub/move-playbin-implementation-to-cpp-file |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Jim Hodapp (community) | code | Approve | |
Review via email:
|
Commit message
Move gstreamer::Playbin implementation to its own cpp file, thus internalizing the Hybris setup portions.
Make sure that media-hub-service knows about media::
Description of the change
Move gstreamer::Playbin implementation to its own cpp file, thus internalizing the Hybris setup portions.
Make sure that media-hub-service knows about media::
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
- 104. By Thomas Voß
-
Remerge prereq branch.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:104
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 105. By Thomas Voß
-
[ Jim Hodapp ]
* Resubmitting with prerequisite branch (LP: #1331041)
[ Justin McPherson ]
* Resubmitting with prerequisite branch (LP: #1331041)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:105
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 106. By Thomas Voß
-
[ Jim Hodapp ]
* Error reporting all the way up to the app level from the playbin
pipeline.
[ Ubuntu daily release ]
* New rebuild forced
[ Jim Hodapp ]
* Don't auto-resume playback of videos after a phone call ends. (LP:
#1411273)
[ Ubuntu daily release ]
* New rebuild forced
[ Ricardo Salveti de Araujo ]
* service_implementation: adding debug for call started/ended signals.
Make sure account and connection are available when setting up
account manager (patch from Gustavo Boiko). call_monitor: don't
check caps when hooking up on/off signals, until bug 1409125 is
fixed. Enable parallel building . (LP: #1409125)
[ Jim Hodapp ]
* Pause playback when recording begins. (LP: #1398047)
[ Ricardo Salveti de Araujo ]
* call_monitor.cpp: waiting for bridge to be up, and also protecting
the on_change call (LP: #1408137)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:106
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 107. By Thomas Voß
-
Merge prereq branch.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:107
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Francis Ginther (fginther) wrote : | # |
The jenkins node for the i386 build failed, I've restarted a new ci run.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:107
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 108. By Thomas Voß
-
Merge prereq branch.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:108
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 109. By Thomas Voß
-
* debian/control:
- Removing pre-depends that are not required
- Bumping standards-version to 3.9.6
[ Ricardo Salveti de Araujo ]
* Migrating tests to use ogg instead of mp3/avi removed:
tests/h264.avi tests/test.mp3 added: tests/test-audio-1. ogg
tests/test-video. ogg tests/test.mp3 renamed: tests/test.ogg =>
tests/test-audio. ogg
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:109
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'src/core/media/CMakeLists.txt' |
2 | --- src/core/media/CMakeLists.txt 2015-03-12 11:42:10 +0000 |
3 | +++ src/core/media/CMakeLists.txt 2015-03-12 11:42:10 +0000 |
4 | @@ -93,6 +93,7 @@ |
5 | recorder_observer.cpp |
6 | hybris_recorder_observer.cpp |
7 | gstreamer/engine.cpp |
8 | + gstreamer/playbin.cpp |
9 | |
10 | player_skeleton.cpp |
11 | player_implementation.cpp |
12 | @@ -105,6 +106,7 @@ |
13 | target_link_libraries( |
14 | media-hub-service |
15 | |
16 | + media-hub-client |
17 | media-hub-common |
18 | call-monitor |
19 | ${DBUS_LIBRARIES} |
20 | |
21 | === modified file 'src/core/media/gstreamer/engine.cpp' |
22 | --- src/core/media/gstreamer/engine.cpp 2015-03-12 11:42:10 +0000 |
23 | +++ src/core/media/gstreamer/engine.cpp 2015-03-12 11:42:10 +0000 |
24 | @@ -333,7 +333,7 @@ |
25 | |
26 | bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri) |
27 | { |
28 | - d->playbin.set_uri(uri); |
29 | + d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}); |
30 | return true; |
31 | } |
32 | |
33 | |
34 | === added file 'src/core/media/gstreamer/playbin.cpp' |
35 | --- src/core/media/gstreamer/playbin.cpp 1970-01-01 00:00:00 +0000 |
36 | +++ src/core/media/gstreamer/playbin.cpp 2015-03-12 11:42:10 +0000 |
37 | @@ -0,0 +1,556 @@ |
38 | +/* |
39 | + * Copyright © 2013 Canonical Ltd. |
40 | + * |
41 | + * This program is free software: you can redistribute it and/or modify it |
42 | + * under the terms of the GNU Lesser General Public License version 3, |
43 | + * as published by the Free Software Foundation. |
44 | + * |
45 | + * This program is distributed in the hope that it will be useful, |
46 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
47 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
48 | + * GNU Lesser General Public License for more details. |
49 | + * |
50 | + * You should have received a copy of the GNU Lesser General Public License |
51 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
52 | + * |
53 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
54 | + */ |
55 | + |
56 | +#include <core/media/gstreamer/playbin.h> |
57 | + |
58 | +#include <core/media/gstreamer/engine.h> |
59 | + |
60 | +#if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER) |
61 | +#include <hybris/media/surface_texture_client_hybris.h> |
62 | +#include <hybris/media/media_codec_layer.h> |
63 | + |
64 | +namespace |
65 | +{ |
66 | +void setup_video_sink_for_buffer_streaming(GstElement* video_sink) |
67 | +{ |
68 | + // Get the service-side BufferQueue (IGraphicBufferProducer) and associate it with |
69 | + // the SurfaceTextureClientHybris instance |
70 | + IGBPWrapperHybris igbp = decoding_service_get_igraphicbufferproducer(); |
71 | + SurfaceTextureClientHybris stc = surface_texture_client_create_by_igbp(igbp); |
72 | + // Because mirsink is being loaded, we are definitely doing * hardware rendering. |
73 | + surface_texture_client_set_hardware_rendering (stc, TRUE); |
74 | + g_object_set (G_OBJECT (video_sink), "surface", static_cast<gpointer>(stc), static_cast<char*>(NULL)); |
75 | +} |
76 | +} |
77 | +#else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER |
78 | +namespace |
79 | +{ |
80 | +void setup_video_sink_for_buffer_streaming(GstElement*) |
81 | +{ |
82 | + throw core::ubuntu::media::Player::Error::OutOfProcessBufferStreamingNotSupported{}; |
83 | +} |
84 | +} |
85 | +#endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER |
86 | + |
87 | +namespace |
88 | +{ |
89 | +bool is_mir_video_sink() |
90 | +{ |
91 | + return g_strcmp0(::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), "mirsink") == 0; |
92 | +} |
93 | +} |
94 | +// Uncomment to generate a dot file at the time that the pipeline |
95 | +// goes to the PLAYING state. Make sure to export GST_DEBUG_DUMP_DOT_DIR |
96 | +// before starting media-hub-server. To convert the dot file to something |
97 | +// other image format, use: dot pipeline.dot -Tpng -o pipeline.png |
98 | +//#define DEBUG_GST_PIPELINE |
99 | + |
100 | +namespace media = core::ubuntu::media; |
101 | +namespace video = core::ubuntu::media::video; |
102 | + |
103 | +const std::string& gstreamer::Playbin::pipeline_name() |
104 | +{ |
105 | + static const std::string s{"playbin"}; |
106 | + return s; |
107 | +} |
108 | + |
109 | +void gstreamer::Playbin::about_to_finish(GstElement*, gpointer user_data) |
110 | +{ |
111 | + auto thiz = static_cast<Playbin*>(user_data); |
112 | + thiz->signals.about_to_finish(); |
113 | +} |
114 | + |
115 | +void gstreamer::Playbin::source_setup(GstElement*, |
116 | + GstElement *source, |
117 | + gpointer user_data) |
118 | +{ |
119 | + if (user_data == nullptr) |
120 | + return; |
121 | + |
122 | + static_cast<Playbin*>(user_data)->setup_source(source); |
123 | +} |
124 | + |
125 | +gstreamer::Playbin::Playbin() |
126 | + : pipeline(gst_element_factory_make("playbin", pipeline_name().c_str())), |
127 | + bus{gst_element_get_bus(pipeline)}, |
128 | + file_type(MEDIA_FILE_TYPE_NONE), |
129 | + video_sink(nullptr), |
130 | + on_new_message_connection( |
131 | + bus.on_new_message.connect( |
132 | + std::bind( |
133 | + &Playbin::on_new_message, |
134 | + this, |
135 | + std::placeholders::_1))), |
136 | + is_seeking(false), |
137 | + player_lifetime(media::Player::Lifetime::normal) |
138 | +{ |
139 | + if (!pipeline) |
140 | + throw std::runtime_error("Could not create pipeline for playbin."); |
141 | + |
142 | + // Add audio and/or video sink elements depending on environment variables |
143 | + // being set or not set |
144 | + setup_pipeline_for_audio_video(); |
145 | + |
146 | + g_signal_connect( |
147 | + pipeline, |
148 | + "about-to-finish", |
149 | + G_CALLBACK(about_to_finish), |
150 | + this |
151 | + ); |
152 | + |
153 | + g_signal_connect( |
154 | + pipeline, |
155 | + "source-setup", |
156 | + G_CALLBACK(source_setup), |
157 | + this |
158 | + ); |
159 | +} |
160 | + |
161 | +gstreamer::Playbin::~Playbin() |
162 | +{ |
163 | + if (pipeline) |
164 | + gst_object_unref(pipeline); |
165 | +} |
166 | + |
167 | +void gstreamer::Playbin::reset() |
168 | +{ |
169 | + std::cout << "Client died, resetting pipeline" << std::endl; |
170 | + // When the client dies, tear down the current pipeline and get it |
171 | + // in a state that is ready for the next client that connects to the |
172 | + // service |
173 | + |
174 | + // Don't reset the pipeline if we want to resume |
175 | + if (player_lifetime != media::Player::Lifetime::resumable) { |
176 | + reset_pipeline(); |
177 | + } |
178 | + // Signal to the Player class that the client side has disconnected |
179 | + signals.client_disconnected(); |
180 | +} |
181 | + |
182 | +void gstreamer::Playbin::reset_pipeline() |
183 | +{ |
184 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
185 | + auto ret = gst_element_set_state(pipeline, GST_STATE_NULL); |
186 | + switch(ret) |
187 | + { |
188 | + case GST_STATE_CHANGE_FAILURE: |
189 | + std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl; |
190 | + break; |
191 | + case GST_STATE_CHANGE_NO_PREROLL: |
192 | + case GST_STATE_CHANGE_SUCCESS: |
193 | + case GST_STATE_CHANGE_ASYNC: |
194 | + break; |
195 | + default: |
196 | + std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl; |
197 | + } |
198 | + file_type = MEDIA_FILE_TYPE_NONE; |
199 | +} |
200 | + |
201 | +void gstreamer::Playbin::on_new_message(const Bus::Message& message) |
202 | +{ |
203 | + switch(message.type) |
204 | + { |
205 | + case GST_MESSAGE_ERROR: |
206 | + signals.on_error(message.detail.error_warning_info); |
207 | + break; |
208 | + case GST_MESSAGE_WARNING: |
209 | + signals.on_warning(message.detail.error_warning_info); |
210 | + break; |
211 | + case GST_MESSAGE_INFO: |
212 | + signals.on_info(message.detail.error_warning_info); |
213 | + break; |
214 | + case GST_MESSAGE_TAG: |
215 | + { |
216 | + gchar *orientation; |
217 | + if (gst_tag_list_get_string(message.detail.tag.tag_list, "image-orientation", &orientation)) |
218 | + { |
219 | + // If the image-orientation tag is in the GstTagList, signal the Engine |
220 | + signals.on_orientation_changed(orientation_lut(orientation)); |
221 | + g_free (orientation); |
222 | + } |
223 | + |
224 | + signals.on_tag_available(message.detail.tag); |
225 | + } |
226 | + break; |
227 | + case GST_MESSAGE_STATE_CHANGED: |
228 | + signals.on_state_changed(message.detail.state_changed); |
229 | + break; |
230 | + case GST_MESSAGE_ASYNC_DONE: |
231 | + if (is_seeking) |
232 | + { |
233 | + // FIXME: Pass the actual playback time position to the signal call |
234 | + signals.on_seeked_to(0); |
235 | + is_seeking = false; |
236 | + } |
237 | + break; |
238 | + case GST_MESSAGE_EOS: |
239 | + signals.on_end_of_stream(); |
240 | + default: |
241 | + break; |
242 | + } |
243 | +} |
244 | + |
245 | +gstreamer::Bus& gstreamer::Playbin::message_bus() |
246 | +{ |
247 | + return bus; |
248 | +} |
249 | + |
250 | +void gstreamer::Playbin::setup_pipeline_for_audio_video() |
251 | +{ |
252 | + gint flags; |
253 | + g_object_get (pipeline, "flags", &flags, nullptr); |
254 | + flags |= GST_PLAY_FLAG_AUDIO; |
255 | + flags |= GST_PLAY_FLAG_VIDEO; |
256 | + flags &= ~GST_PLAY_FLAG_TEXT; |
257 | + g_object_set (pipeline, "flags", flags, nullptr); |
258 | + |
259 | + if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") != nullptr) |
260 | + { |
261 | + auto audio_sink = gst_element_factory_make ( |
262 | + ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"), |
263 | + "audio-sink"); |
264 | + |
265 | + std::cout << "audio_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") << std::endl; |
266 | + |
267 | + g_object_set ( |
268 | + pipeline, |
269 | + "audio-sink", |
270 | + audio_sink, |
271 | + NULL); |
272 | + } |
273 | + |
274 | + if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr) |
275 | + { |
276 | + video_sink = gst_element_factory_make ( |
277 | + ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), |
278 | + "video-sink"); |
279 | + |
280 | + std::cout << "video_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") << std::endl; |
281 | + |
282 | + g_object_set ( |
283 | + pipeline, |
284 | + "video-sink", |
285 | + video_sink, |
286 | + NULL); |
287 | + } |
288 | +} |
289 | + |
290 | +void gstreamer::Playbin::create_video_sink(uint32_t) |
291 | +{ |
292 | + if (not video_sink) throw std::logic_error |
293 | + { |
294 | + "No video sink configured for the current pipeline" |
295 | + }; |
296 | + |
297 | + setup_video_sink_for_buffer_streaming(video_sink); |
298 | +} |
299 | + |
300 | +void gstreamer::Playbin::set_volume(double new_volume) |
301 | +{ |
302 | + g_object_set (pipeline, "volume", new_volume, NULL); |
303 | +} |
304 | + |
305 | +/** Translate the AudioStreamRole enum into a string */ |
306 | +std::string gstreamer::Playbin::get_audio_role_str(media::Player::AudioStreamRole audio_role) |
307 | +{ |
308 | + switch (audio_role) |
309 | + { |
310 | + case media::Player::AudioStreamRole::alarm: |
311 | + return "alarm"; |
312 | + break; |
313 | + case media::Player::AudioStreamRole::alert: |
314 | + return "alert"; |
315 | + break; |
316 | + case media::Player::AudioStreamRole::multimedia: |
317 | + return "multimedia"; |
318 | + break; |
319 | + case media::Player::AudioStreamRole::phone: |
320 | + return "phone"; |
321 | + break; |
322 | + default: |
323 | + return "multimedia"; |
324 | + break; |
325 | + } |
326 | +} |
327 | + |
328 | +media::Player::Orientation gstreamer::Playbin::orientation_lut(const gchar *orientation) |
329 | +{ |
330 | + if (g_strcmp0(orientation, "rotate-0") == 0) |
331 | + return media::Player::Orientation::rotate0; |
332 | + else if (g_strcmp0(orientation, "rotate-90") == 0) |
333 | + return media::Player::Orientation::rotate90; |
334 | + else if (g_strcmp0(orientation, "rotate-180") == 0) |
335 | + return media::Player::Orientation::rotate180; |
336 | + else if (g_strcmp0(orientation, "rotate-270") == 0) |
337 | + return media::Player::Orientation::rotate270; |
338 | + else |
339 | + return media::Player::Orientation::rotate0; |
340 | +} |
341 | + |
342 | +/** Sets the new audio stream role on the pulsesink in playbin */ |
343 | +void gstreamer::Playbin::set_audio_stream_role(media::Player::AudioStreamRole new_audio_role) |
344 | +{ |
345 | + GstElement *audio_sink = NULL; |
346 | + g_object_get (pipeline, "audio-sink", &audio_sink, NULL); |
347 | + |
348 | + std::string role_str("props,media.role=" + get_audio_role_str(new_audio_role)); |
349 | + std::cout << "Audio stream role: " << role_str << std::endl; |
350 | + |
351 | + GstStructure *props = gst_structure_from_string (role_str.c_str(), NULL); |
352 | + if (audio_sink != nullptr && props != nullptr) |
353 | + g_object_set (audio_sink, "stream-properties", props, NULL); |
354 | + else |
355 | + { |
356 | + std::cerr << |
357 | + "Warning: couldn't set audio stream role - couldn't get audio_sink from pipeline" << |
358 | + std::endl; |
359 | + } |
360 | + |
361 | + gst_structure_free (props); |
362 | +} |
363 | + |
364 | +void gstreamer::Playbin::set_lifetime(media::Player::Lifetime lifetime) |
365 | +{ |
366 | + player_lifetime = lifetime; |
367 | +} |
368 | + |
369 | +uint64_t gstreamer::Playbin::position() const |
370 | +{ |
371 | + int64_t pos = 0; |
372 | + gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos); |
373 | + |
374 | + // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly |
375 | + return static_cast<uint64_t>(pos); |
376 | +} |
377 | + |
378 | +uint64_t gstreamer::Playbin::duration() const |
379 | +{ |
380 | + int64_t dur = 0; |
381 | + gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur); |
382 | + |
383 | + // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly |
384 | + return static_cast<uint64_t>(dur); |
385 | +} |
386 | + |
387 | +void gstreamer::Playbin::set_uri( |
388 | + const std::string& uri, |
389 | + const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType()) |
390 | +{ |
391 | + reset_pipeline(); |
392 | + |
393 | + g_object_set(pipeline, "uri", uri.c_str(), NULL); |
394 | + if (is_video_file(uri)) |
395 | + file_type = MEDIA_FILE_TYPE_VIDEO; |
396 | + else if (is_audio_file(uri)) |
397 | + file_type = MEDIA_FILE_TYPE_AUDIO; |
398 | + |
399 | + request_headers = headers; |
400 | +} |
401 | + |
402 | +void gstreamer::Playbin::setup_source(GstElement *source) |
403 | +{ |
404 | + if (source == NULL || request_headers.empty()) |
405 | + return; |
406 | + |
407 | + if (request_headers.find("Cookie") != request_headers.end()) { |
408 | + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), |
409 | + "cookies") != NULL) { |
410 | + gchar ** cookies = g_strsplit(request_headers["Cookie"].c_str(), ";", 0); |
411 | + g_object_set(source, "cookies", cookies, NULL); |
412 | + g_strfreev(cookies); |
413 | + } |
414 | + } |
415 | + |
416 | + if (request_headers.find("User-Agent") != request_headers.end()) { |
417 | + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), |
418 | + "user-agent") != NULL) { |
419 | + g_object_set(source, "user-agent", request_headers["User-Agent"].c_str(), NULL); |
420 | + } |
421 | + } |
422 | +} |
423 | + |
424 | +std::string gstreamer::Playbin::uri() const |
425 | +{ |
426 | + gchar* data = nullptr; |
427 | + g_object_get(pipeline, "current-uri", &data, nullptr); |
428 | + |
429 | + std::string result((data == nullptr ? "" : data)); |
430 | + g_free(data); |
431 | + |
432 | + return result; |
433 | +} |
434 | + |
435 | +bool gstreamer::Playbin::set_state_and_wait(GstState new_state) |
436 | +{ |
437 | + static const std::chrono::nanoseconds state_change_timeout |
438 | + { |
439 | + // We choose a quite high value here as tests are run under valgrind |
440 | + // and gstreamer pipeline setup/state changes take longer in that scenario. |
441 | + // The value does not negatively impact runtime performance. |
442 | + std::chrono::milliseconds{5000} |
443 | + }; |
444 | + |
445 | + auto ret = gst_element_set_state(pipeline, new_state); |
446 | + bool result = false; GstState current, pending; |
447 | + switch(ret) |
448 | + { |
449 | + case GST_STATE_CHANGE_FAILURE: |
450 | + result = false; break; |
451 | + case GST_STATE_CHANGE_NO_PREROLL: |
452 | + case GST_STATE_CHANGE_SUCCESS: |
453 | + result = true; break; |
454 | + case GST_STATE_CHANGE_ASYNC: |
455 | + result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state( |
456 | + pipeline, |
457 | + ¤t, |
458 | + &pending, |
459 | + state_change_timeout.count()); |
460 | + break; |
461 | + } |
462 | + |
463 | + // We only should query the pipeline if we actually succeeded in |
464 | + // setting the requested state. |
465 | + if (result && new_state == GST_STATE_PLAYING) |
466 | + { |
467 | + // Get the video height/width from the video sink |
468 | + try |
469 | + { |
470 | + signals.on_video_dimensions_changed(get_video_dimensions()); |
471 | + } |
472 | + catch (const std::exception& e) |
473 | + { |
474 | + std::cerr << "Problem querying video dimensions: " << e.what() << std::endl; |
475 | + } |
476 | + catch (...) |
477 | + { |
478 | + std::cerr << "Problem querying video dimensions." << std::endl; |
479 | + } |
480 | + |
481 | +#ifdef DEBUG_GST_PIPELINE |
482 | + std::cout << "Dumping pipeline dot file" << std::endl; |
483 | + GST_DEBUG_BIN_TO_DOT_FILE((GstBin*)pipeline, GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); |
484 | +#endif |
485 | + } |
486 | + |
487 | + return result; |
488 | +} |
489 | + |
490 | +bool gstreamer::Playbin::seek(const std::chrono::microseconds& ms) |
491 | +{ |
492 | + is_seeking = true; |
493 | + return gst_element_seek_simple( |
494 | + pipeline, |
495 | + GST_FORMAT_TIME, |
496 | + (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), |
497 | + ms.count() * 1000); |
498 | +} |
499 | + |
500 | +core::ubuntu::media::video::Dimensions gstreamer::Playbin::get_video_dimensions() const |
501 | +{ |
502 | + if (not video_sink || not is_mir_video_sink()) |
503 | + throw std::runtime_error |
504 | + { |
505 | + "Missing video sink or video sink does not support query of width and height." |
506 | + }; |
507 | + |
508 | + // Initialize to default value prior to querying actual values from the sink. |
509 | + uint32_t video_width = 0, video_height = 0; |
510 | + g_object_get (video_sink, "height", &video_height, nullptr); |
511 | + g_object_get (video_sink, "width", &video_width, nullptr); |
512 | + // TODO(tvoss): We should probably check here if width and height are valid. |
513 | + return core::ubuntu::media::video::Dimensions |
514 | + { |
515 | + core::ubuntu::media::video::Height{video_height}, |
516 | + core::ubuntu::media::video::Width{video_width} |
517 | + }; |
518 | +} |
519 | + |
520 | +std::string gstreamer::Playbin::get_file_content_type(const std::string& uri) const |
521 | +{ |
522 | + if (uri.empty()) |
523 | + return std::string(); |
524 | + |
525 | + std::string filename(uri); |
526 | + std::cout << "filename: " << filename << std::endl; |
527 | + size_t pos = uri.find("file://"); |
528 | + if (pos != std::string::npos) |
529 | + filename = uri.substr(pos + 7, std::string::npos); |
530 | + else |
531 | + // Anything other than a file, for now claim that the type |
532 | + // is both audio and video. |
533 | + // FIXME: implement true net stream sampling and get the type from GstCaps |
534 | + return std::string("audio/video/"); |
535 | + |
536 | + |
537 | + GError *error = nullptr; |
538 | + std::unique_ptr<GFile, void(*)(void *)> file( |
539 | + g_file_new_for_path(filename.c_str()), g_object_unref); |
540 | + std::unique_ptr<GFileInfo, void(*)(void *)> info( |
541 | + g_file_query_info( |
542 | + file.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE "," |
543 | + G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_QUERY_INFO_NONE, |
544 | + /* cancellable */ NULL, &error), |
545 | + g_object_unref); |
546 | + if (!info) |
547 | + { |
548 | + std::string error_str(error->message); |
549 | + g_error_free(error); |
550 | + |
551 | + std::cout << "Failed to query the URI for the presence of video content: " |
552 | + << error_str << std::endl; |
553 | + return std::string(); |
554 | + } |
555 | + |
556 | + std::string content_type(g_file_info_get_attribute_string( |
557 | + info.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)); |
558 | + |
559 | + return content_type; |
560 | +} |
561 | + |
562 | +bool gstreamer::Playbin::is_audio_file(const std::string& uri) const |
563 | +{ |
564 | + if (uri.empty()) |
565 | + return false; |
566 | + |
567 | + if (get_file_content_type(uri).find("audio/") == 0) |
568 | + { |
569 | + std::cout << "Found audio content" << std::endl; |
570 | + return true; |
571 | + } |
572 | + |
573 | + return false; |
574 | +} |
575 | + |
576 | +bool gstreamer::Playbin::is_video_file(const std::string& uri) const |
577 | +{ |
578 | + if (uri.empty()) |
579 | + return false; |
580 | + |
581 | + if (get_file_content_type(uri).find("video/") == 0) |
582 | + { |
583 | + std::cout << "Found video content" << std::endl; |
584 | + return true; |
585 | + } |
586 | + |
587 | + return false; |
588 | +} |
589 | + |
590 | +gstreamer::Playbin::MediaFileType gstreamer::Playbin::media_file_type() const |
591 | +{ |
592 | + return file_type; |
593 | +} |
594 | |
595 | === modified file 'src/core/media/gstreamer/playbin.h' |
596 | --- src/core/media/gstreamer/playbin.h 2015-03-12 11:42:10 +0000 |
597 | +++ src/core/media/gstreamer/playbin.h 2015-03-12 11:42:10 +0000 |
598 | @@ -19,12 +19,11 @@ |
599 | #ifndef GSTREAMER_PLAYBIN_H_ |
600 | #define GSTREAMER_PLAYBIN_H_ |
601 | |
602 | +#include <core/media/player.h> |
603 | + |
604 | #include "bus.h" |
605 | #include "../mpris/player.h" |
606 | |
607 | -#include <hybris/media/surface_texture_client_hybris.h> |
608 | -#include <hybris/media/media_codec_layer.h> |
609 | - |
610 | #include <gio/gio.h> |
611 | #include <gst/gst.h> |
612 | |
613 | @@ -37,8 +36,6 @@ |
614 | // other image format, use: dot pipeline.dot -Tpng -o pipeline.png |
615 | //#define DEBUG_GST_PIPELINE |
616 | |
617 | -namespace media = core::ubuntu::media; |
618 | - |
619 | namespace gstreamer |
620 | { |
621 | struct Playbin |
622 | @@ -57,508 +54,66 @@ |
623 | MEDIA_FILE_TYPE_VIDEO |
624 | }; |
625 | |
626 | - static const std::string& pipeline_name() |
627 | - { |
628 | - static const std::string s{"playbin"}; |
629 | - return s; |
630 | - } |
631 | - |
632 | - static void about_to_finish(GstElement*, |
633 | - gpointer user_data) |
634 | - { |
635 | - auto thiz = static_cast<Playbin*>(user_data); |
636 | - thiz->signals.about_to_finish(); |
637 | - } |
638 | + static std::string get_audio_role_str(core::ubuntu::media::Player::AudioStreamRole audio_role); |
639 | + |
640 | + static const std::string& pipeline_name(); |
641 | + |
642 | + static void about_to_finish(GstElement*, gpointer user_data); |
643 | |
644 | static void source_setup(GstElement*, |
645 | GstElement *source, |
646 | - gpointer user_data) |
647 | - { |
648 | - if (user_data == nullptr) |
649 | - return; |
650 | - |
651 | - static_cast<Playbin*>(user_data)->setup_source(source); |
652 | - } |
653 | - |
654 | - Playbin() |
655 | - : pipeline(gst_element_factory_make("playbin", pipeline_name().c_str())), |
656 | - bus{gst_element_get_bus(pipeline)}, |
657 | - file_type(MEDIA_FILE_TYPE_NONE), |
658 | - video_sink(nullptr), |
659 | - on_new_message_connection( |
660 | - bus.on_new_message.connect( |
661 | - std::bind( |
662 | - &Playbin::on_new_message, |
663 | - this, |
664 | - std::placeholders::_1))), |
665 | - is_seeking(false), |
666 | - player_lifetime(media::Player::Lifetime::normal) |
667 | - { |
668 | - if (!pipeline) |
669 | - throw std::runtime_error("Could not create pipeline for playbin."); |
670 | - |
671 | - // Add audio and/or video sink elements depending on environment variables |
672 | - // being set or not set |
673 | - setup_pipeline_for_audio_video(); |
674 | - |
675 | - g_signal_connect( |
676 | - pipeline, |
677 | - "about-to-finish", |
678 | - G_CALLBACK(about_to_finish), |
679 | - this |
680 | - ); |
681 | - |
682 | - g_signal_connect( |
683 | - pipeline, |
684 | - "source-setup", |
685 | - G_CALLBACK(source_setup), |
686 | - this |
687 | - ); |
688 | - |
689 | - } |
690 | - |
691 | - ~Playbin() |
692 | - { |
693 | - if (pipeline) |
694 | - gst_object_unref(pipeline); |
695 | - } |
696 | - |
697 | - void reset() |
698 | - { |
699 | - std::cout << "Client died, resetting pipeline" << std::endl; |
700 | - // When the client dies, tear down the current pipeline and get it |
701 | - // in a state that is ready for the next client that connects to the |
702 | - // service |
703 | - |
704 | - // Don't reset the pipeline if we want to resume |
705 | - if (player_lifetime != media::Player::Lifetime::resumable) { |
706 | - reset_pipeline(); |
707 | - } |
708 | - |
709 | - // Signal to the Player class that the client side has disconnected |
710 | - signals.client_disconnected(); |
711 | - } |
712 | - |
713 | - void reset_pipeline() |
714 | - { |
715 | - std::cout << __PRETTY_FUNCTION__ << std::endl; |
716 | - auto ret = gst_element_set_state(pipeline, GST_STATE_NULL); |
717 | - switch(ret) |
718 | - { |
719 | - case GST_STATE_CHANGE_FAILURE: |
720 | - std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl; |
721 | - break; |
722 | - case GST_STATE_CHANGE_NO_PREROLL: |
723 | - case GST_STATE_CHANGE_SUCCESS: |
724 | - case GST_STATE_CHANGE_ASYNC: |
725 | - break; |
726 | - default: |
727 | - std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl; |
728 | - } |
729 | - file_type = MEDIA_FILE_TYPE_NONE; |
730 | - } |
731 | - |
732 | - void on_new_message(const Bus::Message& message) |
733 | - { |
734 | - switch(message.type) |
735 | - { |
736 | - case GST_MESSAGE_ERROR: |
737 | - signals.on_error(message.detail.error_warning_info); |
738 | - break; |
739 | - case GST_MESSAGE_WARNING: |
740 | - signals.on_warning(message.detail.error_warning_info); |
741 | - break; |
742 | - case GST_MESSAGE_INFO: |
743 | - signals.on_info(message.detail.error_warning_info); |
744 | - break; |
745 | - case GST_MESSAGE_TAG: |
746 | - { |
747 | - gchar *orientation; |
748 | - if (gst_tag_list_get_string(message.detail.tag.tag_list, "image-orientation", &orientation)) |
749 | - { |
750 | - // If the image-orientation tag is in the GstTagList, signal the Engine |
751 | - signals.on_orientation_changed(orientation_lut(orientation)); |
752 | - g_free (orientation); |
753 | - } |
754 | - |
755 | - signals.on_tag_available(message.detail.tag); |
756 | - } |
757 | - break; |
758 | - case GST_MESSAGE_STATE_CHANGED: |
759 | - signals.on_state_changed(message.detail.state_changed); |
760 | - break; |
761 | - case GST_MESSAGE_ASYNC_DONE: |
762 | - if (is_seeking) |
763 | - { |
764 | - // FIXME: Pass the actual playback time position to the signal call |
765 | - signals.on_seeked_to(0); |
766 | - is_seeking = false; |
767 | - } |
768 | - break; |
769 | - case GST_MESSAGE_EOS: |
770 | - signals.on_end_of_stream(); |
771 | - default: |
772 | - break; |
773 | - } |
774 | - } |
775 | - |
776 | - gstreamer::Bus& message_bus() |
777 | - { |
778 | - return bus; |
779 | - } |
780 | - |
781 | - void setup_pipeline_for_audio_video() |
782 | - { |
783 | - gint flags; |
784 | - g_object_get (pipeline, "flags", &flags, nullptr); |
785 | - flags |= GST_PLAY_FLAG_AUDIO; |
786 | - flags |= GST_PLAY_FLAG_VIDEO; |
787 | - flags &= ~GST_PLAY_FLAG_TEXT; |
788 | - g_object_set (pipeline, "flags", flags, nullptr); |
789 | - |
790 | - if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") != nullptr) |
791 | - { |
792 | - auto audio_sink = gst_element_factory_make ( |
793 | - ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"), |
794 | - "audio-sink"); |
795 | - |
796 | - std::cout << "audio_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") << std::endl; |
797 | - |
798 | - g_object_set ( |
799 | - pipeline, |
800 | - "audio-sink", |
801 | - audio_sink, |
802 | - NULL); |
803 | - } |
804 | - |
805 | - if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr) |
806 | - { |
807 | - video_sink = gst_element_factory_make ( |
808 | - ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), |
809 | - "video-sink"); |
810 | - |
811 | - std::cout << "video_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") << std::endl; |
812 | - |
813 | - g_object_set ( |
814 | - pipeline, |
815 | - "video-sink", |
816 | - video_sink, |
817 | - NULL); |
818 | - } |
819 | - } |
820 | - |
821 | - void create_video_sink(uint32_t texture_id) |
822 | - { |
823 | - std::cout << "Creating video sink for texture_id: " << texture_id << std::endl; |
824 | - |
825 | - if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr) |
826 | - { |
827 | - g_object_get (pipeline, "video_sink", &video_sink, NULL); |
828 | - |
829 | - // Get the service-side BufferQueue (IGraphicBufferProducer) and associate it with |
830 | - // the SurfaceTextureClientHybris instance |
831 | - IGBPWrapperHybris igbp = decoding_service_get_igraphicbufferproducer(); |
832 | - SurfaceTextureClientHybris stc = surface_texture_client_create_by_igbp(igbp); |
833 | - // Because mirsink is being loaded, we are definitely doing * hardware rendering. |
834 | - surface_texture_client_set_hardware_rendering (stc, TRUE); |
835 | - g_object_set (G_OBJECT (video_sink), "surface", static_cast<gpointer>(stc), static_cast<char*>(NULL)); |
836 | - } |
837 | - } |
838 | - |
839 | - void set_volume(double new_volume) |
840 | - { |
841 | - g_object_set (pipeline, "volume", new_volume, NULL); |
842 | - } |
843 | - |
844 | - /** Translate the AudioStreamRole enum into a string */ |
845 | - static std::string get_audio_role_str(media::Player::AudioStreamRole audio_role) |
846 | - { |
847 | - switch (audio_role) |
848 | - { |
849 | - case media::Player::AudioStreamRole::alarm: |
850 | - return "alarm"; |
851 | - break; |
852 | - case media::Player::AudioStreamRole::alert: |
853 | - return "alert"; |
854 | - break; |
855 | - case media::Player::AudioStreamRole::multimedia: |
856 | - return "multimedia"; |
857 | - break; |
858 | - case media::Player::AudioStreamRole::phone: |
859 | - return "phone"; |
860 | - break; |
861 | - default: |
862 | - return "multimedia"; |
863 | - break; |
864 | - } |
865 | - } |
866 | - |
867 | - media::Player::Orientation orientation_lut(const gchar *orientation) |
868 | - { |
869 | - if (g_strcmp0(orientation, "rotate-0") == 0) |
870 | - return media::Player::Orientation::rotate0; |
871 | - else if (g_strcmp0(orientation, "rotate-90") == 0) |
872 | - return media::Player::Orientation::rotate90; |
873 | - else if (g_strcmp0(orientation, "rotate-180") == 0) |
874 | - return media::Player::Orientation::rotate180; |
875 | - else if (g_strcmp0(orientation, "rotate-270") == 0) |
876 | - return media::Player::Orientation::rotate270; |
877 | - else |
878 | - return media::Player::Orientation::rotate0; |
879 | - } |
880 | + gpointer user_data); |
881 | + |
882 | + Playbin(); |
883 | + ~Playbin(); |
884 | + |
885 | + void reset(); |
886 | + void reset_pipeline(); |
887 | + |
888 | + void on_new_message(const Bus::Message& message); |
889 | + |
890 | + gstreamer::Bus& message_bus(); |
891 | + |
892 | + void setup_pipeline_for_audio_video(); |
893 | + |
894 | + void create_video_sink(uint32_t texture_id); |
895 | + |
896 | + void set_volume(double new_volume); |
897 | + |
898 | + void set_lifetime(core::ubuntu::media::Player::Lifetime); |
899 | + core::ubuntu::media::Player::Orientation orientation_lut(const gchar *orientation); |
900 | |
901 | /** Sets the new audio stream role on the pulsesink in playbin */ |
902 | - void set_audio_stream_role(media::Player::AudioStreamRole new_audio_role) |
903 | - { |
904 | - GstElement *audio_sink = NULL; |
905 | - g_object_get (pipeline, "audio-sink", &audio_sink, NULL); |
906 | - |
907 | - std::string role_str("props,media.role=" + get_audio_role_str(new_audio_role)); |
908 | - std::cout << "Audio stream role: " << role_str << std::endl; |
909 | - |
910 | - GstStructure *props = gst_structure_from_string (role_str.c_str(), NULL); |
911 | - if (audio_sink != nullptr && props != nullptr) |
912 | - g_object_set (audio_sink, "stream-properties", props, NULL); |
913 | - else |
914 | - { |
915 | - std::cerr << |
916 | - "Warning: couldn't set audio stream role - couldn't get audio_sink from pipeline" << |
917 | - std::endl; |
918 | - } |
919 | - |
920 | - gst_structure_free (props); |
921 | - } |
922 | - |
923 | - void set_lifetime(media::Player::Lifetime lifetime) |
924 | - { |
925 | - player_lifetime = lifetime; |
926 | - } |
927 | - |
928 | - uint64_t position() const |
929 | - { |
930 | - int64_t pos = 0; |
931 | - gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos); |
932 | - |
933 | - // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly |
934 | - return static_cast<uint64_t>(pos); |
935 | - } |
936 | - |
937 | - uint64_t duration() const |
938 | - { |
939 | - int64_t dur = 0; |
940 | - gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur); |
941 | - |
942 | - // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly |
943 | - return static_cast<uint64_t>(dur); |
944 | - } |
945 | - |
946 | - void set_uri(const std::string& uri, |
947 | - const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType()) |
948 | - { |
949 | - reset_pipeline(); |
950 | - |
951 | - g_object_set(pipeline, "uri", uri.c_str(), NULL); |
952 | - if (is_video_file(uri)) |
953 | - file_type = MEDIA_FILE_TYPE_VIDEO; |
954 | - else if (is_audio_file(uri)) |
955 | - file_type = MEDIA_FILE_TYPE_AUDIO; |
956 | - |
957 | - request_headers = headers; |
958 | - } |
959 | - |
960 | - void setup_source(GstElement *source) |
961 | - { |
962 | - if (source == NULL || request_headers.empty()) |
963 | - return; |
964 | - |
965 | - if (request_headers.find("Cookie") != request_headers.end()) { |
966 | - if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), |
967 | - "cookies") != NULL) { |
968 | - gchar ** cookies = g_strsplit(request_headers["Cookie"].c_str(), ";", 0); |
969 | - g_object_set(source, "cookies", cookies, NULL); |
970 | - g_strfreev(cookies); |
971 | - } |
972 | - } |
973 | - |
974 | - if (request_headers.find("User-Agent") != request_headers.end()) { |
975 | - if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), |
976 | - "user-agent") != NULL) { |
977 | - g_object_set(source, "user-agent", request_headers["User-Agent"].c_str(), NULL); |
978 | - } |
979 | - } |
980 | - } |
981 | - |
982 | - std::string uri() const |
983 | - { |
984 | - gchar* data = nullptr; |
985 | - g_object_get(pipeline, "current-uri", &data, nullptr); |
986 | - |
987 | - std::string result((data == nullptr ? "" : data)); |
988 | - g_free(data); |
989 | - |
990 | - return result; |
991 | - } |
992 | - |
993 | - bool set_state_and_wait(GstState new_state) |
994 | - { |
995 | - static const std::chrono::nanoseconds state_change_timeout |
996 | - { |
997 | - // We choose a quite high value here as tests are run under valgrind |
998 | - // and gstreamer pipeline setup/state changes take longer in that scenario. |
999 | - // The value does not negatively impact runtime performance. |
1000 | - std::chrono::milliseconds{5000} |
1001 | - }; |
1002 | - |
1003 | - auto ret = gst_element_set_state(pipeline, new_state); |
1004 | - bool result = false; GstState current, pending; |
1005 | - switch(ret) |
1006 | - { |
1007 | - case GST_STATE_CHANGE_FAILURE: |
1008 | - result = false; break; |
1009 | - case GST_STATE_CHANGE_NO_PREROLL: |
1010 | - case GST_STATE_CHANGE_SUCCESS: |
1011 | - result = true; break; |
1012 | - case GST_STATE_CHANGE_ASYNC: |
1013 | - result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state( |
1014 | - pipeline, |
1015 | - ¤t, |
1016 | - &pending, |
1017 | - state_change_timeout.count()); |
1018 | - break; |
1019 | - } |
1020 | - |
1021 | - // The state change has to have been successful to make |
1022 | - // sure that we indeed reached the requested new state. |
1023 | - if (result && new_state == GST_STATE_PLAYING) |
1024 | - { |
1025 | - // Get the video height/width from the video sink |
1026 | - if (has_video_sink_with_height_and_width()) |
1027 | - signals.on_video_dimensions_changed(get_video_dimensions()); |
1028 | -#ifdef DEBUG_GST_PIPELINE |
1029 | - std::cout << "Dumping pipeline dot file" << std::endl; |
1030 | - GST_DEBUG_BIN_TO_DOT_FILE((GstBin*)pipeline, GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); |
1031 | -#endif |
1032 | - } |
1033 | - |
1034 | - return result; |
1035 | - } |
1036 | - |
1037 | - bool seek(const std::chrono::microseconds& ms) |
1038 | - { |
1039 | - is_seeking = true; |
1040 | - return gst_element_seek_simple( |
1041 | - pipeline, |
1042 | - GST_FORMAT_TIME, |
1043 | - (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), |
1044 | - ms.count() * 1000); |
1045 | - } |
1046 | - |
1047 | - bool has_video_sink_with_height_and_width() |
1048 | - { |
1049 | - return video_sink != nullptr && g_strcmp0(::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), "mirsink") == 0; |
1050 | - } |
1051 | - |
1052 | - core::ubuntu::media::video::Dimensions get_video_dimensions() |
1053 | - { |
1054 | - if (not has_video_sink_with_height_and_width()) |
1055 | - throw std::runtime_error{"Could not get the height/width of each video frame"}; |
1056 | - |
1057 | - uint32_t video_height = 0, video_width = 0; |
1058 | - g_object_get (video_sink, "height", &video_height, nullptr); |
1059 | - g_object_get (video_sink, "width", &video_width, nullptr); |
1060 | - std::cout << "video_height: " << video_height << ", video_width: " << video_width << std::endl; |
1061 | - |
1062 | - return core::ubuntu::media::video::Dimensions |
1063 | - { |
1064 | - core::ubuntu::media::video::Height{video_height}, |
1065 | - core::ubuntu::media::video::Width{video_width} |
1066 | - }; |
1067 | - } |
1068 | - |
1069 | - std::string get_file_content_type(const std::string& uri) const |
1070 | - { |
1071 | - if (uri.empty()) |
1072 | - return std::string(); |
1073 | - |
1074 | - std::string filename(uri); |
1075 | - std::cout << "filename: " << filename << std::endl; |
1076 | - size_t pos = uri.find("file://"); |
1077 | - if (pos != std::string::npos) |
1078 | - filename = uri.substr(pos + 7, std::string::npos); |
1079 | - else |
1080 | - // Anything other than a file, for now claim that the type |
1081 | - // is both audio and video. |
1082 | - // FIXME: implement true net stream sampling and get the type from GstCaps |
1083 | - return std::string("audio/video/"); |
1084 | - |
1085 | - |
1086 | - GError *error = nullptr; |
1087 | - std::unique_ptr<GFile, void(*)(void *)> file( |
1088 | - g_file_new_for_path(filename.c_str()), g_object_unref); |
1089 | - std::unique_ptr<GFileInfo, void(*)(void *)> info( |
1090 | - g_file_query_info( |
1091 | - file.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE "," |
1092 | - G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_QUERY_INFO_NONE, |
1093 | - /* cancellable */ NULL, &error), |
1094 | - g_object_unref); |
1095 | - if (!info) |
1096 | - { |
1097 | - std::string error_str(error->message); |
1098 | - g_error_free(error); |
1099 | - |
1100 | - std::cout << "Failed to query the URI for the presence of video content: " |
1101 | - << error_str << std::endl; |
1102 | - return std::string(); |
1103 | - } |
1104 | - |
1105 | - std::string content_type(g_file_info_get_attribute_string( |
1106 | - info.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE)); |
1107 | - |
1108 | - return content_type; |
1109 | - } |
1110 | - |
1111 | - bool is_audio_file(const std::string& uri) const |
1112 | - { |
1113 | - if (uri.empty()) |
1114 | - return false; |
1115 | - |
1116 | - if (get_file_content_type(uri).find("audio/") == 0) |
1117 | - { |
1118 | - std::cout << "Found audio content" << std::endl; |
1119 | - return true; |
1120 | - } |
1121 | - |
1122 | - return false; |
1123 | - } |
1124 | - |
1125 | - bool is_video_file(const std::string& uri) const |
1126 | - { |
1127 | - if (uri.empty()) |
1128 | - return false; |
1129 | - |
1130 | - if (get_file_content_type(uri).find("video/") == 0) |
1131 | - { |
1132 | - std::cout << "Found video content" << std::endl; |
1133 | - return true; |
1134 | - } |
1135 | - |
1136 | - return false; |
1137 | - } |
1138 | - |
1139 | - MediaFileType media_file_type() const |
1140 | - { |
1141 | - return file_type; |
1142 | - } |
1143 | + void set_audio_stream_role(core::ubuntu::media::Player::AudioStreamRole new_audio_role); |
1144 | + |
1145 | + uint64_t position() const; |
1146 | + uint64_t duration() const; |
1147 | + |
1148 | + void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers); |
1149 | + std::string uri() const; |
1150 | + |
1151 | + void setup_source(GstElement *source); |
1152 | + |
1153 | + bool set_state_and_wait(GstState new_state); |
1154 | + bool seek(const std::chrono::microseconds& ms); |
1155 | + |
1156 | + core::ubuntu::media::video::Dimensions get_video_dimensions() const; |
1157 | + |
1158 | + std::string get_file_content_type(const std::string& uri) const; |
1159 | + |
1160 | + bool is_audio_file(const std::string& uri) const; |
1161 | + bool is_video_file(const std::string& uri) const; |
1162 | + |
1163 | + MediaFileType media_file_type() const; |
1164 | |
1165 | GstElement* pipeline; |
1166 | gstreamer::Bus bus; |
1167 | MediaFileType file_type; |
1168 | - SurfaceTextureClientHybris stc_hybris; |
1169 | GstElement* video_sink; |
1170 | core::Connection on_new_message_connection; |
1171 | bool is_seeking; |
1172 | core::ubuntu::media::Player::HeadersType request_headers; |
1173 | - media::Player::Lifetime player_lifetime; |
1174 | + core::ubuntu::media::Player::Lifetime player_lifetime; |
1175 | struct |
1176 | { |
1177 | core::Signal<void> about_to_finish; |
FAILED: Continuous integration, rev:103 jenkins. qa.ubuntu. com/job/ media-hub- ci/180/ jenkins. qa.ubuntu. com/job/ media-hub- vivid-amd64- ci/20/console jenkins. qa.ubuntu. com/job/ media-hub- vivid-armhf- ci/20/console jenkins. qa.ubuntu. com/job/ media-hub- vivid-i386- ci/20/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 180/rebuild
http://