// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromecast/renderer/cast_content_renderer_client.h" #include #include "base/command_line.h" #include "base/optional.h" #include "base/strings/string_number_conversions.h" #include "chromecast/base/bitstream_audio_codecs.h" #include "chromecast/base/cast_features.h" #include "chromecast/base/chromecast_switches.h" #include "chromecast/crash/app_state_tracker.h" #include "chromecast/media/base/media_codec_support.h" #include "chromecast/media/base/supported_codec_profile_levels_memo.h" #include "chromecast/public/media/media_capabilities_shlib.h" #include "chromecast/renderer/cast_url_loader_throttle_provider.h" #include "chromecast/renderer/cast_websocket_handshake_throttle_provider.h" #include "chromecast/renderer/identification_settings_manager.h" #include "chromecast/renderer/js_channel_bindings.h" #include "chromecast/renderer/media/key_systems_cast.h" #include "chromecast/renderer/media/media_caps_observer_impl.h" #include "components/media_control/renderer/media_playback_options.h" #include "components/network_hints/renderer/web_prescient_networking_impl.h" #include "components/on_load_script_injector/renderer/on_load_script_injector.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "media/base/audio_parameters.h" #include "media/base/media.h" #include "media/remoting/receiver_controller.h" #include "media/remoting/remoting_constants.h" #include "media/remoting/stream_provider.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/web_runtime_features.h" #include "third_party/blink/public/web/web_frame_widget.h" #include "third_party/blink/public/web/web_security_policy.h" #include "third_party/blink/public/web/web_settings.h" #include "third_party/blink/public/web/web_view.h" #if defined(OS_ANDROID) #include "chromecast/media/audio/cast_audio_device_factory.h" #include "media/base/android/media_codec_util.h" #else #include "chromecast/renderer/memory_pressure_observer_impl.h" #endif // OS_ANDROID #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS) #include "chromecast/common/cast_extensions_client.h" #include "chromecast/renderer/cast_extensions_renderer_client.h" #include "content/public/common/content_constants.h" #include "extensions/common/common_manifest_handlers.h" // nogncheck #include "extensions/common/extension_urls.h" // nogncheck #include "extensions/renderer/dispatcher.h" // nogncheck #include "extensions/renderer/extension_frame_helper.h" // nogncheck #include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h" // nogncheck #endif namespace chromecast { namespace shell { namespace { bool IsSupportedBitstreamAudioCodecHelper(::media::AudioCodec codec, int mask) { return (codec == ::media::kCodecAC3 && (kBitstreamAudioCodecAc3 & mask)) || (codec == ::media::kCodecEAC3 && (kBitstreamAudioCodecEac3 & mask)) || (codec == ::media::kCodecMpegHAudio && (kBitstreamAudioCodecMpegHAudio & mask)); } } // namespace #if defined(OS_ANDROID) // Audio renderer algorithm maximum capacity. constexpr base::TimeDelta kAudioRendererMaxCapacity = base::TimeDelta::FromSeconds(10); // Audio renderer algorithm starting capacity. Configure large enough to // prevent underrun. constexpr base::TimeDelta kAudioRendererStartingCapacity = base::TimeDelta::FromMilliseconds(5000); constexpr base::TimeDelta kAudioRendererStartingCapacityEncrypted = base::TimeDelta::FromMilliseconds(5500); #endif // defined(OS_ANDROID) CastContentRendererClient::CastContentRendererClient() : supported_profiles_( std::make_unique()), activity_url_filter_manager_( std::make_unique()) { #if defined(OS_ANDROID) DCHECK(::media::MediaCodecUtil::IsMediaCodecAvailable()) << "MediaCodec is not available!"; // Platform decoder support must be enabled before we set the // IsCodecSupportedCB because the latter instantiates the lazy MimeUtil // instance, which caches the platform decoder supported state when it is // constructed. ::media::EnablePlatformDecoderSupport(); // Registers a custom content::AudioDeviceFactory cast_audio_device_factory_ = std::make_unique(); #endif // OS_ANDROID } CastContentRendererClient::~CastContentRendererClient() = default; void CastContentRendererClient::RenderThreadStarted() { // Register as observer for media capabilities content::RenderThread* thread = content::RenderThread::Get(); mojo::Remote media_caps; thread->BindHostReceiver(media_caps.BindNewPipeAndPassReceiver()); mojo::PendingRemote proxy; media_caps_observer_.reset( new media::MediaCapsObserverImpl(&proxy, supported_profiles_.get())); media_caps->AddObserver(std::move(proxy)); #if !defined(OS_ANDROID) // Register to observe memory pressure changes mojo::Remote memory_pressure_controller; thread->BindHostReceiver( memory_pressure_controller.BindNewPipeAndPassReceiver()); mojo::PendingRemote memory_pressure_proxy; memory_pressure_observer_.reset( new MemoryPressureObserverImpl(&memory_pressure_proxy)); memory_pressure_controller->AddObserver(std::move(memory_pressure_proxy)); #endif base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); std::string last_launched_app = command_line->GetSwitchValueNative(switches::kLastLaunchedApp); if (!last_launched_app.empty()) AppStateTracker::SetLastLaunchedApp(last_launched_app); std::string previous_app = command_line->GetSwitchValueNative(switches::kPreviousApp); if (!previous_app.empty()) AppStateTracker::SetPreviousApp(previous_app); #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS) extensions_client_ = std::make_unique(); extensions::ExtensionsClient::Set(extensions_client_.get()); extensions_renderer_client_ = std::make_unique(); extensions::ExtensionsRendererClient::Set(extensions_renderer_client_.get()); thread->AddObserver(extensions_renderer_client_->GetDispatcher()); guest_view_container_dispatcher_ = std::make_unique(); thread->AddObserver(guest_view_container_dispatcher_.get()); #endif for (auto& origin_or_hostname_pattern : network::SecureOriginAllowlist::GetInstance().GetCurrentAllowlist()) { blink::WebSecurityPolicy::AddOriginToTrustworthySafelist( blink::WebString::FromUTF8(origin_or_hostname_pattern)); } } void CastContentRendererClient::RenderViewCreated( content::RenderView* render_view) { blink::WebView* webview = render_view->GetWebView(); webview->SetBaseBackgroundColor(chromecast::GetSwitchValueColor( switches::kCastAppBackgroundColor, SK_ColorBLACK)); // Disable application cache as Chromecast doesn't support off-line // application running. webview->GetSettings()->SetOfflineWebApplicationCacheEnabled(false); } void CastContentRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { DCHECK(render_frame); // Lifetime is tied to |render_frame| via content::RenderFrameObserver. new media_control::MediaPlaybackOptions(render_frame); // Add script injection support to the RenderFrame, used by Cast platform // APIs. The injector's lifetime is bound to the RenderFrame's lifetime. new on_load_script_injector::OnLoadScriptInjector(render_frame); if (!app_media_capabilities_observer_receiver_.is_bound()) { mojo::Remote app_media_capabilities; render_frame->GetBrowserInterfaceBroker()->GetInterface( app_media_capabilities.BindNewPipeAndPassReceiver()); app_media_capabilities->AddObserver( app_media_capabilities_observer_receiver_.BindNewPipeAndPassRemote()); } #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS) extensions::Dispatcher* dispatcher = extensions_renderer_client_->GetDispatcher(); // ExtensionFrameHelper destroys itself when the RenderFrame is destroyed. new extensions::ExtensionFrameHelper(render_frame, dispatcher); dispatcher->OnRenderFrameCreated(render_frame); #endif #if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)) && defined(USE_OZONE) // JsChannelBindings destroys itself when the RenderFrame is destroyed. JsChannelBindings::Create(render_frame); #endif activity_url_filter_manager_->OnRenderFrameCreated(render_frame); // |base::Unretained| is safe here since the callback is triggered before the // destruction of IdentificationSettingsManager by which point // CastContentRendererClient should be alive. settings_managers_.emplace( render_frame->GetRoutingID(), std::make_unique( render_frame, base::BindOnce(&CastContentRendererClient::OnRenderFrameRemoved, base::Unretained(this), render_frame->GetRoutingID()))); } void CastContentRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS) extensions_renderer_client_->GetDispatcher()->RunScriptsAtDocumentStart( render_frame); #endif } void CastContentRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS) extensions_renderer_client_->GetDispatcher()->RunScriptsAtDocumentEnd( render_frame); #endif } void CastContentRendererClient::AddSupportedKeySystems( std::vector>* key_systems_properties) { media::AddChromecastKeySystems(key_systems_properties, false /* enable_persistent_license_support */, false /* force_software_crypto */); } bool CastContentRendererClient::IsSupportedAudioType( const ::media::AudioType& type) { #if defined(OS_ANDROID) if (type.spatial_rendering) return false; // No ATV device we know of has (E)AC3 decoder, so it relies on the audio sink // device. if (type.codec == ::media::kCodecEAC3) { return kBitstreamAudioCodecEac3 & supported_bitstream_audio_codecs_info_.codecs; } if (type.codec == ::media::kCodecAC3) { return kBitstreamAudioCodecAc3 & supported_bitstream_audio_codecs_info_.codecs; } if (type.codec == ::media::kCodecMpegHAudio) { return kBitstreamAudioCodecMpegHAudio & supported_bitstream_audio_codecs_info_.codecs; } return ::media::IsDefaultSupportedAudioType(type); #else if (type.profile == ::media::AudioCodecProfile::kXHE_AAC) return false; // If the HDMI sink supports bitstreaming the codec, then the vendor backend // does not need to support it. if (CheckSupportedBitstreamAudioCodec(type.codec, type.spatial_rendering)) return true; media::AudioCodec codec = media::ToCastAudioCodec(type.codec); // Cast platform implements software decoding of Opus and FLAC, so only PCM // support is necessary in order to support Opus and FLAC. if (codec == media::kCodecOpus || codec == media::kCodecFLAC) codec = media::kCodecPCM; media::AudioConfig cast_audio_config; cast_audio_config.codec = codec; return media::MediaCapabilitiesShlib::IsSupportedAudioConfig( cast_audio_config); #endif } bool CastContentRendererClient::IsSupportedVideoType( const ::media::VideoType& type) { // TODO(servolk): make use of eotf. // TODO(1066567): Check attached screen for support of type.hdr_metadata_type. if (type.hdr_metadata_type != ::gfx::HdrMetadataType::kNone) { NOTIMPLEMENTED() << "HdrMetadataType support signaling not implemented."; return false; } #if defined(OS_ANDROID) return supported_profiles_->IsSupportedVideoConfig( media::ToCastVideoCodec(type.codec, type.profile), media::ToCastVideoProfile(type.profile), type.level); #else return media::MediaCapabilitiesShlib::IsSupportedVideoConfig( media::ToCastVideoCodec(type.codec, type.profile), media::ToCastVideoProfile(type.profile), type.level); #endif } bool CastContentRendererClient::IsSupportedBitstreamAudioCodec( ::media::AudioCodec codec) { return IsSupportedBitstreamAudioCodecHelper( codec, supported_bitstream_audio_codecs_info_.codecs); } bool CastContentRendererClient::CheckSupportedBitstreamAudioCodec( ::media::AudioCodec codec, bool check_spatial_rendering) { if (!IsSupportedBitstreamAudioCodec(codec)) return false; if (!check_spatial_rendering) return true; return IsSupportedBitstreamAudioCodecHelper( codec, supported_bitstream_audio_codecs_info_.spatial_rendering); } std::unique_ptr CastContentRendererClient::CreatePrescientNetworking( content::RenderFrame* render_frame) { return std::make_unique( render_frame); } bool CastContentRendererClient::DeferMediaLoad( content::RenderFrame* render_frame, bool render_frame_has_played_media_before, base::OnceClosure closure) { return RunWhenInForeground(render_frame, std::move(closure)); } std::unique_ptr<::media::Demuxer> CastContentRendererClient::OverrideDemuxerForUrl( content::RenderFrame* render_frame, const GURL& url, scoped_refptr task_runner) { if (render_frame->GetRenderFrameMediaPlaybackOptions() .is_remoting_renderer_enabled() && url.SchemeIs(::media::remoting::kRemotingScheme)) { return std::make_unique<::media::remoting::StreamProvider>( ::media::remoting::ReceiverController::GetInstance(), task_runner); } return nullptr; } bool CastContentRendererClient::RunWhenInForeground( content::RenderFrame* render_frame, base::OnceClosure closure) { auto* playback_options = media_control::MediaPlaybackOptions::Get(render_frame); DCHECK(playback_options); return playback_options->RunWhenInForeground(std::move(closure)); } bool CastContentRendererClient::IsIdleMediaSuspendEnabled() { return false; } void CastContentRendererClient:: SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() { // Allow HtmlMediaElement.volume to be greater than 1, for normalization. blink::WebRuntimeFeatures::EnableFeatureFromString( "MediaElementVolumeGreaterThanOne", true); // Settings for ATV (Android defaults are not what we want). blink::WebRuntimeFeatures::EnableMediaControlsOverlayPlayButton(false); } void CastContentRendererClient::OnSupportedBitstreamAudioCodecsChanged( const BitstreamAudioCodecsInfo& info) { supported_bitstream_audio_codecs_info_ = info; } std::unique_ptr CastContentRendererClient::CreateWebSocketHandshakeThrottleProvider() { return std::make_unique( activity_url_filter_manager_.get()); } std::unique_ptr CastContentRendererClient::CreateURLLoaderThrottleProvider( content::URLLoaderThrottleProviderType type) { return std::make_unique( type, activity_url_filter_manager(), this); } base::Optional<::media::AudioRendererAlgorithmParameters> CastContentRendererClient::GetAudioRendererAlgorithmParameters( ::media::AudioParameters audio_parameters) { #if defined(OS_ANDROID) ::media::AudioRendererAlgorithmParameters parameters; parameters.max_capacity = kAudioRendererMaxCapacity; parameters.starting_capacity = kAudioRendererStartingCapacity; parameters.starting_capacity_for_encrypted = kAudioRendererStartingCapacityEncrypted; return base::Optional<::media::AudioRendererAlgorithmParameters>(parameters); #else return base::nullopt; #endif } IdentificationSettingsManager* CastContentRendererClient::GetSettingsManagerFromRenderFrameID( int render_frame_id) { const auto& it = settings_managers_.find(render_frame_id); if (it == settings_managers_.end()) { return nullptr; } return it->second.get(); } void CastContentRendererClient::OnRenderFrameRemoved(int render_frame_id) { size_t result = settings_managers_.erase(render_frame_id); if (result != 1U) { LOG(WARNING) << "Can't find the identification settings manager for render frame: " << render_frame_id; } } } // namespace shell } // namespace chromecast