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
=== modified file 'src/core/media/engine.h'
--- src/core/media/engine.h 2015-04-03 22:52:08 +0000
+++ src/core/media/engine.h 2016-07-04 15:03:57 +0000
@@ -83,7 +83,7 @@
83 virtual void create_video_sink(uint32_t texture_id) = 0;83 virtual void create_video_sink(uint32_t texture_id) = 0;
8484
85 virtual bool play() = 0;85 virtual bool play() = 0;
86 virtual bool stop() = 0;86 virtual bool stop(bool use_main_context = false) = 0;
87 virtual bool pause() = 0;87 virtual bool pause() = 0;
88 virtual bool seek_to(const std::chrono::microseconds& ts) = 0;88 virtual bool seek_to(const std::chrono::microseconds& ts) = 0;
8989
9090
=== modified file 'src/core/media/gstreamer/engine.cpp'
--- src/core/media/gstreamer/engine.cpp 2016-06-08 16:54:31 +0000
+++ src/core/media/gstreamer/engine.cpp 2016-07-04 15:03:57 +0000
@@ -446,7 +446,7 @@
446 return result;446 return result;
447}447}
448448
449bool gstreamer::Engine::stop()449bool gstreamer::Engine::stop(bool use_main_thread)
450{450{
451 // No need to wait, and we can immediately return.451 // No need to wait, and we can immediately return.
452 if (d->state == media::Engine::State::stopped)452 if (d->state == media::Engine::State::stopped)
@@ -455,7 +455,7 @@
455 return true;455 return true;
456 }456 }
457457
458 const auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);458 const auto result = d->playbin.set_state_and_wait(GST_STATE_NULL, use_main_thread);
459 if (result)459 if (result)
460 {460 {
461 d->state = media::Engine::State::stopped;461 d->state = media::Engine::State::stopped;
462462
=== modified file 'src/core/media/gstreamer/engine.h'
--- src/core/media/gstreamer/engine.h 2015-04-03 22:52:08 +0000
+++ src/core/media/gstreamer/engine.h 2016-07-04 15:03:57 +0000
@@ -38,7 +38,8 @@
38 void create_video_sink(uint32_t texture_id);38 void create_video_sink(uint32_t texture_id);
3939
40 bool play();40 bool play();
41 bool stop();41 // use_main_thread will set the pipeline's new_state in the main thread context
42 bool stop(bool use_main_thread = false);
42 bool pause();43 bool pause();
43 bool seek_to(const std::chrono::microseconds& ts);44 bool seek_to(const std::chrono::microseconds& ts);
4445
4546
=== modified file 'src/core/media/gstreamer/playbin.cpp'
--- src/core/media/gstreamer/playbin.cpp 2016-06-16 22:42:30 +0000
+++ src/core/media/gstreamer/playbin.cpp 2016-07-04 15:03:57 +0000
@@ -121,7 +121,8 @@
121 is_missing_audio_codec(false),121 is_missing_audio_codec(false),
122 is_missing_video_codec(false),122 is_missing_video_codec(false),
123 audio_stream_id(-1),123 audio_stream_id(-1),
124 video_stream_id(-1)124 video_stream_id(-1),
125 current_new_state(GST_STATE_NULL)
125{126{
126 if (!pipeline)127 if (!pipeline)
127 throw std::runtime_error("Could not create pipeline for playbin.");128 throw std::runtime_error("Could not create pipeline for playbin.");
@@ -524,7 +525,17 @@
524 return result;525 return result;
525}526}
526527
527bool gstreamer::Playbin::set_state_and_wait(GstState new_state)528gboolean gstreamer::Playbin::set_state_in_main_thread(gpointer user_data)
529{
530 MH_TRACE("");
531 auto thiz = static_cast<Playbin*>(user_data);
532 if (thiz and thiz->pipeline)
533 gst_element_set_state(thiz->pipeline, thiz->current_new_state);
534
535 // Always return false so this is a single shot function call
536 return false;
537}
538bool gstreamer::Playbin::set_state_and_wait(GstState new_state, bool use_main_thread)
528{539{
529 static const std::chrono::nanoseconds state_change_timeout540 static const std::chrono::nanoseconds state_change_timeout
530 {541 {
@@ -534,25 +545,44 @@
534 std::chrono::milliseconds{5000}545 std::chrono::milliseconds{5000}
535 };546 };
536547
537 auto ret = gst_element_set_state(pipeline, new_state);548 bool result = false;
538549 GstState current, pending;
539 MH_DEBUG("Requested state change.");550 if (use_main_thread)
540
541 bool result = false; GstState current, pending;
542 switch(ret)
543 {551 {
544 case GST_STATE_CHANGE_FAILURE:552 // Cache this value for the static g_idle_add handler function
545 result = false; break;553 current_new_state = new_state;
546 case GST_STATE_CHANGE_NO_PREROLL:554 g_idle_add((GSourceFunc) gstreamer::Playbin::set_state_in_main_thread, (gpointer) this);
547 case GST_STATE_CHANGE_SUCCESS:555
548 result = true; break;556 MH_DEBUG("Requested state change in main thread context.");
549 case GST_STATE_CHANGE_ASYNC:557
558 GstState current, pending;
550 result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(559 result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
551 pipeline,560 pipeline,
552 &current,561 &current,
553 &pending,562 &pending,
554 state_change_timeout.count());563 state_change_timeout.count());
555 break;564 }
565 else
566 {
567 const auto ret = gst_element_set_state(pipeline, new_state);
568
569 MH_DEBUG("Requested state change not using main thread context.");
570
571 switch (ret)
572 {
573 case GST_STATE_CHANGE_FAILURE:
574 result = false; break;
575 case GST_STATE_CHANGE_NO_PREROLL:
576 case GST_STATE_CHANGE_SUCCESS:
577 result = true; break;
578 case GST_STATE_CHANGE_ASYNC:
579 result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
580 pipeline,
581 &current,
582 &pending,
583 state_change_timeout.count());
584 break;
585 }
556 }586 }
557587
558 // We only should query the pipeline if we actually succeeded in588 // We only should query the pipeline if we actually succeeded in
559589
=== modified file 'src/core/media/gstreamer/playbin.h'
--- src/core/media/gstreamer/playbin.h 2016-02-19 16:14:42 +0000
+++ src/core/media/gstreamer/playbin.h 2016-07-04 15:03:57 +0000
@@ -96,9 +96,12 @@
9696
97 void setup_source(GstElement *source);97 void setup_source(GstElement *source);
9898
99 // Sets the pipeline's state (stopped, playing, paused, etc). Optional parameter makes this call99 // Sets the pipeline state in the main thread context instead of the possibility of creating
100 // in the main_loop context.100 // a deadlock in the streaming thread
101 bool set_state_and_wait(GstState new_state);101 static gboolean set_state_in_main_thread(gpointer user_data);
102 // Sets the pipeline's state (stopped, playing, paused, etc). use_main_thread will set the
103 // pipeline's new_state in the main thread context.
104 bool set_state_and_wait(GstState new_state, bool use_main_thread = false);
102 bool seek(const std::chrono::microseconds& ms);105 bool seek(const std::chrono::microseconds& ms);
103106
104 core::ubuntu::media::video::Dimensions get_video_dimensions() const;107 core::ubuntu::media::video::Dimensions get_video_dimensions() const;
@@ -148,6 +151,7 @@
148 bool is_missing_video_codec;151 bool is_missing_video_codec;
149 gint audio_stream_id;152 gint audio_stream_id;
150 gint video_stream_id;153 gint video_stream_id;
154 GstState current_new_state;
151};155};
152}156}
153157
154158
=== modified file 'src/core/media/metadata.cpp'
--- src/core/media/metadata.cpp 2016-06-15 17:49:49 +0000
+++ src/core/media/metadata.cpp 2016-07-04 15:03:57 +0000
@@ -29,9 +29,19 @@
29 if (not is_set(key))29 if (not is_set(key))
30 return std::string{};30 return std::string{};
3131
32 return std::string{g_uri_escape_string(map.at(key).c_str(),32 char* escaped {g_uri_escape_string(map.at(key).c_str(),
33 "!$&'()*+,;=:/?[]@", // Reserved chars33 "!$&'()*+,;=:/?[]@", // Reserved chars
34 true)}; // Allow UTF-8 chars34 true)};
35 if (!escaped)
36 {
37 return std::string{};
38 }
39 else
40 {
41 std::string s{escaped};
42 g_free(escaped);
43 return s;
44 }
35}45}
3646
37const std::string& media::Track::MetaData::album() const47const std::string& media::Track::MetaData::album() const
3848
=== modified file 'src/core/media/player_implementation.cpp'
--- src/core/media/player_implementation.cpp 2016-06-23 00:17:16 +0000
+++ src/core/media/player_implementation.cpp 2016-07-04 15:03:57 +0000
@@ -665,7 +665,8 @@
665 && d->engine->state() != gstreamer::Engine::State::stopped)665 && d->engine->state() != gstreamer::Engine::State::stopped)
666 {666 {
667 MH_INFO("End of tracklist reached, stopping playback");667 MH_INFO("End of tracklist reached, stopping playback");
668 d->engine->stop();668 const constexpr bool use_main_thread = true;
669 d->engine->stop(use_main_thread);
669 }670 }
670 });671 });
671672

Subscribers

People subscribed via source and target branches

to all changes: