Force player stop on application suspend

Change-Id: Id65b8988127496a2bc1cc7b5446cf6df9f0f9be1
diff --git a/src/third_party/starboard/rdk/shared/application_rdk.cc b/src/third_party/starboard/rdk/shared/application_rdk.cc
index 90e28f6..bd53d6a 100644
--- a/src/third_party/starboard/rdk/shared/application_rdk.cc
+++ b/src/third_party/starboard/rdk/shared/application_rdk.cc
@@ -55,6 +55,10 @@
 void Teardown();
 }
 
+namespace player {
+void ForceStop();
+}  // namespace player
+
 EssTerminateListener Application::terminateListener = {
   //terminated
   [](void* data) { reinterpret_cast<Application*>(data)->OnTerminated(); }
@@ -229,6 +233,10 @@
 }
 
 void Application::Inject(Event* e) {
+  if (e && e->event && e->event->type == kSbEventTypeSuspend) {
+    player::ForceStop();
+  }
+
   QueueApplication::Inject(e);
 }
 
diff --git a/src/third_party/starboard/rdk/shared/player/player_internal.cc b/src/third_party/starboard/rdk/shared/player/player_internal.cc
index 5f61394..d63bcc1 100644
--- a/src/third_party/starboard/rdk/shared/player/player_internal.cc
+++ b/src/third_party/starboard/rdk/shared/player/player_internal.cc
@@ -29,6 +29,7 @@
 #include <map>
 #include <string>
 
+#include "starboard/once.h"
 #include "starboard/common/mutex.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
@@ -815,6 +816,8 @@
   // DrmSystemOcdm::Observer
   void OnKeyReady(const uint8_t* key, size_t key_len) override;
 
+  GstElement* GetPipeline() const { return pipeline_;  }
+
  private:
   enum class State {
     kNull,
@@ -1028,8 +1031,48 @@
   mutable SbTime position_update_time_us_{0};
   PendingBounds pending_bounds_;
   SbMediaColorMetadata color_metadata_{};
+  bool force_stop_ { false };
 };
 
+struct PlayerRegistry
+{
+  ::starboard::Mutex mutex_;
+  std::vector<PlayerImpl*> players_;
+
+  void Add(PlayerImpl *p) {
+    ::starboard::ScopedLock lock(mutex_);
+    auto it = std::find(players_.begin(), players_.end(), p);
+    if (it == players_.end()) {
+      players_.push_back(p);
+    }
+  }
+
+  void Remove(PlayerImpl *p) {
+    ::starboard::ScopedLock lock(mutex_);
+    players_.erase(std::remove(players_.begin(), players_.end(), p), players_.end());
+  }
+
+  void ForceStop() {
+    std::vector<GstElement*> pipelines;
+    {
+      ::starboard::ScopedLock lock(mutex_);
+      for(const auto& p: players_) {
+        GstElement* pipeline = p->GetPipeline();
+        if (pipeline) {
+          gst_object_ref(pipeline);
+          pipelines.push_back(pipeline);
+        }
+      }
+    }
+    for (GstElement* pipeline : pipelines) {
+      GstStructure* structure = gst_structure_new_empty("force-stop");
+      gst_element_post_message(pipeline, gst_message_new_application(GST_OBJECT(pipeline), structure));
+      gst_object_unref(pipeline);
+    }
+  }
+};
+SB_ONCE_INITIALIZE_FUNCTION(PlayerRegistry, GetPlayerRegistry);
+
 PlayerImpl::PlayerImpl(SbPlayer player,
                        SbWindow window,
                        SbMediaVideoCodec video_codec,
@@ -1118,9 +1161,12 @@
     while(!g_main_loop_is_running(main_loop_))
       g_usleep(1);
   }
+  GetPlayerRegistry()->Add(this);
 }
 
 PlayerImpl::~PlayerImpl() {
+  GetPlayerRegistry()->Remove(this);
+
   GST_DEBUG_OBJECT(pipeline_, "Destroying player");
   {
     ::starboard::ScopedLock lock(source_setup_mutex_);
@@ -1158,6 +1204,23 @@
   GST_TRACE("%d", SbThreadGetId());
 
   switch (GST_MESSAGE_TYPE(message)) {
+    case GST_MESSAGE_APPLICATION: {
+      const GstStructure* structure = gst_message_get_structure(message);
+      if (gst_structure_has_name(structure, "force-stop") && !self->force_stop_) {
+         GST_INFO("Received force STOP, pipeline = %p!!!", self->pipeline_);
+         self->force_stop_ = true;
+         self->ChangePipelineState(GST_STATE_READY);
+         g_signal_handlers_disconnect_by_func(self->pipeline_, reinterpret_cast<gpointer>(&PlayerImpl::SetupSource), self);
+         ::starboard::ScopedLock lock(self->source_setup_mutex_);
+         if (self->source_setup_id_ > -1) {
+           GSource* src = g_main_context_find_source_by_id(self->main_loop_context_, self->source_setup_id_);
+           g_source_destroy(src);
+           self->source_setup_id_ = -1;
+         }
+      }
+      break;
+    }
+
     case GST_MESSAGE_EOS:
       if (GST_MESSAGE_SRC(message) == GST_OBJECT(self->pipeline_)) {
         GST_INFO("EOS");
@@ -1938,6 +2001,10 @@
 }
 
 bool PlayerImpl::ChangePipelineState(GstState state) const {
+  if (force_stop_ && state > GST_STATE_READY) {
+    GST_INFO_OBJECT(pipeline_, "Ignore state change due to forced stop");
+    return false;
+  }
   GST_DEBUG_OBJECT(pipeline_, "Changing state to %s",
                    gst_element_state_get_name(state));
   return gst_element_set_state(pipeline_, state) != GST_STATE_CHANGE_FAILURE;
@@ -2131,6 +2198,12 @@
 }
 
 }  // namespace
+
+void ForceStop() {
+  using third_party::starboard::rdk::shared::player::GetPlayerRegistry;
+  GetPlayerRegistry()->ForceStop();
+}
+
 }  // namespace player
 }  // namespace shared
 }  // namespace rdk