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

Proposed by Jim Hodapp
Status: Merged
Approved by: Scott Sweeny
Approved revision: 196
Merged at revision: 198
Proposed branch: lp:~phablet-team/media-hub/fix-1596329
Merge into: lp:media-hub
Diff against target: 211 lines (+76/-30)
7 files modified
src/core/media/engine.h (+1/-1)
src/core/media/gstreamer/engine.cpp (+2/-2)
src/core/media/gstreamer/engine.h (+2/-1)
src/core/media/gstreamer/playbin.cpp (+49/-19)
src/core/media/gstreamer/playbin.h (+7/-3)
src/core/media/metadata.cpp (+13/-3)
src/core/media/player_implementation.cpp (+2/-1)
To merge this branch: bzr merge lp:~phablet-team/media-hub/fix-1596329
Reviewer Review Type Date Requested Status
Scott Sweeny (community) Approve
PS Jenkins bot continuous-integration Pending
Review via email: mp+298829@code.launchpad.net

Commit message

To avoid deadlocking, make sure that we don't ever change the pipeline's state on the streaming thread. It will always be set from the main thread context thanks to g_idle_add(). The deadlock that this fixes was when calling stop() in player_implementation.cpp in the on_end_of_tracklist() handler.

Description of the change

To avoid deadlocking, make sure that we don't ever change the pipeline's state on the streaming thread. It will always be set from the main thread context thanks to g_idle_add(). The deadlock that this fixes was when calling stop() in player_implementation.cpp in the on_end_of_tracklist() handler.

To post a comment you must log in.
Revision history for this message
Scott Sweeny (ssweeny) wrote :

LGTM

review: Approve
197. By Jim Hodapp

Disable specific fragile unit test. Many of these tests need rewriting.

198. By Jim Hodapp

Only use the main thread context set_state_and_wait for stop(). Also re-enable the disabled unit test.

199. By Jim Hodapp

Further refine the fix so that the unit tests pass and we still don't get a deadlock

200. By Jim Hodapp

Don't break tracklist support

201. By Jim Hodapp

