Merge lp:~phablet-team/media-hub/fix-1458040 into lp:media-hub
- fix-1458040
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Nick Dedekind | ||||
Approved revision: | 147 | ||||
Merged at revision: | 145 | ||||
Proposed branch: | lp:~phablet-team/media-hub/fix-1458040 | ||||
Merge into: | lp:media-hub | ||||
Prerequisite: | lp:~phablet-team/media-hub/fix-1457129 | ||||
Diff against target: |
318 lines (+121/-36) 5 files modified
src/core/media/gstreamer/bus.h (+41/-10) src/core/media/gstreamer/engine.cpp (+56/-12) src/core/media/gstreamer/playbin.cpp (+15/-12) src/core/media/gstreamer/playbin.h (+4/-1) tests/unit-tests/test-gstreamer-engine.cpp (+5/-1) |
||||
To merge this branch: | bzr merge lp:~phablet-team/media-hub/fix-1458040 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nick Dedekind (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+260507@code.launchpad.net |
Commit message
Handle a wider array of GStreamer errors so that .ogv files are reported as failing to play.
Description of the change
Handle a wider array of GStreamer errors so that .ogv files are reported as failing to play.
Nick Dedekind (nick-dedekind) wrote : | # |
Nick Dedekind (nick-dedekind) wrote : | # |
Still seems to be stalling in the dash usage, but not in the mediaplayer-app. Not sure why though; checking the differences but either way it shouldn't be happening.
Jim Hodapp (jhodapp) wrote : | # |
> Feels a bit strange that we return "no_error" if there is an error. Should we
> not add a "unknown_error" to the Player error api?
> This seems likely to be the reason why this failed in the first place?
My limitation is the types of errors supported by QMediaPlayer: http://
- 144. By Jim Hodapp
-
Log any playbin errors to stderr
- 145. By Jim Hodapp
-
Merged with trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:145
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 146. By Jim Hodapp
-
Don't use a sync_handler for GST_MESSAGE handling. Use an async one instead so that we aren't on the streaming thread's context. This prevents the pipeline from deadlocking after an error is reached.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:146
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 147. By Jim Hodapp
-
Use both the sync_handler and async bus watch to handle GST_MESSAGES, but only using the sync one where absolutely necessary since it can cause deadlocks when used in general.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:147
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : | # |
Looks good and does as said on tin.
Preview Diff
1 | === modified file 'src/core/media/gstreamer/bus.h' |
2 | --- src/core/media/gstreamer/bus.h 2014-10-23 14:42:59 +0000 |
3 | +++ src/core/media/gstreamer/bus.h 2015-06-01 21:35:00 +0000 |
4 | @@ -277,12 +277,38 @@ |
5 | |
6 | auto thiz = static_cast<Bus*>(data); |
7 | Message message(msg); |
8 | - thiz->on_new_message(message); |
9 | - |
10 | - return GST_BUS_DROP; |
11 | - } |
12 | - |
13 | - Bus(GstBus* bus) : bus(bus) |
14 | + if (message.type == GST_MESSAGE_TAG || message.type == GST_MESSAGE_ASYNC_DONE) |
15 | + thiz->on_new_message(message); |
16 | + |
17 | + return GST_BUS_PASS; |
18 | + } |
19 | + |
20 | + static gboolean bus_watch_handler( |
21 | + GstBus* bus, |
22 | + GstMessage* msg, |
23 | + gpointer data) |
24 | + { |
25 | + (void) bus; |
26 | + |
27 | + auto thiz = static_cast<Bus*>(data); |
28 | + Message message(msg); |
29 | + thiz->on_new_message_async(message); |
30 | + |
31 | + return true; |
32 | + } |
33 | + |
34 | + Bus(GstBus* bus) : bus(bus), bus_watch_id(0) |
35 | + { |
36 | + set_bus(bus); |
37 | + } |
38 | + |
39 | + ~Bus() |
40 | + { |
41 | + g_source_remove(bus_watch_id); |
42 | + gst_object_unref(bus); |
43 | + } |
44 | + |
45 | + void set_bus(GstBus* bus) |
46 | { |
47 | if (!bus) |
48 | throw std::runtime_error("Cannot create Bus instance if underlying instance is NULL."); |
49 | @@ -292,15 +318,20 @@ |
50 | Bus::sync_handler, |
51 | this, |
52 | nullptr); |
53 | - } |
54 | |
55 | - ~Bus() |
56 | - { |
57 | - gst_object_unref(bus); |
58 | + // Use a watch for most messages instead of the sync handler so that our context is not |
59 | + // the same as the streaming thread, which can cause deadlocks in GStreamer |
60 | + // if, for example, attempting to change the pipeline state from this context. |
61 | + bus_watch_id = gst_bus_add_watch( |
62 | + bus, |
63 | + Bus::bus_watch_handler, |
64 | + this); |
65 | } |
66 | |
67 | GstBus* bus; |
68 | core::Signal<Message> on_new_message; |
69 | + core::Signal<Message> on_new_message_async; |
70 | + guint bus_watch_id; |
71 | }; |
72 | } |
73 | |
74 | |
75 | === modified file 'src/core/media/gstreamer/engine.cpp' |
76 | --- src/core/media/gstreamer/engine.cpp 2015-05-01 19:44:11 +0000 |
77 | +++ src/core/media/gstreamer/engine.cpp 2015-06-01 21:35:00 +0000 |
78 | @@ -75,53 +75,97 @@ |
79 | // Converts from a GStreamer GError to a media::Player:Error enum |
80 | media::Player::Error from_gst_errorwarning(const gstreamer::Bus::Message::Detail::ErrorWarningInfo& ewi) |
81 | { |
82 | + media::Player::Error ret_error = media::Player::Error::no_error; |
83 | + |
84 | if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-core-error-quark") == 0) |
85 | { |
86 | switch (ewi.error->code) |
87 | { |
88 | + case GST_CORE_ERROR_FAILED: |
89 | + std::cerr << "** Encountered a GST_CORE_ERROR_FAILED" << std::endl; |
90 | + ret_error = media::Player::Error::resource_error; |
91 | + break; |
92 | case GST_CORE_ERROR_NEGOTIATION: |
93 | - return media::Player::Error::resource_error; |
94 | + std::cerr << "** Encountered a GST_CORE_ERROR_NEGOTIATION" << std::endl; |
95 | + ret_error = media::Player::Error::resource_error; |
96 | + break; |
97 | case GST_CORE_ERROR_MISSING_PLUGIN: |
98 | - return media::Player::Error::format_error; |
99 | + std::cerr << "** Encountered a GST_CORE_ERROR_MISSING_PLUGIN" << std::endl; |
100 | + ret_error = media::Player::Error::format_error; |
101 | + break; |
102 | default: |
103 | - std::cerr << "Got an unhandled core error: '" |
104 | + std::cerr << "** Encountered an unhandled core error: '" |
105 | << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl; |
106 | - return media::Player::Error::no_error; |
107 | + ret_error = media::Player::Error::no_error; |
108 | + break; |
109 | } |
110 | } |
111 | else if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-resource-error-quark") == 0) |
112 | { |
113 | switch (ewi.error->code) |
114 | { |
115 | + case GST_RESOURCE_ERROR_FAILED: |
116 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_FAILED" << std::endl; |
117 | + ret_error = media::Player::Error::resource_error; |
118 | + break; |
119 | case GST_RESOURCE_ERROR_NOT_FOUND: |
120 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_NOT_FOUND" << std::endl; |
121 | + ret_error = media::Player::Error::resource_error; |
122 | + break; |
123 | case GST_RESOURCE_ERROR_OPEN_READ: |
124 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_OPEN_READ" << std::endl; |
125 | + ret_error = media::Player::Error::resource_error; |
126 | + break; |
127 | case GST_RESOURCE_ERROR_OPEN_WRITE: |
128 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_OPEN_WRITE" << std::endl; |
129 | + ret_error = media::Player::Error::resource_error; |
130 | + break; |
131 | case GST_RESOURCE_ERROR_READ: |
132 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_READ" << std::endl; |
133 | + ret_error = media::Player::Error::resource_error; |
134 | + break; |
135 | case GST_RESOURCE_ERROR_WRITE: |
136 | - return media::Player::Error::resource_error; |
137 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_WRITE" << std::endl; |
138 | + ret_error = media::Player::Error::resource_error; |
139 | + break; |
140 | case GST_RESOURCE_ERROR_NOT_AUTHORIZED: |
141 | - return media::Player::Error::access_denied_error; |
142 | + std::cerr << "** Encountered a GST_RESOURCE_ERROR_NOT_AUTHORIZED" << std::endl; |
143 | + ret_error = media::Player::Error::access_denied_error; |
144 | + break; |
145 | default: |
146 | - std::cerr << "Got an unhandled resource error: '" |
147 | + std::cerr << "** Encountered an unhandled resource error: '" |
148 | << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl; |
149 | - return media::Player::Error::no_error; |
150 | + ret_error = media::Player::Error::no_error; |
151 | + break; |
152 | } |
153 | } |
154 | else if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-stream-error-quark") == 0) |
155 | { |
156 | switch (ewi.error->code) |
157 | { |
158 | + case GST_STREAM_ERROR_FAILED: |
159 | + std::cerr << "** Encountered a GST_STREAM_ERROR_FAILED" << std::endl; |
160 | + ret_error = media::Player::Error::resource_error; |
161 | + break; |
162 | case GST_STREAM_ERROR_CODEC_NOT_FOUND: |
163 | + std::cerr << "** Encountered a GST_STREAM_ERROR_CODEC_NOT_FOUND" << std::endl; |
164 | + ret_error = media::Player::Error::format_error; |
165 | + break; |
166 | case GST_STREAM_ERROR_DECODE: |
167 | - return media::Player::Error::format_error; |
168 | + std::cerr << "** Encountered a GST_STREAM_ERROR_DECODE" << std::endl; |
169 | + ret_error = media::Player::Error::format_error; |
170 | + break; |
171 | default: |
172 | - std::cerr << "Got an unhandled stream error: '" |
173 | + std::cerr << "** Encountered an unhandled stream error: '" |
174 | << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl; |
175 | - return media::Player::Error::no_error; |
176 | + ret_error = media::Player::Error::no_error; |
177 | + break; |
178 | } |
179 | } |
180 | |
181 | - return media::Player::Error::no_error; |
182 | + std::cout << "Resetting playbin pipeline after unrecoverable error" << std::endl; |
183 | + playbin.reset(); |
184 | + return ret_error; |
185 | } |
186 | |
187 | void on_playbin_error(const gstreamer::Bus::Message::Detail::ErrorWarningInfo& ewi) |
188 | |
189 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
190 | --- src/core/media/gstreamer/playbin.cpp 2015-05-01 19:44:11 +0000 |
191 | +++ src/core/media/gstreamer/playbin.cpp 2015-06-01 21:35:00 +0000 |
192 | @@ -92,34 +92,34 @@ |
193 | bus{gst_element_get_bus(pipeline)}, |
194 | file_type(MEDIA_FILE_TYPE_NONE), |
195 | video_sink(nullptr), |
196 | - on_new_message_connection( |
197 | - bus.on_new_message.connect( |
198 | + on_new_message_connection_async( |
199 | + bus.on_new_message_async.connect( |
200 | std::bind( |
201 | - &Playbin::on_new_message, |
202 | + &Playbin::on_new_message_async, |
203 | this, |
204 | std::placeholders::_1))), |
205 | is_seeking(false), |
206 | previous_position(0), |
207 | player_lifetime(media::Player::Lifetime::normal), |
208 | - is_eos(false) |
209 | + is_eos(false), |
210 | + about_to_finish_handler_id(0), |
211 | + source_setup_handler_id(0) |
212 | { |
213 | if (!pipeline) |
214 | throw std::runtime_error("Could not create pipeline for playbin."); |
215 | |
216 | - is_eos = false; |
217 | - |
218 | // Add audio and/or video sink elements depending on environment variables |
219 | // being set or not set |
220 | setup_pipeline_for_audio_video(); |
221 | |
222 | - g_signal_connect( |
223 | + about_to_finish_handler_id = g_signal_connect( |
224 | pipeline, |
225 | "about-to-finish", |
226 | G_CALLBACK(about_to_finish), |
227 | this |
228 | ); |
229 | |
230 | - g_signal_connect( |
231 | + source_setup_handler_id = g_signal_connect( |
232 | pipeline, |
233 | "source-setup", |
234 | G_CALLBACK(source_setup), |
235 | @@ -129,6 +129,9 @@ |
236 | |
237 | gstreamer::Playbin::~Playbin() |
238 | { |
239 | + g_signal_handler_disconnect(pipeline, about_to_finish_handler_id); |
240 | + g_signal_handler_disconnect(pipeline, source_setup_handler_id); |
241 | + |
242 | if (pipeline) |
243 | gst_object_unref(pipeline); |
244 | } |
245 | @@ -167,7 +170,7 @@ |
246 | file_type = MEDIA_FILE_TYPE_NONE; |
247 | } |
248 | |
249 | -void gstreamer::Playbin::on_new_message(const Bus::Message& message) |
250 | +void gstreamer::Playbin::on_new_message_async(const Bus::Message& message) |
251 | { |
252 | switch(message.type) |
253 | { |
254 | @@ -180,6 +183,9 @@ |
255 | case GST_MESSAGE_INFO: |
256 | signals.on_info(message.detail.error_warning_info); |
257 | break; |
258 | + case GST_MESSAGE_STATE_CHANGED: |
259 | + signals.on_state_changed(std::make_pair(message.detail.state_changed, message.source)); |
260 | + break; |
261 | case GST_MESSAGE_TAG: |
262 | { |
263 | gchar *orientation; |
264 | @@ -193,9 +199,6 @@ |
265 | signals.on_tag_available(message.detail.tag); |
266 | } |
267 | break; |
268 | - case GST_MESSAGE_STATE_CHANGED: |
269 | - signals.on_state_changed(std::make_pair(message.detail.state_changed, message.source)); |
270 | - break; |
271 | case GST_MESSAGE_ASYNC_DONE: |
272 | if (is_seeking) |
273 | { |
274 | |
275 | === modified file 'src/core/media/gstreamer/playbin.h' |
276 | --- src/core/media/gstreamer/playbin.h 2015-05-01 19:44:11 +0000 |
277 | +++ src/core/media/gstreamer/playbin.h 2015-06-01 21:35:00 +0000 |
278 | @@ -71,6 +71,7 @@ |
279 | void reset_pipeline(); |
280 | |
281 | void on_new_message(const Bus::Message& message); |
282 | + void on_new_message_async(const Bus::Message& message); |
283 | |
284 | gstreamer::Bus& message_bus(); |
285 | |
286 | @@ -112,12 +113,14 @@ |
287 | gstreamer::Bus bus; |
288 | MediaFileType file_type; |
289 | GstElement* video_sink; |
290 | - core::Connection on_new_message_connection; |
291 | + core::Connection on_new_message_connection_async; |
292 | bool is_seeking; |
293 | mutable uint64_t previous_position; |
294 | core::ubuntu::media::Player::HeadersType request_headers; |
295 | core::ubuntu::media::Player::Lifetime player_lifetime; |
296 | bool is_eos; |
297 | + gulong about_to_finish_handler_id; |
298 | + gulong source_setup_handler_id; |
299 | struct |
300 | { |
301 | core::Signal<void> about_to_finish; |
302 | |
303 | === modified file 'tests/unit-tests/test-gstreamer-engine.cpp' |
304 | --- tests/unit-tests/test-gstreamer-engine.cpp 2015-04-03 22:52:08 +0000 |
305 | +++ tests/unit-tests/test-gstreamer-engine.cpp 2015-06-01 21:35:00 +0000 |
306 | @@ -411,7 +411,11 @@ |
307 | ASSERT_TRUE(test::copy_test_media_file_to("test.mp3", test_file)); |
308 | |
309 | gstreamer::Engine engine; |
310 | - auto md = engine.meta_data_extractor()->meta_data_for_track_with_uri(test_file_uri); |
311 | + |
312 | + core::ubuntu::media::Track::MetaData md; |
313 | + ASSERT_NO_THROW({ |
314 | + md = engine.meta_data_extractor()->meta_data_for_track_with_uri(test_file_uri); |
315 | + }); |
316 | |
317 | if (0 < md.count(xesam::Album::name)) |
318 | EXPECT_EQ("Test", md.get(xesam::Album::name)); |
Feels a bit strange that we return "no_error" if there is an error. Should we not add a "unknown_error" to the Player error api?
This seems likely to be the reason why this failed in the first place?