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/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.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/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/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/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_ 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. +