Patch up a memory leak

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/core/media/engine.h'
2--- src/core/media/engine.h 2015-04-03 22:52:08 +0000
3+++ src/core/media/engine.h 2016-07-04 15:03:57 +0000
4@@ -83,7 +83,7 @@
5 virtual void create_video_sink(uint32_t texture_id) = 0;
6
7 virtual bool play() = 0;
8- virtual bool stop() = 0;
9+ virtual bool stop(bool use_main_context = false) = 0;
10 virtual bool pause() = 0;
11 virtual bool seek_to(const std::chrono::microseconds& ts) = 0;
12
13
14=== modified file 'src/core/media/gstreamer/engine.cpp'
15--- src/core/media/gstreamer/engine.cpp 2016-06-08 16:54:31 +0000
16+++ src/core/media/gstreamer/engine.cpp 2016-07-04 15:03:57 +0000
17@@ -446,7 +446,7 @@
18 return result;
19 }
20
21-bool gstreamer::Engine::stop()
22+bool gstreamer::Engine::stop(bool use_main_thread)
23 {
24 // No need to wait, and we can immediately return.
25 if (d->state == media::Engine::State::stopped)
26@@ -455,7 +455,7 @@
27 return true;
28 }
29
30- const auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
31+ const auto result = d->playbin.set_state_and_wait(GST_STATE_NULL, use_main_thread);
32 if (result)
33 {
34 d->state = media::Engine::State::stopped;
35
36=== modified file 'src/core/media/gstreamer/engine.h'
37--- src/core/media/gstreamer/engine.h 2015-04-03 22:52:08 +0000
38+++ src/core/media/gstreamer/engine.h 2016-07-04 15:03:57 +0000
39@@ -38,7 +38,8 @@
40 void create_video_sink(uint32_t texture_id);
41
42 bool play();
43- bool stop();
44+ // use_main_thread will set the pipeline's new_state in the main thread context
45+ bool stop(bool use_main_thread = false);
46 bool pause();
47 bool seek_to(const std::chrono::microseconds& ts);
48
49
50=== modified file 'src/core/media/gstreamer/playbin.cpp'
51--- src/core/media/gstreamer/playbin.cpp 2016-06-16 22:42:30 +0000
52+++ src/core/media/gstreamer/playbin.cpp 2016-07-04 15:03:57 +0000
53@@ -121,7 +121,8 @@
54 is_missing_audio_codec(false),
55 is_missing_video_codec(false),
56 audio_stream_id(-1),
57- video_stream_id(-1)
58+ video_stream_id(-1),
59+ current_new_state(GST_STATE_NULL)
60 {
61 if (!pipeline)
62 throw std::runtime_error("Could not create pipeline for playbin.");
63@@ -524,7 +525,17 @@
64 return result;
65 }
66
67-bool gstreamer::Playbin::set_state_and_wait(GstState new_state)
68+gboolean gstreamer::Playbin::set_state_in_main_thread(gpointer user_data)
69+{
70+ MH_TRACE("");
71+ auto thiz = static_cast<Playbin*>(user_data);
72+ if (thiz and thiz->pipeline)
73+ gst_element_set_state(thiz->pipeline, thiz->current_new_state);
74+
75+ // Always return false so this is a single shot function call
76+ return false;
77+}
78+bool gstreamer::Playbin::set_state_and_wait(GstState new_state, bool use_main_thread)
79 {
80 static const std::chrono::nanoseconds state_change_timeout
81 {
82@@ -534,25 +545,44 @@
83 std::chrono::milliseconds{5000}
84 };
85
86- auto ret = gst_element_set_state(pipeline, new_state);
87-
88- MH_DEBUG("Requested state change.");
89-
90- bool result = false; GstState current, pending;
91- switch(ret)
92+ bool result = false;
93+ GstState current, pending;
94+ if (use_main_thread)
95 {
96- case GST_STATE_CHANGE_FAILURE:
97- result = false; break;
98- case GST_STATE_CHANGE_NO_PREROLL:
99- case GST_STATE_CHANGE_SUCCESS:
100- result = true; break;
101- case GST_STATE_CHANGE_ASYNC:
102+ // Cache this value for the static g_idle_add handler function
103+ current_new_state = new_state;
104+ g_idle_add((GSourceFunc) gstreamer::Playbin::set_state_in_main_thread, (gpointer) this);
105+
106+ MH_DEBUG("Requested state change in main thread context.");
107+
108+ GstState current, pending;
109 result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
110- pipeline,
111- &current,
112- &pending,
113- state_change_timeout.count());
114- break;
115+ pipeline,
116+ &current,
117+ &pending,
118+ state_change_timeout.count());
119+ }
120+ else
121+ {
122+ const auto ret = gst_element_set_state(pipeline, new_state);
123+
124+ MH_DEBUG("Requested state change not using main thread context.");
125+
126+ switch (ret)
127+ {
128+ case GST_STATE_CHANGE_FAILURE:
129+ result = false; break;
130+ case GST_STATE_CHANGE_NO_PREROLL:
131+ case GST_STATE_CHANGE_SUCCESS:
132+ result = true; break;
133+ case GST_STATE_CHANGE_ASYNC:
134+ result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
135+ pipeline,
136+ &current,
137+ &pending,
138+ state_change_timeout.count());
139+ break;
140+ }
141 }
142
143 // We only should query the pipeline if we actually succeeded in
144
145=== modified file 'src/core/media/gstreamer/playbin.h'
146--- src/core/media/gstreamer/playbin.h 2016-02-19 16:14:42 +0000
147+++ src/core/media/gstreamer/playbin.h 2016-07-04 15:03:57 +0000
148@@ -96,9 +96,12 @@
149
150 void setup_source(GstElement *source);
151
152- // Sets the pipeline's state (stopped, playing, paused, etc). Optional parameter makes this call
153- // in the main_loop context.
154- bool set_state_and_wait(GstState new_state);
155+ // Sets the pipeline state in the main thread context instead of the possibility of creating
156+ // a deadlock in the streaming thread
157+ static gboolean set_state_in_main_thread(gpointer user_data);
158+ // Sets the pipeline's state (stopped, playing, paused, etc). use_main_thread will set the
159+ // pipeline's new_state in the main thread context.
160+ bool set_state_and_wait(GstState new_state, bool use_main_thread = false);
161 bool seek(const std::chrono::microseconds& ms);
162
163 core::ubuntu::media::video::Dimensions get_video_dimensions() const;
164@@ -148,6 +151,7 @@
165 bool is_missing_video_codec;
166 gint audio_stream_id;
167 gint video_stream_id;
168+ GstState current_new_state;
169 };
170 }
171
172
173=== modified file 'src/core/media/metadata.cpp'
174--- src/core/media/metadata.cpp 2016-06-15 17:49:49 +0000
175+++ src/core/media/metadata.cpp 2016-07-04 15:03:57 +0000
176@@ -29,9 +29,19 @@
177 if (not is_set(key))
178 return std::string{};
179
180- return std::string{g_uri_escape_string(map.at(key).c_str(),
181- "!$&'()*+,;=:/?[]@", // Reserved chars
182- true)}; // Allow UTF-8 chars
183+ char* escaped {g_uri_escape_string(map.at(key).c_str(),
184+ "!$&'()*+,;=:/?[]@", // Reserved chars
185+ true)};
186+ if (!escaped)
187+ {
188+ return std::string{};
189+ }
190+ else
191+ {
192+ std::string s{escaped};
193+ g_free(escaped);
194+ return s;
195+ }
196 }
197
198 const std::string& media::Track::MetaData::album() const
199
200=== modified file 'src/core/media/player_implementation.cpp'
201--- src/core/media/player_implementation.cpp 2016-06-23 00:17:16 +0000
202+++ src/core/media/player_implementation.cpp 2016-07-04 15:03:57 +0000
203@@ -665,7 +665,8 @@
204 && d->engine->state() != gstreamer::Engine::State::stopped)
205 {
206 MH_INFO("End of tracklist reached, stopping playback");
207- d->engine->stop();
208+ const constexpr bool use_main_thread = true;
209+ d->engine->stop(use_main_thread);
210 }
211 });
212

Subscribers

People subscribed via source and target branches

to all changes: