Merge "PREMIUMAPP-776: Add changes to accommodate distro feature"
diff --git a/plugin/Cobalt.config b/plugin/Cobalt.config
index b2b837a..674e0d5 100644
--- a/plugin/Cobalt.config
+++ b/plugin/Cobalt.config
@@ -28,6 +28,9 @@
     if(PLUGIN_COBALT_CONTENT_DIR)
         kv(contentdir ${PLUGIN_COBALT_CONTENT_DIR})
     endif()
+    if(PLUGIN_COBALT_ESSOS_CONTEXT_DESTROY)
+        kv(essoscontextdestroy ${PLUGIN_COBALT_ESSOS_CONTEXT_DESTROY})
+    endif()
     if(PLUGIN_COBALT_PRELOAD)
         kv(preload ${PLUGIN_COBALT_PRELOAD})
     endif()
diff --git a/plugin/CobaltImplementation.cpp b/plugin/CobaltImplementation.cpp
index 01c23e8..8f5346b 100644
--- a/plugin/CobaltImplementation.cpp
+++ b/plugin/CobaltImplementation.cpp
@@ -99,12 +99,14 @@
       , Language()
       , ContentDir()
       , PreloadEnabled()
+      , EssosContextDestroy()
       , AutoSuspendDelay() {
       Add(_T("url"), &Url);
       Add(_T("clientidentifier"), &ClientIdentifier);
       Add(_T("language"), &Language);
       Add(_T("contentdir"), &ContentDir);
       Add(_T("gstdebug"), &GstDebug);
+      Add(_T("essoscontextdestroy"), &EssosContextDestroy);
       Add(_T("preload"), &PreloadEnabled);
       Add(_T("autosuspenddelay"), &AutoSuspendDelay);
       Add(_T("systemproperties"), &SystemProperties);
@@ -119,6 +121,7 @@
     Core::JSON::String Language;
     Core::JSON::String ContentDir;
     Core::JSON::String GstDebug;
+    Core::JSON::String EssosContextDestroy;
     Core::JSON::Boolean PreloadEnabled;
     Core::JSON::DecUInt16 AutoSuspendDelay;
     Core::JSON::VariantContainer SystemProperties;
@@ -297,6 +300,11 @@
         Core::SystemInfo::SetEnvironment(_T("GST_DEBUG"), gstDebug);
       }
 
+      if (config.EssosContextDestroy.IsSet() == true) {
+        string val = config.EssosContextDestroy.Value();
+        Core::SystemInfo::SetEnvironment(_T("COBALT_ESSOS_CONTEXT_DESTROY"), val);
+      }
+
       if (config.PreloadEnabled.IsSet() == true) {
         _preloadEnabled = config.PreloadEnabled.Value();
       }
diff --git a/src/third_party/starboard/rdk/shared/application_rdk.cc b/src/third_party/starboard/rdk/shared/application_rdk.cc
index 1ac8625..57bcdc1 100644
--- a/src/third_party/starboard/rdk/shared/application_rdk.cc
+++ b/src/third_party/starboard/rdk/shared/application_rdk.cc
@@ -97,26 +97,8 @@
 Application::Application()
   : input_handler_(new EssInput)
   , hang_monitor_(new HangMonitor("Application")) {
-  bool error = false;
-  ctx_ = EssContextCreate();
-
-  if ( !EssContextInit(ctx_) ) {
-    error = true;
-  }
-  else if ( !EssContextSetTerminateListener(ctx_, this, &terminateListener) ) {
-    error = true;
-  }
-  else if ( !EssContextSetKeyListener(ctx_, this, &keyListener) ) {
-    error = true;
-  }
-  else if ( !EssContextSetSettingsListener(ctx_, this, &settingsListener) ) {
-    error = true;
-  }
-
-  if ( error ) {
-    const char *detail = EssContextGetLastErrorDetail(ctx_);
-    SB_LOG(ERROR) << "Essos error: '" <<  detail << '\'';
-  }
+  essos_context_recycle_ = !!getenv("COBALT_ESSOS_CONTEXT_DESTROY");
+  BuildEssosContext();
 }
 
 Application::~Application() {
@@ -279,6 +261,9 @@
 }
 
 void Application::OnResume() {
+  if ( essos_context_recycle_ )
+    BuildEssosContext();
+
   setTimerInterval(ess_timer_fd_, kEssRunLoopPeriod);
   MaterializeNativeWindow();
 }
@@ -344,7 +329,12 @@
 
   native_window_ = 0;
 
-  EssContextStop(ctx_);
+  if ( essos_context_recycle_ ) {
+    EssContextDestroy(ctx_);
+    ctx_ = NULL;
+  }
+  else
+    EssContextStop(ctx_);
 }
 
 void Application::DisplayInfoChanged() {
@@ -359,6 +349,30 @@
   WindowSizeChanged(data, &Application::DeleteDestructor<SbEventWindowSizeChangedData>);
 }
 
+void Application::BuildEssosContext()
+{
+  bool error = false;
+  ctx_ = EssContextCreate();
+
+  if ( !EssContextInit(ctx_) ) {
+    error = true;
+  }
+  else if ( !EssContextSetTerminateListener(ctx_, this, &terminateListener) ) {
+    error = true;
+  }
+  else if ( !EssContextSetKeyListener(ctx_, this, &keyListener) ) {
+    error = true;
+  }
+  else if ( !EssContextSetSettingsListener(ctx_, this, &settingsListener) ) {
+    error = true;
+  }
+
+  if ( error ) {
+    const char *detail = EssContextGetLastErrorDetail(ctx_);
+    SB_LOG(ERROR) << "Essos error: '" <<  detail << '\'';
+  }
+}
+
 }  // namespace shared
 }  // namespace rdk
 }  // namespace starboard
diff --git a/src/third_party/starboard/rdk/shared/application_rdk.h b/src/third_party/starboard/rdk/shared/application_rdk.h
index 123603a..7b0c7dc 100644
--- a/src/third_party/starboard/rdk/shared/application_rdk.h
+++ b/src/third_party/starboard/rdk/shared/application_rdk.h
@@ -97,6 +97,7 @@
  private:
   void MaterializeNativeWindow();
   void DestroyNativeWindow();
+  void BuildEssosContext();
 
   static EssTerminateListener terminateListener;
   static EssKeyListener keyListener;
@@ -109,6 +110,7 @@
   int window_width_ { 0 };
   int window_height_ { 0 };
   bool resize_pending_ { false };
+  bool essos_context_recycle_ { false };
 
   SbTime ess_loop_last_ts_ { 0 };
   int ess_timer_fd_ { -1 };
diff --git a/src/third_party/starboard/rdk/shared/ess_input.cc b/src/third_party/starboard/rdk/shared/ess_input.cc
index 54a2b15..a069fcc 100644
--- a/src/third_party/starboard/rdk/shared/ess_input.cc
+++ b/src/third_party/starboard/rdk/shared/ess_input.cc
@@ -394,6 +394,61 @@
   return kSbKeyModifiersNone;
 }
 
+bool IsYouTubeCompliantKey(SbKey key) {
+  // Returns true if `key` is supported by YouTube apps.
+  // See 'Remote keys requirements' from
+  //  https://developers.google.com/youtube/living-room/certification/software-certification-2023#remote-keys-and-events
+  // section:
+  // '8.1.2 Device remote keys not implemented by the YouTube application or reserved for device system functions MUST NOT dispatch any key event or key code.'
+  switch (key) {
+    case kSbKeyLeft:
+    case kSbKeyRight:
+    case kSbKeyUp:
+    case kSbKeyDown:
+    case kSbKeyReturn:
+    case kSbKeyEscape:
+    case kSbKeyPlay:
+    case kSbKeyPause:
+    case kSbKeyMediaPlayPause:
+    case kSbKeyMediaStop:
+    case kSbKeyMediaFastForward:
+    case kSbKeyMediaRewind:
+    case kSbKeySpace:
+    case kSbKeyBackspace:
+    case kSbKeyDelete:
+    case kSbKeyBrowserSearch:
+    case kSbKeyMicrophone:
+    case kSbKeyMediaPrevTrack:
+    case kSbKeyMediaNextTrack:
+    case kSbKeySubtitle:
+    case kSbKeyRed:
+    case kSbKeyGreen:
+    case kSbKeyYellow:
+    case kSbKeyBlue:
+    case kSbKeyLaunchThisApplication:
+    case kSbKey0:
+    case kSbKey1:
+    case kSbKey2:
+    case kSbKey3:
+    case kSbKey4:
+    case kSbKey5:
+    case kSbKey6:
+    case kSbKey7:
+    case kSbKey8:
+    case kSbKey9:
+    case kSbKeyChannelUp:
+    case kSbKeyChannelDown:
+    case kSbKeyLast:
+    case kSbKeyMediaAudioTrack:
+    case kSbKeyInfo:
+    case kSbKeyGuide:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 }  // namespace
 
 EssInput::EssInput() : key_repeat_interval_(kKeyHoldTime) {
@@ -410,6 +465,11 @@
     return;
   }
 
+  if (!IsYouTubeCompliantKey(sb_key)) {
+    DeleteRepeatKey();
+    return;
+  }
+
   SbInputData* data = new SbInputData();
   memset(data, 0, sizeof(*data));
 #if SB_API_VERSION < 13
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 a93fb88..5f3bdca 100644
--- a/src/third_party/starboard/rdk/shared/player/player_internal.cc
+++ b/src/third_party/starboard/rdk/shared/player/player_internal.cc
@@ -651,7 +651,11 @@
     case kSbMediaTransferId12BitBt2020:
       return GST_VIDEO_TRANSFER_BT2020_12;
     case kSbMediaTransferIdSmpteSt2084:
+#if GST_CHECK_VERSION(1, 18, 0)
+      return GST_VIDEO_TRANSFER_SMPTE2084;
+#else
       return GST_VIDEO_TRANSFER_SMPTE_ST_2084;
+#endif
     case kSbMediaTransferIdAribStdB67:
       return GST_VIDEO_TRANSFER_ARIB_STD_B67;
     case kSbMediaTransferIdUnspecified: