Merge lp:~phablet-team/media-hub/fix-1458040 into lp:media-hub

Proposed by Jim Hodapp
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
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.

To post a comment you must log in.
Revision history for this message
Nick Dedekind (nick-dedekind) 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?

Revision history for this message
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.

Revision history for this message
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://doc.qt.io/qt-5/qmediaplayer.html#Error-enum

144. By Jim Hodapp

Log any playbin errors to stderr

145. By Jim Hodapp

Merged with trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

Looks good and does as said on tin.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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));

Subscribers

People subscribed via source and target branches

to all changes: