Select libcobalt version

Select desired cobalt version in meta-rdk/conf/distro/include/rdkv.inc:

diff --git a/conf/distro/include/rdkv.inc b/conf/distro/include/rdkv.inc
index 31d8c74a..97ad8dfb 100644
--- a/conf/distro/include/rdkv.inc
+++ b/conf/distro/include/rdkv.inc
@@ -108,5 +108,5 @@ BBMASK .= "${@bb.utils.contains('DISTRO_FEATURES', 'ssl-1.1.1', '|.meta-rdk-ext/
 
 inherit ${@bb.utils.contains('DISTRO_FEATURES', 'sbom', 'create-spdx', '', d)}
 
-PREFERRED_VERSION_libcobalt ?= "22.lts.stable"
+PREFERRED_VERSION_libcobalt ?= "23.lts.stable"
 PREFERRED_VERSION_libcobalt_morty = "21.lts.stable"

Libcobalt modifications

Enable ocdm rialto in libcobalt recipt in meta-rdk-video/recipes-extended/cobalt/libcobalt_22.lts.stable.bb:

 
-DEPENDS += "virtual/egl essos wpeframework-clientlibraries gstreamer1.0 gstreamer1.0-plugins-base python-native ninja-native bison-native openssl-native"
-RDEPENDS_${PN} += "gstreamer1.0-plugins-base-app gstreamer1.0-plugins-base-playback"
+DEPENDS += "virtual/egl essos wpeframework-clientlibraries gstreamer1.0 gstreamer1.0-plugins-base python-native ninja-native bison-native openssl-native rialto-ocdm"
+RDEPENDS_${PN} += "gstreamer1.0-plugins-base-app gstreamer1.0-plugins-base-playback rialto-ocdm"
 
 TUNE_CCARGS_remove = "-fno-omit-frame-pointer -fno-optimize-sibling-calls"
 
@@ -54,6 +54,7 @@ addtask unpack_extra after do_unpack before do_patch
 
 do_configure() {
     export COBALT_HAS_OCDM="${@bb.utils.contains('PACKAGECONFIG', 'opencdm', '1', '0', d)}"
+    export COBALT_OCDM_LIBRARY_NAME="ocdmRialto"
     export COBALT_ARM_CALLCONVENTION="${@bb.utils.contains('TUNE_FEATURES', 'callconvention-hard', 'hardfp', 'softfp', d)}"
     ${S}/src/cobalt/build/gyp_cobalt -C qa -C gold -C devel ${COBALT_PLATFORM}
 }

Build libcobalt:

bitbake libcobalt

Patch libcobalt to enable ocdmRialto:

diff --git a/src/third_party/starboard/rdk/arm/gyp_configuration.py b/src/third_party/starboard/rdk/arm/gyp_configuration.py
index c189893..5cc850a 100644
--- a/src/third_party/starboard/rdk/arm/gyp_configuration.py
+++ b/src/third_party/starboard/rdk/arm/gyp_configuration.py
@@ -47,6 +47,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
     super(RDKPlatformConfig, self).__init__(platform)
 
     self.has_ocdm = os.environ.get('COBALT_HAS_OCDM', '0')
+    self.ocdm_library_name = os.environ.get('COBALT_OCDM_LIBRARY_NAME', 'ocdm')
     self.sabi_json_path = 'starboard/sabi/arm/%s/sabi-v13.json' % (os.environ.get('COBALT_ARM_CALLCONVENTION', 'hardfp'))
     self.enable_evergreen_lite = os.environ.get('COBALT_EVERGREEN_LITE', '0')
     self.sysroot = os.path.realpath(os.environ.get('PKG_CONFIG_SYSROOT_DIR', '/'))
@@ -66,6 +67,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
         'clang': 0,
         'sysroot': self.sysroot,
         'has_ocdm': self.has_ocdm,
+        'ocdm_library_name': self.ocdm_library_name,
         'sb_evergreen_compatible': self.enable_evergreen_lite,
         'sb_evergreen_compatible_lite': self.enable_evergreen_lite,
         'sb_evergreen_compatible_libunwind': self.enable_evergreen_lite,
diff --git a/src/third_party/starboard/rdk/brcm/arm/gyp_configuration.py b/src/third_party/starboard/rdk/brcm/arm/gyp_configuration.py
index 0d99905..8525232 100644
--- a/src/third_party/starboard/rdk/brcm/arm/gyp_configuration.py
+++ b/src/third_party/starboard/rdk/brcm/arm/gyp_configuration.py
@@ -47,6 +47,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
     super(RDKPlatformConfig, self).__init__(platform)
 
     self.has_ocdm = os.environ.get('COBALT_HAS_OCDM', '0')
+    self.ocdm_library_name = os.environ.get('COBALT_OCDM_LIBRARY_NAME', 'ocdm')
     self.sabi_json_path = 'starboard/sabi/arm/%s/sabi-v13.json' % (os.environ.get('COBALT_ARM_CALLCONVENTION', 'hardfp'))
     self.sysroot = os.path.realpath(os.environ.get('PKG_CONFIG_SYSROOT_DIR', '/'))
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
@@ -65,6 +66,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
         'clang': 0,
         'sysroot': self.sysroot,
         'has_ocdm': self.has_ocdm,
+        'ocdm_library_name': self.ocdm_library_name,
     })
     variables.update({
         'cobalt_font_package': 'limited',
diff --git a/src/third_party/starboard/rdk/rpi/gyp_configuration.py b/src/third_party/starboard/rdk/rpi/gyp_configuration.py
index b0081b6..918ecd7 100644
--- a/src/third_party/starboard/rdk/rpi/gyp_configuration.py
+++ b/src/third_party/starboard/rdk/rpi/gyp_configuration.py
@@ -47,6 +47,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
     super(RDKPlatformConfig, self).__init__(platform)
 
     self.has_ocdm = os.environ.get('COBALT_HAS_OCDM', '0')
+    self.ocdm_library_name = os.environ.get('COBALT_OCDM_LIBRARY_NAME', 'ocdm')
     self.sabi_json_path = 'starboard/sabi/arm/%s/sabi-v13.json' % (os.environ.get('COBALT_ARM_CALLCONVENTION', 'hardfp'))
     self.sysroot = os.path.realpath(os.environ.get('PKG_CONFIG_SYSROOT_DIR', '/'))
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
@@ -65,6 +66,7 @@ class RDKPlatformConfig(platform_configuration.PlatformConfiguration):
         'clang': 0,
         'sysroot': self.sysroot,
         'has_ocdm': self.has_ocdm,
+        'ocdm_library_name': self.ocdm_library_name,
     })
     variables.update({
         'cobalt_font_package': 'limited',
diff --git a/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc b/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
index 934c5c8..5182351 100644
--- a/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
+++ b/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
@@ -203,8 +203,17 @@ std::vector<std::string> CodecToGstCaps(SbMediaAudioCodec codec,
     }
 
     case kSbMediaAudioCodecAc3:
-    case kSbMediaAudioCodecEac3:
-      return {{"audio/x-eac3"}};
+    case kSbMediaAudioCodecEac3:{
+    std::string primary_caps = "audio/x-eac3";
+    if (info) {
+        primary_caps +=
+            ", channels=" + std::to_string(info->number_of_channels);
+        primary_caps += ", rate=" + std::to_string(info->samples_per_second);
+        
+      }
+      SB_LOG(INFO) << "Adding audio caps data: " << primary_caps;
+      return {{primary_caps}};
+    }
 
     case kSbMediaAudioCodecOpus: {
       std::string primary_caps = "audio/x-opus, channel-mapping-family=0";
diff --git a/src/third_party/starboard/rdk/shared/system.gyp b/src/third_party/starboard/rdk/shared/system.gyp
index 0e8eb74..f48aec8 100644
--- a/src/third_party/starboard/rdk/shared/system.gyp
+++ b/src/third_party/starboard/rdk/shared/system.gyp
@@ -115,15 +115,15 @@
           'type': 'none',
           'direct_dependent_settings': {
             'cflags': [
-              '<!@(<(pkg-config) --cflags ocdm)',
+              '<!@(<(pkg-config) --cflags <(ocdm_library_name))',
             ],
           },
           'link_settings': {
             'ldflags': [
-              '<!@(<(pkg-config) --libs-only-L --libs-only-other ocdm)',
+              '<!@(<(pkg-config) --libs-only-L --libs-only-other <(ocdm_library_name))',
             ],
             'libraries': [
-              '<!@(<(pkg-config) --libs-only-l ocdm)',
+              '<!@(<(pkg-config) --libs-only-l <(ocdm_library_name))',
               '-ldl',
             ],
           },

Rebuild libcobalt:

bitbake libcobalt -C configure

Note: libcobalt 23 is still been tested with rialto and may not be stable

Enable ocdm rialto in libcobalt recipe in meta-rdk-video/recipes-extended/cobalt/libcobalt_23.lts.stable.bb

  diff --git a/recipes-extended/cobalt/libcobalt_23.lts.stable.bb b/recipes-extended/cobalt/libcobalt_23.lts.stable.bb
index 6bc2e7f8..8236fead 100644
--- a/recipes-extended/cobalt/libcobalt_23.lts.stable.bb
+++ b/recipes-extended/cobalt/libcobalt_23.lts.stable.bb
@@ -29,9 +29,9 @@ S = "${WORKDIR}/git"
 
 DEPENDS += "virtual/egl essos wpeframework-clientlibraries gstreamer1.0 gstreamer1.0-plugins-base"
 DEPENDS += " ninja-native bison-native openssl-native gn-native ccache-native"
-DEPENDS += " python3-six-native python3-urllib3-native"
+DEPENDS += " python3-six-native python3-urllib3-native rialto-ocdm"
 
-RDEPENDS_${PN} += "gstreamer1.0-plugins-base-app gstreamer1.0-plugins-base-playback"
+RDEPENDS_${PN} += "gstreamer1.0-plugins-base-app gstreamer1.0-plugins-base-playback rialto-ocdm"
 
 TUNE_CCARGS_remove = "-fno-omit-frame-pointer -fno-optimize-sibling-calls"

Build libcobalt:

bitbake libcobalt

Patch libcobalt:

diff --git a/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc b/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
index 934c5c8..e6f6753 100644
--- a/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
+++ b/src/third_party/starboard/rdk/shared/media/gst_media_utils.cc
@@ -203,8 +203,17 @@ std::vector<std::string> CodecToGstCaps(SbMediaAudioCodec codec,
     }
 
     case kSbMediaAudioCodecAc3:
-    case kSbMediaAudioCodecEac3:
-      return {{"audio/x-eac3"}};
+    case kSbMediaAudioCodecEac3: {
+      std::string primary_caps = "audio/x-eac3";
+      if (info)
+      {
+        primary_caps +=
+            ", channels=" + std::to_string(info->number_of_channels);
+        primary_caps += ", rate=" + std::to_string(info->samples_per_second);
+      }
+      SB_LOG(INFO) << "Adding audio caps data: " << primary_caps;
+      return {{primary_caps}};
+    }
 
     case kSbMediaAudioCodecOpus: {
       std::string primary_caps = "audio/x-opus, channel-mapping-family=0";
diff --git a/src/third_party/starboard/rdk/shared/player/player_internal.cc b/src/third_party/star
board/rdk/shared/player/player_internal.cc
index d5ce9a6..8b464bb 100644
--- a/src/third_party/starboard/rdk/shared/player/player_internal.cc
+++ b/src/third_party/starboard/rdk/shared/player/player_internal.cc
@@ -1241,6 +1241,7 @@ class PlayerImpl : public Player {
   static void SetupElement(GstElement* pipeline,
                            GstElement* element,
                            PlayerImpl* self);
+  static void ConfigureLimitedVideoWesterossink(GstElement* element);
   static void OnVideoBufferUnderflow(PlayerImpl* self);
 
   bool ChangePipelineState(GstState state) const;
@@ -1272,7 +1273,6 @@ class PlayerImpl : public Player {
   void HandleApplicationMessage(GstBus* bus, GstMessage* message);
   void WritePendingSamples();
   void CheckBuffering(gint64 position);
-  void ConfigureLimitedVideo();
   void SchedulePlayingStateUpdate();
   void AddBufferingProbe(GstClockTime target, int ticket);
 
@@ -1461,7 +1461,8 @@ PlayerImpl::PlayerImpl(SbPlayer player,
 
   if (max_video_capabilities && *max_video_capabilities) {
     max_video_capabilities_ = max_video_capabilities;
-    ConfigureLimitedVideo();
+    // Limited video only
+    audio_codec_ = kSbMediaAudioCodecNone;
   }
 
   if (audio_codec_ == kSbMediaAudioCodecNone) {
@@ -1929,29 +1930,58 @@ void PlayerImpl::OnVideoBufferUnderflow(PlayerImpl* self)
 void PlayerImpl::SetupElement(GstElement* pipeline,
                               GstElement* element,
                               PlayerImpl* self) {
-  if (GST_IS_BASE_SINK(element)) {
-    static bool disable_wait_video = !!getenv("COBALT_AML_DISABLE_WAIT_VIDEO");
-    bool has_video = (self->video_codec_ != kSbMediaVideoCodecNone);
-    if (has_video && g_str_has_prefix(GST_ELEMENT_NAME(element), "amlhalasink") && !disable_wait_
video) {
-      g_object_set(element, "wait-video", TRUE, "a-wait-timeout", 4000, nullptr);
+  if (!self->max_video_capabilities_.empty() && g_str_has_prefix(GST_ELEMENT_NAME(element), "rial
tomsevideosink")) {
+    uint32_t width = 0;
+    uint32_t height = 0;
+    ParseMaxVideoCapabilities(self->max_video_capabilities_.c_str(), &width, &height, nullptr);
+    if (0 != width){
+      if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "maxVideoWidth")) {
+        GST_INFO("Setting rialtomsevideosink maxVideoWidth to %u", width);
+        g_object_set(element, "maxVideoWidth", width, nullptr);
+      }
     }
-    else
-    if (has_video && g_str_has_prefix(GST_ELEMENT_NAME(element), "westerossink")) {
-      if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "zoom-mode")) {
-        GST_INFO("Setting westerossink zoom-mode to 0");
-        g_object_set(element, "zoom-mode", 0, nullptr);
+    if (0 != height){
+      if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "maxVideoHeight")) {
+        GST_INFO("Setting rialtomsevideosink maxVideoHeight to %u", height);
+        g_object_set(element, "maxVideoHeight", height, nullptr);
       }
-      g_signal_connect_swapped(
-        G_OBJECT(element), "buffer-underflow-callback",
-        G_CALLBACK(OnVideoBufferUnderflow), self);
     }
-    else
-    if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiosink")) {
-      g_object_set(G_OBJECT(element), "async", TRUE, nullptr);
+  }
+  else {
+    if (GST_IS_BASE_SINK(element)) {
+      static bool disable_wait_video = !!getenv("COBALT_AML_DISABLE_WAIT_VIDEO");
+      bool has_video = (self->video_codec_ != kSbMediaVideoCodecNone);
+      if (has_video && g_str_has_prefix(GST_ELEMENT_NAME(element), "amlhalasink") && !disable_wai
t_video) {
+        g_object_set(element, "wait-video", TRUE, "a-wait-timeout", 4000, nullptr);
+      }
+      else
+      if (has_video && g_str_has_prefix(GST_ELEMENT_NAME(element), "westerossink")) {
+        if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "zoom-mode")) {
+          GST_INFO("Setting westerossink zoom-mode to 0");
+          g_object_set(element, "zoom-mode", 0, nullptr);
+        }
+        g_signal_connect_swapped(
+          G_OBJECT(element), "buffer-underflow-callback",
+          G_CALLBACK(OnVideoBufferUnderflow), self);
+      }
+      else
+      if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiosink")) {
+        g_object_set(G_OBJECT(element), "async", TRUE, nullptr);
+      }
     }
   }
 }
 
+// static
+void PlayerImpl::ConfigureLimitedVideoWesterossink(GstElement* element) {
+  if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "res-usage")) {
+    g_object_set(element, "res-usage", 0x0u, nullptr);
+  }
+  else {
+    GST_WARNING("'westerossink' has no 'res-usage' property, secondary video may steal decoder");
+  }
+}
+
 void PlayerImpl::MarkEOS(SbMediaType stream_type) {
   GstElement* src = nullptr;
   if (stream_type == kSbMediaTypeVideo) {
@@ -2788,29 +2818,6 @@ void PlayerImpl::HandleApplicationMessage(GstBus* bus, GstMessage* message)
 {
   }
 }
 
-void PlayerImpl::ConfigureLimitedVideo() {
-  GstElementFactory* factory = gst_element_factory_find("westerossink");
-  if (factory) {
-    GstElement* video_sink = gst_element_factory_create(factory, nullptr);
-    if (video_sink) {
-      if (g_object_class_find_property(G_OBJECT_GET_CLASS(video_sink), "res-usage")) {
-        g_object_set(video_sink, "res-usage", 0x0u, nullptr);
-      }
-      else {
-        GST_WARNING("'westerossink' has no 'res-usage' property, secondary video may steal decode
r");
-      }
-      g_object_set(pipeline_, "video-sink", video_sink, nullptr);
-    }
-    else {
-      GST_DEBUG("Failed to create 'westerossink'");
-    }
-    gst_object_unref(GST_OBJECT(factory));
-  }
-
-  // enforce no audio
-  audio_codec_ = kSbMediaAudioCodecNone;
-}
-
 void PlayerImpl::AddBufferingProbe(GstClockTime target, int ticket) {
   struct BufferingProbeData {
     GstClockTime target_time;

Rebuild libcobalt:

bitbake libcobalt -C configure

Running Cobalt + Rialto

Copy Rialto libraries and libcobalt to STB:

scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/rialto-gstreamer/git-r0/package/usr/lib/gstreamer-1.0/libgstrialtosinks.so root@[BOX_IP]:/tmp
scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/rialto/git-r0/package/usr/lib/libRialtoClient.so root@[BOX_IP]:/tmp
scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/rialto/git-r0/package/usr/bin/RialtoServer* root@[BOX_IP]:/tmp
scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/rialto/git-r0/package/usr/lib/libRialtoServerManager.so root@[BOX_IP]:/tmp
scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/rialto-ocdm/git-r0/package/usr/lib/libocdmRialto.so root@[BOX_IP]:/tmp
scp -P [BOX_PORT] tmp/work/armv7vet2hf-neon-rdk-linux-gnueabi/libcobalt/21.lts.stable-7/deploy-ipks/armv7vet2hf-neon/libcobalt_21.lts.stable-7_armv7vet2hf-neon.ipk root@[BOX_IP]:/tmp

Login to STB and:

Mount libs:

mount -o bind libRialtoServerManager.so /usr/lib/libRialtoServerManager.so
mount -o bind RialtoServer /usr/bin/RialtoServer
mount -o bind /tmp/libRialtoClient.so /usr/lib/libRialtoClient.so
mount -o bind /tmp/libgstrialtosinks.so /usr/lib/gstreamer-1.0/libgstrialtosinks.so
mount -o bind /tmp/libocdmRialto.so /usr/lib/libocdmRialto.so
mount -o bind RialtoServerManagerSim /usr/bin/RialtoServerManagerSim

Unpack libcobalt:

ar -x libcobalt_22.lts.stable-5_armv7vet2hf-neon.ipk
tar -xf data.tar.xz

Run westeros:

XDG_RUNTIME_DIR=/tmp LD_PRELOAD=/usr/lib/libwesteros_gl.so.0 westeros --renderer /usr/lib/libwesteros_render_embedded.so.0.0.0 --embedded --display "westeros-cobalt" --window-size 1920x1080

Start server manager simulator. Environment variables enabling debug logs for server can be used in this step Rialto Debug Control. For other config options see Rialto Config:

RIALTO_DEBUG=5 RIALTO_SESSION_SERVER_STARTUP_TIMEOUT_MS=2000 SESSION_SERVER_ENV_VARS='XDG_RUNTIME_DIR=/tmp;RIALTO_SINKS_RANK=0;GST_REGISTRY=/tmp/rialto-server-gstreamer-cache.bin;WAYLAND_DISPLAY=westeros-cobalt;FORCE_SAP=TRUE;FORCE_SVP=TRUE' /usr/bin/RialtoServerManagerSim

Request ServerManager Simulator to spawn Rialto Server for YouTube app:

curl -X POST -d "" localhost:9008/SetState/YouTube/Active

At this step You can either:

Run YouTube Conformance Tests :

RIALTO_DEBUG=5 RIALTO_SOCKET_PATH=/tmp/rialto-0 XDG_RUNTIME_DIR=/tmp WAYLAND_DISPLAY=westeros-cobalt LD_LIBRARY_PATH="$PWD/usr/lib" COBALT_CONTENT_DIR="$PWD/usr/share/content/data" RIALTO_CLIENT_BACKEND_LIB="/usr/lib/libRialtoClient.so" usr/bin/cobalt_bin --url="https://ytlr-cert.appspot.com/2021/main.html"

or Run YouTube app:

RIALTO_DEBUG=5 RIALTO_SOCKET_PATH=/tmp/rialto-0 XDG_RUNTIME_DIR=/tmp WAYLAND_DISPLAY=westeros-cobalt LD_LIBRARY_PATH="$PWD/usr/lib" COBALT_CONTENT_DIR="$PWD/usr/share/content/data" RIALTO_CLIENT_BACKEND_LIB="/usr/lib/libRialtoClient.so" usr/bin/cobalt_bin

Rialto Debug Control environment variables can be used to enable client logs both for YT app and Conformance tests.