From 0cd0993333ca819b0b514a8e0779bc37ade6f0fe Mon Sep 17 00:00:00 2001 From: Abhijeet Kandalkar Date: Thu, 30 Jul 2026 11:03:51 +0530 Subject: [PATCH 1/2] starboard: Replace URL player config macro with tvOS buildflag SB_HAS_PLAYER_WITH_URL is legacy Cobalt configuration that was only defined for tvOS in configuration_public.h. In the Chromium architecture, BUILDFLAG(IS_IOS_TVOS) is the standard way to gate tvOS-specific code. Replace all SB_HAS(PLAYER_WITH_URL) guards with BUILDFLAG(IS_IOS_TVOS) across sbplayer_bridge, sbplayer_interface, starboard_renderer, and url_player_create_test. Replace the indirect SB_URL_PLAYER_INCLUDE_PATH include with the direct header path. Remove both definitions from starboard/tvos/shared/configuration_public.h. Required-For: #10698 Bug: 512045535 --- media/starboard/sbplayer_bridge.cc | 46 +++++++++---------- media/starboard/sbplayer_bridge.h | 25 +++++----- media/starboard/sbplayer_interface.cc | 4 +- media/starboard/sbplayer_interface.h | 15 +++--- .../starboard/starboard_renderer_unittest.cc | 5 +- starboard/nplb/url_player_create_test.cc | 11 +++-- starboard/tvos/shared/configuration_public.h | 8 ---- 7 files changed, 55 insertions(+), 59 deletions(-) diff --git a/media/starboard/sbplayer_bridge.cc b/media/starboard/sbplayer_bridge.cc index 310d3c405cc9..4875546f4cc5 100644 --- a/media/starboard/sbplayer_bridge.cc +++ b/media/starboard/sbplayer_bridge.cc @@ -168,7 +168,7 @@ GetStarboardExtensionExperimentalFeatures( SB_ONCE_INITIALIZE_FUNCTION(StatisticsWrapper, StatisticsWrapper::GetInstance); #endif // BUILDFLAG(COBALT_MEDIA_ENABLE_STARTUP_LATENCY_TRACKING) -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) SbPlayerBridge::SbPlayerBridge( SbPlayerInterface* interface, const scoped_refptr& task_runner, @@ -214,7 +214,7 @@ SbPlayerBridge::SbPlayerBridge( weak_factory_.GetWeakPtr())); #endif // BUILDFLAG(COBALT_MEDIA_ENABLE_SUSPEND_RESUME) } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) SbPlayerBridge::SbPlayerBridge( SbPlayerInterface* interface, @@ -266,10 +266,10 @@ SbPlayerBridge::SbPlayerBridge( , surface_view_(surface_view) #endif // BUILDFLAG(IS_ANDROID) -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) , is_url_based_(false) -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) #if BUILDFLAG(COBALT_MEDIA_ENABLE_CVAL) , cval_stats_(&interface->cval_stats_), @@ -373,9 +373,9 @@ void SbPlayerBridge::WriteBuffers( DemuxerStream::Type type, const std::vector>& buffers) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) DCHECK(!is_url_based_); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) #if BUILDFLAG(COBALT_MEDIA_ENABLE_SUSPEND_RESUME) if (allow_resume_after_suspend_) { @@ -512,7 +512,7 @@ SbPlayerBridge::GetAudioConfigurations() { return configurations; } -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) void SbPlayerBridge::GetUrlPlayerBufferedTimeRanges( TimeDelta* buffer_start_time, TimeDelta* buffer_length_time) { @@ -600,7 +600,7 @@ void SbPlayerBridge::SetDrmSystem(SbDrmSystem drm_system) { drm_system_ = drm_system; sbplayer_interface_->SetUrlPlayerDrmSystem(player_, drm_system); } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) void SbPlayerBridge::Suspend() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); @@ -646,7 +646,7 @@ void SbPlayerBridge::Resume(SbWindow window) { decoder_buffer_cache_.StartResuming(); #endif // BUILDFLAG(COBALT_MEDIA_ENABLE_SUSPEND_RESUME) -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) if (is_url_based_) { CreateUrlPlayer(url_); if (SbDrmSystemIsValid(drm_system_)) { @@ -655,9 +655,9 @@ void SbPlayerBridge::Resume(SbWindow window) { } else { CreatePlayer(); } -#else // SB_HAS(PLAYER_WITH_URL) +#else // BUILDFLAG(IS_IOS_TVOS) CreatePlayer(); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) if (SbPlayerIsValid(player_)) { state_ = kResuming; @@ -665,7 +665,7 @@ void SbPlayerBridge::Resume(SbWindow window) { } } -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) // static void SbPlayerBridge::EncryptedMediaInitDataEncounteredCB( SbPlayer player, @@ -715,7 +715,7 @@ void SbPlayerBridge::CreateUrlPlayer(const std::string& url) { UpdateBounds(); } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) void SbPlayerBridge::CreatePlayer() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); @@ -842,9 +842,9 @@ void SbPlayerBridge::CreatePlayer() { void SbPlayerBridge::WriteNextBuffersFromCache(DemuxerStream::Type type, int max_buffers_per_write) { DCHECK(state_ != kSuspended); -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) DCHECK(!is_url_based_); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) DCHECK(SbPlayerIsValid(player_)); @@ -875,9 +875,9 @@ void SbPlayerBridge::WriteBuffersInternal( const SbMediaAudioStreamInfo* audio_stream_info, const SbMediaVideoStreamInfo* video_stream_info) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) DCHECK(!is_url_based_); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) auto sample_type = DemuxerStreamTypeToSbMediaType(type); if (buffers.size() == 1 && buffers[0]->end_of_stream()) { @@ -1112,9 +1112,9 @@ void SbPlayerBridge::OnDecoderStatus(SbPlayer player, SbMediaType type, SbPlayerDecoderState state, int ticket) { -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) DCHECK(!is_url_based_); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) DCHECK(task_runner_->RunsTasksInCurrentSequence()); if (player_ != player || ticket != ticket_) { @@ -1230,9 +1230,9 @@ void SbPlayerBridge::OnPlayerError(SbPlayer player, } void SbPlayerBridge::OnDeallocateSample(const void* sample_buffer) { -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) DCHECK(!is_url_based_); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) DCHECK(task_runner_->RunsTasksInCurrentSequence()); if (enable_batched_buffer_deallocation_) { @@ -1447,7 +1447,7 @@ void SbPlayerBridge::DeallocateSampleCB(SbPlayer player, sample_buffer)); } -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) SbPlayerOutputMode SbPlayerBridge::ComputeSbUrlPlayerOutputMode( SbPlayerOutputMode default_output_mode) { // Try to choose the output mode according to the passed in value of @@ -1468,7 +1468,7 @@ SbPlayerOutputMode SbPlayerBridge::ComputeSbUrlPlayerOutputMode( return output_mode; } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) SbPlayerOutputMode SbPlayerBridge::ComputeSbPlayerOutputMode( SbPlayerOutputMode default_output_mode) const { diff --git a/media/starboard/sbplayer_bridge.h b/media/starboard/sbplayer_bridge.h index df3cbd292685..80ea56b9ee52 100644 --- a/media/starboard/sbplayer_bridge.h +++ b/media/starboard/sbplayer_bridge.h @@ -29,6 +29,7 @@ #include "base/task/sequenced_task_runner.h" #include "base/task/single_thread_task_runner.h" #include "base/time/time.h" +#include "build/build_config.h" #include "media/base/audio_decoder_config.h" #include "media/base/decoder_buffer.h" #include "media/base/demuxer_stream.h" @@ -77,7 +78,7 @@ class SbPlayerBridge { using GetDecodeTargetGraphicsContextProviderFunc = base::RepeatingCallback; -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) using OnEncryptedMediaInitDataEncounteredCB = base::RepeatingCallback< void(const char*, const unsigned char*, unsigned)>; // Create an SbPlayerBridge with url-based player. @@ -95,7 +96,7 @@ class SbPlayerBridge { std::string pipeline_identifier #endif // BUILDFLAG(COBALT_MEDIA_ENABLE_CVAL) ); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) using ExperimentalFeatures = StarboardRendererConfig::ExperimentalFeatures; // Create a SbPlayerBridge with normal player SbPlayerBridge(SbPlayerInterface* interface, @@ -146,14 +147,14 @@ class SbPlayerBridge { void GetInfo(PlayerInfo* out_info); std::vector GetAudioConfigurations(); -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) void GetUrlPlayerBufferedTimeRanges(base::TimeDelta* buffer_start_time, base::TimeDelta* buffer_length_time); void GetVideoResolution(int* frame_width, int* frame_height); base::TimeDelta GetDuration(); base::TimeDelta GetStartDate(); void SetDrmSystem(SbDrmSystem drm_system); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) void Suspend(); // TODO: This is temporary for supporting background media playback. @@ -211,7 +212,7 @@ class SbPlayerBridge { absl::flat_hash_map; -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) OnEncryptedMediaInitDataEncounteredCB on_encrypted_media_init_data_encountered_cb_; @@ -223,7 +224,7 @@ class SbPlayerBridge { unsigned int init_data_length); void CreateUrlPlayer(const std::string& url); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) void CreatePlayer(); #if BUILDFLAG(COBALT_MEDIA_ENABLE_SUSPEND_RESUME) @@ -285,10 +286,10 @@ class SbPlayerBridge { void* context, const void* sample_buffer); -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) SbPlayerOutputMode ComputeSbUrlPlayerOutputMode( SbPlayerOutputMode default_output_mode); -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) // Returns the output mode that should be used for a video with the given // specifications. SbPlayerOutputMode ComputeSbPlayerOutputMode( @@ -298,9 +299,9 @@ class SbPlayerBridge { void SendColorSpaceHistogram() const; // The following variables are initialized in the ctor and never changed. -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) std::string url_; -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) SbPlayerInterface* sbplayer_interface_; const scoped_refptr task_runner_; const GetDecodeTargetGraphicsContextProviderFunc @@ -384,9 +385,9 @@ class SbPlayerBridge { base::Time first_video_sample_time_{}; base::Time sb_player_state_presenting_time_{}; -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) const bool is_url_based_; -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) // Used for Gathered Sample Write. bool pending_audio_eos_buffer_ = false; diff --git a/media/starboard/sbplayer_interface.cc b/media/starboard/sbplayer_interface.cc index 74f9b9313334..1721941b96e9 100644 --- a/media/starboard/sbplayer_interface.cc +++ b/media/starboard/sbplayer_interface.cc @@ -154,7 +154,7 @@ SbDecodeTarget DefaultSbPlayerInterface::GetCurrentFrame(SbPlayer player) { return current_frame; } -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) SbPlayer DefaultSbPlayerInterface::CreateUrlPlayer( const char* url, SbWindow window, @@ -188,7 +188,7 @@ void DefaultSbPlayerInterface::GetUrlPlayerExtraInfo( SbUrlPlayerExtraInfo* out_url_player_info) { SbUrlPlayerGetExtraInfo(player, out_url_player_info); } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) bool DefaultSbPlayerInterface::GetAudioConfiguration( SbPlayer player, diff --git a/media/starboard/sbplayer_interface.h b/media/starboard/sbplayer_interface.h index 5db30b79480b..d927a952cc29 100644 --- a/media/starboard/sbplayer_interface.h +++ b/media/starboard/sbplayer_interface.h @@ -16,6 +16,7 @@ #define MEDIA_STARBOARD_SBPLAYER_INTERFACE_H_ #include "base/time/time.h" +#include "build/build_config.h" #include "media/starboard/buildflags.h" #include "starboard/player.h" @@ -26,9 +27,9 @@ #include "cobalt/media/base/metrics_provider.h" #endif // BUILDFLAG(COBALT_MEDIA_ENABLE_UMA_METRICS) -#if SB_HAS(PLAYER_WITH_URL) -#include SB_URL_PLAYER_INCLUDE_PATH -#endif // SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) +#include "starboard/tvos/shared/media/url_player.h" +#endif // BUILDFLAG(IS_IOS_TVOS) namespace media { @@ -73,7 +74,7 @@ class SbPlayerInterface { virtual SbDecodeTarget GetCurrentFrame(SbPlayer player) = 0; -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) virtual SbPlayer CreateUrlPlayer(const char* url, SbWindow window, SbPlayerStatusFunc player_status_func, @@ -88,7 +89,7 @@ class SbPlayerInterface { virtual void GetUrlPlayerExtraInfo( SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) = 0; -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) virtual bool GetAudioConfiguration( SbPlayer player, @@ -171,7 +172,7 @@ class DefaultSbPlayerInterface final : public SbPlayerInterface { void GetInfo(SbPlayer player, SbPlayerInfo* out_player_info) override; SbDecodeTarget GetCurrentFrame(SbPlayer player) override; -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) SbPlayer CreateUrlPlayer(const char* url, SbWindow window, SbPlayerStatusFunc player_status_func, @@ -184,7 +185,7 @@ class DefaultSbPlayerInterface final : public SbPlayerInterface { void GetUrlPlayerExtraInfo( SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) override; -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) bool GetAudioConfiguration( SbPlayer player, diff --git a/media/starboard/starboard_renderer_unittest.cc b/media/starboard/starboard_renderer_unittest.cc index 7043ceca2a03..fc331aac6c46 100644 --- a/media/starboard/starboard_renderer_unittest.cc +++ b/media/starboard/starboard_renderer_unittest.cc @@ -24,6 +24,7 @@ #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" #include "base/test/task_environment.h" +#include "build/build_config.h" #include "media/base/demuxer_stream.h" #include "media/base/media_util.h" #include "media/base/mock_filters.h" @@ -93,7 +94,7 @@ class MockSbPlayerInterface : public SbPlayerInterface { return kSbDecodeTargetInvalid; } -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) MOCK_METHOD6(CreateUrlPlayer, SbPlayer(const char*, SbWindow, @@ -108,7 +109,7 @@ class MockSbPlayerInterface : public SbPlayerInterface { SbPlayerOutputMode output_mode) override { return true; } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) bool GetAudioConfiguration( SbPlayer player, diff --git a/starboard/nplb/url_player_create_test.cc b/starboard/nplb/url_player_create_test.cc index e813282b6949..51ded3bdf45a 100644 --- a/starboard/nplb/url_player_create_test.cc +++ b/starboard/nplb/url_player_create_test.cc @@ -14,12 +14,13 @@ #include +#include "build/build_config.h" #include "starboard/decode_target.h" #include "starboard/player.h" -#if SB_HAS(PLAYER_WITH_URL) -#include "starboard/shared/uikit/url_player.h" -#endif // SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) +#include "starboard/tvos/shared/media/url_player.h" +#endif // BUILDFLAG(IS_IOS_TVOS) #include "starboard/window.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,7 +28,7 @@ namespace nplb { namespace { -#if SB_HAS(PLAYER_WITH_URL) +#if BUILDFLAG(IS_IOS_TVOS) const char kPlayerUrl[] = "about:blank"; @@ -159,7 +160,7 @@ TEST(SbPlayerUrlTest, MultiPlayer) { SbWindowDestroy(window); } -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // BUILDFLAG(IS_IOS_TVOS) } // namespace } // namespace nplb diff --git a/starboard/tvos/shared/configuration_public.h b/starboard/tvos/shared/configuration_public.h index a3d82b235c01..5592a5d02e0d 100644 --- a/starboard/tvos/shared/configuration_public.h +++ b/starboard/tvos/shared/configuration_public.h @@ -32,12 +32,4 @@ #define SB_IS_WCHAR_T_UTF16 1 #endif -// --- URLPlayer Configuration ------------------------------------------------ - -// Enable URL-based player support (AVPlayer for HLS playback). -#define SB_HAS_PLAYER_WITH_URL 1 - -// Path to the URL player header, used by media/starboard/sbplayer_interface.h. -#define SB_URL_PLAYER_INCLUDE_PATH "starboard/tvos/shared/media/url_player.h" - #endif // STARBOARD_TVOS_SHARED_CONFIGURATION_PUBLIC_H_ From 2f0e1eb2e902e08f95a783b95c5deb556a357c78 Mon Sep 17 00:00:00 2001 From: Abhijeet Kandalkar Date: Fri, 29 May 2026 01:18:57 +0530 Subject: [PATCH 2/2] media: Implement URL player for native HLS on tvOS Add URL player support to StarboardRenderer for native HLS playback. UrlPlayerDemuxer provides placeholder streams for pipeline init while the platform player handles actual playback via SetSourceUrl. Bug: 512045535 --- .../cobalt_content_renderer_client.cc | 19 ++ .../renderer/cobalt_content_renderer_client.h | 6 + .../browser/shell_platform_delegate_ios.mm | 3 +- media/base/demuxer.h | 1 + media/base/media_resource.cc | 6 + media/base/media_resource.h | 9 + .../starboard/starboard_renderer_client.cc | 25 ++- .../starboard_renderer_client_unittest.cc | 1 + media/mojo/mojom/media_types.mojom | 1 + .../mojom/media_types_enum_mojom_traits.h | 9 +- media/mojo/mojom/renderer_extensions.mojom | 3 + .../starboard/starboard_renderer_wrapper.cc | 8 + .../starboard/starboard_renderer_wrapper.h | 1 + media/starboard/BUILD.gn | 6 + media/starboard/starboard_renderer.cc | 167 +++++++++++++----- media/starboard/starboard_renderer.h | 16 ++ media/starboard/url_player_demuxer.cc | 150 ++++++++++++++++ media/starboard/url_player_demuxer.h | 99 +++++++++++ tools/metrics/histograms/enums.xml | 1 + 19 files changed, 485 insertions(+), 46 deletions(-) create mode 100644 media/starboard/url_player_demuxer.cc create mode 100644 media/starboard/url_player_demuxer.h diff --git a/cobalt/renderer/cobalt_content_renderer_client.cc b/cobalt/renderer/cobalt_content_renderer_client.cc index 77d442497d2b..7e5d03ce1901 100644 --- a/cobalt/renderer/cobalt_content_renderer_client.cc +++ b/cobalt/renderer/cobalt_content_renderer_client.cc @@ -20,6 +20,7 @@ #include "base/task/bind_post_task.h" #include "base/time/time.h" +#include "build/build_config.h" #include "cobalt/media/service/mojom/platform_window_provider.mojom.h" #include "cobalt/renderer/cobalt_render_frame_observer.h" #include "cobalt/shell/common/url_constants.h" @@ -46,6 +47,10 @@ #include "third_party/blink/public/web/web_view.h" #include "ui/gfx/geometry/size_conversions.h" +#if BUILDFLAG(IS_IOS_TVOS) +#include "media/starboard/url_player_demuxer.h" +#endif // BUILDFLAG(IS_IOS_TVOS) + namespace cobalt { namespace { @@ -368,4 +373,18 @@ void CobaltContentRendererClient::PostSandboxInitialized() { } } +std::unique_ptr<::media::Demuxer> +CobaltContentRendererClient::OverrideDemuxerForUrl( + content::RenderFrame* render_frame, + const GURL& url, + scoped_refptr task_runner) { +#if BUILDFLAG(IS_IOS_TVOS) + if (::media::IsHlsUrl(url)) { + return std::make_unique<::media::UrlPlayerDemuxer>(std::move(task_runner), + url); + } +#endif // BUILDFLAG(IS_IOS_TVOS) + return nullptr; +} + } // namespace cobalt diff --git a/cobalt/renderer/cobalt_content_renderer_client.h b/cobalt/renderer/cobalt_content_renderer_client.h index 020d63ac6f35..11502047e1dc 100644 --- a/cobalt/renderer/cobalt_content_renderer_client.h +++ b/cobalt/renderer/cobalt_content_renderer_client.h @@ -16,6 +16,7 @@ #define COBALT_RENDERER_COBALT_CONTENT_RENDERER_CLIENT_H_ #include +#include #include "base/functional/callback.h" #include "base/memory/weak_ptr.h" @@ -27,6 +28,7 @@ #include "cobalt/media/audio/cobalt_audio_device_factory.h" #include "cobalt/media/service/mojom/platform_window_provider.mojom.h" #include "content/public/renderer/content_renderer_client.h" +#include "media/base/demuxer.h" #include "media/base/key_systems_support_registration.h" #include "media/base/starboard/renderer_factory_traits.h" #include "mojo/public/cpp/bindings/remote.h" @@ -74,6 +76,10 @@ class CobaltContentRendererClient : public content::ContentRendererClient { void GetStarboardRendererFactoryTraits( ::media::RendererFactoryTraits* traits) override; void PostSandboxInitialized() override; + std::unique_ptr<::media::Demuxer> OverrideDemuxerForUrl( + content::RenderFrame* render_frame, + const GURL& url, + scoped_refptr task_runner) override; uint64_t GetSbWindowHandle() const { return sb_window_handle_; } diff --git a/cobalt/shell/browser/shell_platform_delegate_ios.mm b/cobalt/shell/browser/shell_platform_delegate_ios.mm index 46fe1b562875..0fb6dcbd7520 100644 --- a/cobalt/shell/browser/shell_platform_delegate_ios.mm +++ b/cobalt/shell/browser/shell_platform_delegate_ios.mm @@ -365,7 +365,8 @@ - (void)viewDidLoad { // Note that the actual size and and position of this view are irrelevant at // this point: it will be changed in starboard's // AVSBVideoRenderer::SetBounds() when necessary. - UIView* playerContainerView = [[UIView alloc] init]; + UIView* playerContainerView = + [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; playerContainerView.accessibilityIdentifier = @"Player Container"; [_contentView addSubview:playerContainerView]; [SBDGetApplication() setPlayerContainerView:playerContainerView]; diff --git a/media/base/demuxer.h b/media/base/demuxer.h index 6d4512496f23..c21cdec91918 100644 --- a/media/base/demuxer.h +++ b/media/base/demuxer.h @@ -38,6 +38,7 @@ enum class DemuxerType { kFrameInjectingDemuxer = 5, kStreamProviderDemuxer = 6, kManifestDemuxer = 7, + kUrlPlayerDemuxer = 8, // URL player placeholder demuxer. }; class MEDIA_EXPORT DemuxerHost { diff --git a/media/base/media_resource.cc b/media/base/media_resource.cc index deeadbbf89b2..4200e62ec3fc 100644 --- a/media/base/media_resource.cc +++ b/media/base/media_resource.cc @@ -25,4 +25,10 @@ DemuxerStream* MediaResource::GetFirstStream(DemuxerStream::Type type) { return nullptr; } +#if BUILDFLAG(IS_IOS_TVOS) +GURL MediaResource::GetMediaUrl() const { + return GURL(); +} +#endif // BUILDFLAG(IS_IOS_TVOS) + } // namespace media diff --git a/media/base/media_resource.h b/media/base/media_resource.h index 62e0b605df2f..2b4a88f88d86 100644 --- a/media/base/media_resource.h +++ b/media/base/media_resource.h @@ -9,8 +9,12 @@ #include "base/memory/raw_ptr.h" #include "base/time/time.h" +#include "build/build_config.h" #include "media/base/demuxer_stream.h" #include "media/base/media_export.h" +#if BUILDFLAG(IS_IOS_TVOS) +#include "url/gurl.h" +#endif // BUILDFLAG(IS_IOS_TVOS) namespace media { @@ -41,6 +45,11 @@ class MEDIA_EXPORT MediaResource { // A helper function that return the first stream of the given `type` if one // exists or a null pointer if there is no streams of that type. DemuxerStream* GetFirstStream(DemuxerStream::Type type); + +#if BUILDFLAG(IS_IOS_TVOS) + // Returns the media URL for URL player. + virtual GURL GetMediaUrl() const; +#endif // BUILDFLAG(IS_IOS_TVOS) }; } // namespace media diff --git a/media/mojo/clients/starboard/starboard_renderer_client.cc b/media/mojo/clients/starboard/starboard_renderer_client.cc index 6a85a2f1c728..064b236859f2 100644 --- a/media/mojo/clients/starboard/starboard_renderer_client.cc +++ b/media/mojo/clients/starboard/starboard_renderer_client.cc @@ -20,6 +20,7 @@ #include "base/functional/bind.h" #include "base/time/time.h" #include "base/unguessable_token.h" +#include "build/build_config.h" #include "media/base/media_log.h" #include "media/base/media_resource.h" #include "media/base/media_switches.h" @@ -30,6 +31,10 @@ #include "mojo/public/cpp/bindings/callback_helpers.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#if BUILDFLAG(IS_IOS_TVOS) +#include "url/gurl.h" +#endif // BUILDFLAG(IS_IOS_TVOS) + #if BUILDFLAG(IS_ANDROID) #include "base/task/bind_post_task.h" #endif // BUILDFLAG(IS_ANDROID) @@ -351,8 +356,18 @@ void StarboardRendererClient::InitializeMojoRenderer( DCHECK(media_task_runner_->RunsTasksInCurrentSequence()); DCHECK(AreMojoPipesConnected()); - if (base::FeatureList::IsEnabled(kCobaltBypassMojoForMedia) || - bypass_mojo_for_media_) { + // URL player is incompatible with the bypass bridge because the bypass + // bridge proxies demuxer streams, while the URL player delegates playback + // entirely to the native platform player. + bool is_url_player = false; +#if BUILDFLAG(IS_IOS_TVOS) + GURL url = media_resource->GetMediaUrl(); + is_url_player = url.is_valid(); +#endif // BUILDFLAG(IS_IOS_TVOS) + + if (!is_url_player && + (base::FeatureList::IsEnabled(kCobaltBypassMojoForMedia) || + bypass_mojo_for_media_)) { bypass_bridge_ = base::MakeRefCounted( media_task_runner_, base::BindRepeating(&StarboardRendererClient::OnTimeUpdateFromBridge, @@ -378,6 +393,12 @@ void StarboardRendererClient::InitializeMojoRenderer( return; } +#if BUILDFLAG(IS_IOS_TVOS) + if (is_url_player) { + renderer_extension_->SetSourceUrl(url.spec()); + } +#endif // BUILDFLAG(IS_IOS_TVOS) + MojoRendererWrapper::Initialize(media_resource, client, std::move(init_cb)); } diff --git a/media/mojo/clients/starboard/starboard_renderer_client_unittest.cc b/media/mojo/clients/starboard/starboard_renderer_client_unittest.cc index 942d4732bad2..03c9d0a6cbf5 100644 --- a/media/mojo/clients/starboard/starboard_renderer_client_unittest.cc +++ b/media/mojo/clients/starboard/starboard_renderer_client_unittest.cc @@ -96,6 +96,7 @@ class FakeStarboardRendererExtension #endif // BUILDFLAG(IS_ANDROID) void OnGpuChannelTokenReady( mojom::CommandBufferIdPtr command_buffer_id) override {} + void SetSourceUrl(const std::string& source_url) override {} private: FakeMojomRendererCallRecord* record_ = nullptr; diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom index d649d9e890a2..26d9cfe8f034 100644 --- a/media/mojo/mojom/media_types.mojom +++ b/media/mojo/mojom/media_types.mojom @@ -662,6 +662,7 @@ enum DemuxerType { kFrameInjectingDemuxer = 5, kStreamProviderDemuxer = 6, kManifestDemuxer = 7, + kUrlPlayerDemuxer = 8, // URL player placeholder demuxer. }; // See media::CreateCdmStatus diff --git a/media/mojo/mojom/media_types_enum_mojom_traits.h b/media/mojo/mojom/media_types_enum_mojom_traits.h index 20563e88d10e..b66a10917676 100644 --- a/media/mojo/mojom/media_types_enum_mojom_traits.h +++ b/media/mojo/mojom/media_types_enum_mojom_traits.h @@ -391,7 +391,7 @@ struct EnumTraits { #if BUILDFLAG(USE_STARBOARD_MEDIA) case ::media::RendererType::kStarboard: return media::mojom::RendererType::kStarboard; -#endif // BUILDFLAG(USE_STARBOARD_MEDIA) +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) } NOTREACHED(); @@ -436,7 +436,7 @@ struct EnumTraits { case media::mojom::RendererType::kStarboard: *output = ::media::RendererType::kStarboard; return true; -#endif // BUILDFLAG(USE_STARBOARD_MEDIA) +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) } NOTREACHED(); @@ -461,6 +461,8 @@ struct EnumTraits { return media::mojom::DemuxerType::kStreamProviderDemuxer; case ::media::DemuxerType::kManifestDemuxer: return media::mojom::DemuxerType::kManifestDemuxer; + case ::media::DemuxerType::kUrlPlayerDemuxer: + return media::mojom::DemuxerType::kUrlPlayerDemuxer; } NOTREACHED(); @@ -492,6 +494,9 @@ struct EnumTraits { case media::mojom::DemuxerType::kManifestDemuxer: *output = ::media::DemuxerType::kManifestDemuxer; return true; + case media::mojom::DemuxerType::kUrlPlayerDemuxer: + *output = ::media::DemuxerType::kUrlPlayerDemuxer; + return true; } NOTREACHED(); diff --git a/media/mojo/mojom/renderer_extensions.mojom b/media/mojo/mojom/renderer_extensions.mojom index d61f091311e8..8ee721606d50 100644 --- a/media/mojo/mojom/renderer_extensions.mojom +++ b/media/mojo/mojom/renderer_extensions.mojom @@ -151,6 +151,9 @@ interface StarboardRendererExtension { // Initialize media bypass bridge in single-process mode. InitializeWithBypassBridge(uint32 bypass_bridge_id) => (bool success); + // Pass source URL for URL player. + SetSourceUrl(string source_url); + [EnableIf=is_android] // Notify StarboardRendererWrapper when the current OverlayInfo changes. OnOverlayInfoChanged(OverlayInfo overlay_info); diff --git a/media/mojo/services/starboard/starboard_renderer_wrapper.cc b/media/mojo/services/starboard/starboard_renderer_wrapper.cc index d3a4b7e1e16d..b13fca48cfcb 100644 --- a/media/mojo/services/starboard/starboard_renderer_wrapper.cc +++ b/media/mojo/services/starboard/starboard_renderer_wrapper.cc @@ -20,6 +20,7 @@ #include "base/functional/callback_helpers.h" #include "base/task/bind_post_task.h" #include "base/time/time.h" +#include "build/build_config.h" #include "media/base/demuxer_stream.h" #include "media/base/media_resource.h" #include "media/base/starboard/starboard_rendering_mode.h" @@ -515,6 +516,13 @@ void StarboardRendererWrapper::InitializeWithBypassBridge( std::move(callback).Run(true); } +void StarboardRendererWrapper::SetSourceUrl(const std::string& source_url) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +#if BUILDFLAG(IS_IOS_TVOS) + GetRenderer()->SetSourceUrl(source_url); +#endif // BUILDFLAG(IS_IOS_TVOS) +} + #if BUILDFLAG(IS_ANDROID) void StarboardRendererWrapper::OnOverlayInfoChanged( const OverlayInfo& overlay_info) { diff --git a/media/mojo/services/starboard/starboard_renderer_wrapper.h b/media/mojo/services/starboard/starboard_renderer_wrapper.h index cd36eda0d43d..6991978c03a4 100644 --- a/media/mojo/services/starboard/starboard_renderer_wrapper.h +++ b/media/mojo/services/starboard/starboard_renderer_wrapper.h @@ -104,6 +104,7 @@ class StarboardRendererWrapper void InitializeWithBypassBridge( uint32_t bypass_bridge_id, InitializeWithBypassBridgeCallback callback) override; + void SetSourceUrl(const std::string& source_url) override; #if BUILDFLAG(IS_ANDROID) void OnOverlayInfoChanged(const OverlayInfo& overlay_info) override; #endif // BUILDFLAG(IS_ANDROID) diff --git a/media/starboard/BUILD.gn b/media/starboard/BUILD.gn index 7bb524b3251b..d56ab6a69434 100644 --- a/media/starboard/BUILD.gn +++ b/media/starboard/BUILD.gn @@ -60,6 +60,12 @@ source_set("starboard") { ] deps += [ ":buildflags" ] + if (is_ios && target_platform == "tvos") { + sources += [ + "url_player_demuxer.cc", + "url_player_demuxer.h", + ] + } } deps += [ diff --git a/media/starboard/starboard_renderer.cc b/media/starboard/starboard_renderer.cc index 73016064a46b..283be791a492 100644 --- a/media/starboard/starboard_renderer.cc +++ b/media/starboard/starboard_renderer.cc @@ -213,6 +213,16 @@ void StarboardRenderer::Initialize(MediaResource* media_resource, client_ = client; init_cb_ = std::move(init_cb); + if (IsUrlPlayer()) { + state_ = STATE_INITIALIZING; + if (get_sb_window_handle_cb_) { + get_sb_window_handle_cb_.Run(); + return; + } + CreatePlayerBridge(); + return; + } + audio_stream_ = media_resource->GetFirstStream(DemuxerStream::AUDIO); video_stream_ = media_resource->GetFirstStream(DemuxerStream::VIDEO); @@ -530,6 +540,52 @@ void StarboardRenderer::OnSbWindowHandleReady(const uint64_t sb_window_handle) { CreatePlayerBridge(); } +bool StarboardRenderer::IsUrlPlayer() const { +#if BUILDFLAG(IS_IOS_TVOS) + return !source_url_.empty(); +#else + return false; +#endif +} + +void StarboardRenderer::OnUrlPlayerPresenting() { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); +#if BUILDFLAG(IS_IOS_TVOS) + if (!player_bridge_) { + return; + } + int width = 0, height = 0; + player_bridge_->GetVideoResolution(&width, &height); + if (width > 0 && height > 0) { + gfx::Size size(width, height); + client_->OnVideoNaturalSizeChange(size); + paint_video_hole_frame_cb_.Run(size); + } else { + LOG(WARNING) << "Platform player reported invalid dimensions (" << width + << "x" << height + << ") at presenting; skipping video hole update."; + } + + // Re-apply playback rate; the platform player ignores rate changes + // before it is ready to play. + player_bridge_->SetPlaybackRate(playback_rate_); +#endif // BUILDFLAG(IS_IOS_TVOS) +} + +#if BUILDFLAG(IS_IOS_TVOS) +void StarboardRenderer::SetSourceUrl(const std::string& source_url) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + source_url_ = source_url; +} + +void StarboardRenderer::OnEncryptedMediaInitDataEncountered( + const char* init_data_type, + const unsigned char* init_data, + unsigned int init_data_length) { + // TODO: Forward encrypted media init data to the EME/DRM layer. +} +#endif // BUILDFLAG(IS_IOS_TVOS) + #if BUILDFLAG(IS_ANDROID) void StarboardRenderer::OnOverlayInfoChanged(const OverlayInfo& overlay_info) { bool overlay_changed = !overlay_info_.RefersToSameOverlayAs(overlay_info); @@ -582,7 +638,7 @@ void StarboardRenderer::CreatePlayerBridge() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); DCHECK(init_cb_); DCHECK_EQ(state_, STATE_INITIALIZING); - DCHECK(audio_stream_ || video_stream_); + DCHECK(audio_stream_ || video_stream_ || IsUrlPlayer()); TRACE_EVENT0("media", "StarboardRenderer::CreatePlayerBridge"); @@ -622,27 +678,43 @@ void StarboardRenderer::CreatePlayerBridge() { // number of active players. player_bridge_.reset(); - LOG(INFO) << "Creating SbPlayerBridge."; - - player_bridge_.reset(new SbPlayerBridge( - GetSbPlayerInterface(), task_runner_, - get_decode_target_graphics_context_provider_func_, audio_config, - audio_mime_type, video_config, video_mime_type, - // TODO(b/326497953): Support suspend/resume. - // TODO(b/326508279): Support background mode. - sb_window_, drm_system_, this, - // TODO(b/326497953): Support suspend/resume. - false, - // TODO(b/326825450): Revisit 360 videos. - kSbPlayerOutputModeInvalid, max_video_capabilities_, - // TODO(b/326654546): Revisit HTMLVideoElement.setMaxVideoInputSize. - /*max_video_input_size=*/-1, experimental_features_ + if (IsUrlPlayer()) { +#if BUILDFLAG(IS_IOS_TVOS) + player_bridge_.reset(new SbPlayerBridge( + GetSbPlayerInterface(), task_runner_, source_url_, sb_window_, this, + /*allow_resume_after_suspend=*/false, kSbPlayerOutputModePunchOut, + base::BindRepeating( + &StarboardRenderer::OnEncryptedMediaInitDataEncountered, + base::Unretained(this)) +#if BUILDFLAG(COBALT_MEDIA_ENABLE_CVAL) + , + /*pipeline_identifier=*/"" +#endif // BUILDFLAG(COBALT_MEDIA_ENABLE_CVAL) + )); +#endif // BUILDFLAG(IS_IOS_TVOS) + } else { + LOG(INFO) << "Creating SbPlayerBridge."; + + player_bridge_.reset(new SbPlayerBridge( + GetSbPlayerInterface(), task_runner_, + get_decode_target_graphics_context_provider_func_, audio_config, + audio_mime_type, video_config, video_mime_type, + // TODO(b/326497953): Support suspend/resume. + // TODO(b/326508279): Support background mode. + sb_window_, drm_system_, this, + // TODO(b/326497953): Support suspend/resume. + false, + // TODO(b/326825450): Revisit 360 videos. + kSbPlayerOutputModeInvalid, max_video_capabilities_, + // TODO(b/326654546): Revisit HTMLVideoElement.setMaxVideoInputSize. + /*max_video_input_size=*/-1, experimental_features_ #if BUILDFLAG(IS_ANDROID) - , - // TODO: b/475294958 - Revisit platform-specific codes above starboard. - surface_view_ + , + // TODO: b/475294958 - Revisit platform-specific codes above starboard. + surface_view_ #endif // BUILDFLAG(IS_ANDROID) - )); + )); + } if (!player_bridge_->IsValid()) { error_message = player_bridge_->GetPlayerCreationErrorMessage(); @@ -661,17 +733,19 @@ void StarboardRenderer::CreatePlayerBridge() { return; } - // TODO(b/267678497): When `player_bridge_->GetAudioConfigurations()` - // returns no audio configurations, update the write durations again - // before the SbPlayer reaches `kSbPlayerStatePresenting`. - audio_write_duration_for_preroll_ = audio_write_duration_ = - HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) - ? audio_write_duration_remote_ - : audio_write_duration_local_; - LOG(INFO) << "SbPlayerBridge created, with audio write duration at " - << audio_write_duration_for_preroll_ - << " and with max_video_capabilities_ at " - << max_video_capabilities_; + if (!IsUrlPlayer()) { + // TODO(b/267678497): When `player_bridge_->GetAudioConfigurations()` + // returns no audio configurations, update the write durations again + // before the SbPlayer reaches `kSbPlayerStatePresenting`. + audio_write_duration_for_preroll_ = audio_write_duration_ = + HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) + ? audio_write_duration_remote_ + : audio_write_duration_local_; + LOG(INFO) << "SbPlayerBridge created, with audio write duration at " + << audio_write_duration_for_preroll_ + << " and with max_video_capabilities_ at " + << max_video_capabilities_; + } ApplyPendingBounds(); @@ -690,11 +764,13 @@ void StarboardRenderer::CreatePlayerBridge() { break; } - if (audio_stream_) { - UpdateDecoderConfig(audio_stream_); - } - if (video_stream_) { - UpdateDecoderConfig(video_stream_); + if (!IsUrlPlayer()) { + if (audio_stream_) { + UpdateDecoderConfig(audio_stream_); + } + if (video_stream_) { + UpdateDecoderConfig(video_stream_); + } } player_bridge_->SetVolume(volume_); @@ -858,6 +934,11 @@ void StarboardRenderer::OnNeedData(DemuxerStream::Type type, return; } + // URL player handles all buffering natively so ignore OnNeedData. + if (IsUrlPlayer()) { + return; + } + int max_buffers = std::min(max_number_of_buffers_to_write, max_samples_per_write_); @@ -984,11 +1065,15 @@ void StarboardRenderer::OnPlayerStatus(SbPlayerState state) { FROM_HERE, base::BindOnce(&StarboardRenderer::OnBufferingStateChange, weak_factory_.GetWeakPtr(), buffering_state_)); - audio_write_duration_for_preroll_ = audio_write_duration_ = - HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) - ? audio_write_duration_remote_ - : audio_write_duration_local_; - LOG(INFO) << "Audio write duration is " << audio_write_duration_; + if (IsUrlPlayer()) { + OnUrlPlayerPresenting(); + } else { + audio_write_duration_for_preroll_ = audio_write_duration_ = + HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) + ? audio_write_duration_remote_ + : audio_write_duration_local_; + LOG(INFO) << "Audio write duration is " << audio_write_duration_; + } break; case kSbPlayerStateEndOfStream: client_->OnEnded(); diff --git a/media/starboard/starboard_renderer.h b/media/starboard/starboard_renderer.h index 03ad7cebd201..af4ef37dae21 100644 --- a/media/starboard/starboard_renderer.h +++ b/media/starboard/starboard_renderer.h @@ -22,6 +22,7 @@ #include "base/memory/scoped_refptr.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" +#include "build/build_config.h" #include "media/base/cdm_context.h" #include "media/base/decoder_buffer.h" #include "media/base/demuxer_stream.h" @@ -122,6 +123,12 @@ class MEDIA_EXPORT StarboardRenderer : public Renderer, void OnVideoGeometryChange(const gfx::Rect& output_rect); void OnSbWindowHandleReady(const uint64_t sb_window_handle); +#if BUILDFLAG(IS_IOS_TVOS) + void SetSourceUrl(const std::string& source_url); + void OnEncryptedMediaInitDataEncountered(const char* init_data_type, + const unsigned char* init_data, + unsigned int init_data_length); +#endif // BUILDFLAG(IS_IOS_TVOS) #if BUILDFLAG(IS_ANDROID) void OnOverlayInfoChanged(const OverlayInfo& overlay_info); #endif // BUILDFLAG(IS_ANDROID) @@ -150,6 +157,12 @@ class MEDIA_EXPORT StarboardRenderer : public Renderer, STATE_ERROR }; + // Returns true when the renderer is operating in URL player mode. + bool IsUrlPlayer() const; + // Handles presenting state for URL player: propagates video resolution + // for hole-punch rendering and re-applies playback rate. + void OnUrlPlayerPresenting(); + void CreatePlayerBridge(); void ApplyPendingBounds(); void UpdateDecoderConfig(DemuxerStream* stream); @@ -200,6 +213,9 @@ class MEDIA_EXPORT StarboardRenderer : public Renderer, const TimeDelta audio_write_duration_local_; const TimeDelta audio_write_duration_remote_; const std::string max_video_capabilities_; +#if BUILDFLAG(IS_IOS_TVOS) + std::string source_url_; +#endif // BUILDFLAG(IS_IOS_TVOS) const StarboardRendererConfig::ExperimentalFeatures experimental_features_; // TODO: b/375674101 - Connect this to h5vcc setting. const int max_samples_per_write_; diff --git a/media/starboard/url_player_demuxer.cc b/media/starboard/url_player_demuxer.cc new file mode 100644 index 000000000000..bf3259347267 --- /dev/null +++ b/media/starboard/url_player_demuxer.cc @@ -0,0 +1,150 @@ +// Copyright 2026 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "media/starboard/url_player_demuxer.h" + +#include +#include + +#include "base/functional/bind.h" +#include "base/logging.h" +#include "base/task/sequenced_task_runner.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/channel_layout.h" +#include "media/base/decoder_buffer.h" +#include "media/base/media_track.h" +#include "media/base/sample_format.h" +#include "media/base/video_decoder_config.h" + +namespace media { + +bool IsHlsUrl(const GURL& url) { + auto path = url.path_piece(); + return path.ends_with(".m3u8") || + path.find("hls_variant") != std::string_view::npos; +} + +UrlPlayerDemuxerStream::UrlPlayerDemuxerStream(Type type) : type_(type) {} + +UrlPlayerDemuxerStream::~UrlPlayerDemuxerStream() = default; + +void UrlPlayerDemuxerStream::Read(uint32_t count, ReadCB read_cb) { + NOTREACHED(); +} + +AudioDecoderConfig UrlPlayerDemuxerStream::audio_decoder_config() { + return AudioDecoderConfig(AudioCodec::kAAC, kSampleFormatS16, + CHANNEL_LAYOUT_STEREO, + /*samples_per_second=*/44100, /*extra_data=*/{}, + EncryptionScheme::kUnencrypted); +} + +VideoDecoderConfig UrlPlayerDemuxerStream::video_decoder_config() { + static const gfx::Size kPlaceholderSize(1, 1); + return VideoDecoderConfig( + VideoCodec::kH264, VideoCodecProfile::H264PROFILE_BASELINE, + VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(), + kNoTransformation, kPlaceholderSize, gfx::Rect(kPlaceholderSize), + kPlaceholderSize, /*extra_data=*/{}, EncryptionScheme::kUnencrypted); +} + +DemuxerStream::Type UrlPlayerDemuxerStream::type() const { + return type_; +} + +bool UrlPlayerDemuxerStream::SupportsConfigChanges() { + return false; +} + +UrlPlayerDemuxer::UrlPlayerDemuxer( + scoped_refptr media_task_runner, + GURL url) + : media_task_runner_(std::move(media_task_runner)), url_(std::move(url)) { + DCHECK(media_task_runner_); +} + +UrlPlayerDemuxer::~UrlPlayerDemuxer() = default; + +std::vector UrlPlayerDemuxer::GetAllStreams() { + return {&audio_stream_, &video_stream_}; +} + +GURL UrlPlayerDemuxer::GetMediaUrl() const { + return url_; +} + +std::string UrlPlayerDemuxer::GetDisplayName() const { + return "UrlPlayerDemuxer"; +} + +DemuxerType UrlPlayerDemuxer::GetDemuxerType() const { + return DemuxerType::kUrlPlayerDemuxer; +} + +void UrlPlayerDemuxer::Initialize(DemuxerHost* host, + PipelineStatusCallback status_cb) { + DVLOG(1) << __func__; + host_ = host; + media_task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(status_cb), PIPELINE_OK)); +} + +void UrlPlayerDemuxer::AbortPendingReads() {} +void UrlPlayerDemuxer::StartWaitingForSeek(base::TimeDelta seek_time) {} +void UrlPlayerDemuxer::CancelPendingSeek(base::TimeDelta seek_time) {} + +void UrlPlayerDemuxer::Seek(base::TimeDelta time, + PipelineStatusCallback status_cb) { + DVLOG(1) << __func__ << "(" << time << ")"; + media_task_runner_->PostTask( + FROM_HERE, base::BindOnce(std::move(status_cb), PIPELINE_OK)); +} + +// While the demuxer itself is not seekable, the underlying URL player is. +bool UrlPlayerDemuxer::IsSeekable() const { + return true; +} + +void UrlPlayerDemuxer::Stop() {} + +base::TimeDelta UrlPlayerDemuxer::GetStartTime() const { + return base::TimeDelta(); +} + +base::Time UrlPlayerDemuxer::GetTimelineOffset() const { + return base::Time(); +} + +int64_t UrlPlayerDemuxer::GetMemoryUsage() const { + return 0; +} + +std::optional +UrlPlayerDemuxer::GetContainerForMetrics() const { + return std::nullopt; +} + +void UrlPlayerDemuxer::OnTracksChanged( + DemuxerStream::Type track_type, + const std::vector& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) { + std::vector streams; + std::move(change_completed_cb).Run(streams); + DLOG(WARNING) << "Track changes are not supported by UrlPlayerDemuxer."; +} + +void UrlPlayerDemuxer::SetPlaybackRate(double rate) {} + +} // namespace media diff --git a/media/starboard/url_player_demuxer.h b/media/starboard/url_player_demuxer.h new file mode 100644 index 000000000000..2cd0e8779e43 --- /dev/null +++ b/media/starboard/url_player_demuxer.h @@ -0,0 +1,99 @@ +// Copyright 2026 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MEDIA_STARBOARD_URL_PLAYER_DEMUXER_H_ +#define MEDIA_STARBOARD_URL_PLAYER_DEMUXER_H_ + +#include +#include +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/task/sequenced_task_runner.h" +#include "media/base/demuxer.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_export.h" +#include "url/gurl.h" + +namespace media { + +// Returns true if the URL looks like an HLS stream that the URL player +// should handle. +MEDIA_EXPORT bool IsHlsUrl(const GURL& url); + +// Placeholder stream to satisfy the stream-based pipeline initialization. +// Owned by UrlPlayerDemuxer. Created and accessed on the media thread. +class MEDIA_EXPORT UrlPlayerDemuxerStream : public DemuxerStream { + public: + explicit UrlPlayerDemuxerStream(Type type); + ~UrlPlayerDemuxerStream() override; + + // DemuxerStream implementation. + void Read(uint32_t count, ReadCB read_cb) override; + AudioDecoderConfig audio_decoder_config() override; + VideoDecoderConfig video_decoder_config() override; + Type type() const override; + bool SupportsConfigChanges() override; + + private: + const Type type_; +}; + +// Demuxer placeholder for URL player. Carries the media URL and exposes +// placeholder streams required by the stream-based pipeline initialization. +// Owned by PipelineImpl. Created and accessed on the media thread. +class MEDIA_EXPORT UrlPlayerDemuxer : public Demuxer { + public: + UrlPlayerDemuxer(scoped_refptr media_task_runner, + GURL url); + ~UrlPlayerDemuxer() override; + + // MediaResource implementation. + std::vector GetAllStreams() override; + GURL GetMediaUrl() const override; + + // Demuxer implementation. + std::string GetDisplayName() const override; + DemuxerType GetDemuxerType() const override; + void Initialize(DemuxerHost* host, PipelineStatusCallback status_cb) override; + void AbortPendingReads() override; + void StartWaitingForSeek(base::TimeDelta seek_time) override; + void CancelPendingSeek(base::TimeDelta seek_time) override; + void Seek(base::TimeDelta time, PipelineStatusCallback status_cb) override; + bool IsSeekable() const override; + void Stop() override; + base::TimeDelta GetStartTime() const override; + base::Time GetTimelineOffset() const override; + int64_t GetMemoryUsage() const override; + std::optional GetContainerForMetrics() + const override; + void OnTracksChanged(DemuxerStream::Type track_type, + const std::vector& track_ids, + base::TimeDelta curr_time, + TrackChangeCB change_completed_cb) override; + void SetPlaybackRate(double rate) override; + + private: + scoped_refptr media_task_runner_; + raw_ptr host_ = nullptr; + const GURL url_; + UrlPlayerDemuxerStream audio_stream_{DemuxerStream::AUDIO}; + UrlPlayerDemuxerStream video_stream_{DemuxerStream::VIDEO}; +}; + +} // namespace media + +#endif // MEDIA_STARBOARD_URL_PLAYER_DEMUXER_H_ diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 0c8b02df7e97..a382e43ebffd 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml @@ -19911,6 +19911,7 @@ from previous Chrome versions. +