diff --git a/cobalt/media/base/sbplayer_interface.cc b/cobalt/media/base/sbplayer_interface.cc index df33c907bc28..3e040dbe770e 100644 --- a/cobalt/media/base/sbplayer_interface.cc +++ b/cobalt/media/base/sbplayer_interface.cc @@ -118,6 +118,10 @@ void DefaultSbPlayerInterface::SetVolume(SbPlayer player, double volume) { SbPlayerSetVolume(player, volume); } +bool DefaultSbPlayerInterface::SetMaxInputSize(int max_input_size) { + return SbPlayerSetMaxInputSize(max_input_size); +} + void DefaultSbPlayerInterface::GetInfo(SbPlayer player, #if SB_API_VERSION >= 15 SbPlayerInfo* out_player_info) { diff --git a/cobalt/media/base/sbplayer_interface.h b/cobalt/media/base/sbplayer_interface.h index d9a31744ee3f..2f2c9491b6a8 100644 --- a/cobalt/media/base/sbplayer_interface.h +++ b/cobalt/media/base/sbplayer_interface.h @@ -58,6 +58,7 @@ class SbPlayerInterface { int height) = 0; virtual bool SetPlaybackRate(SbPlayer player, double playback_rate) = 0; virtual void SetVolume(SbPlayer player, double volume) = 0; + virtual bool SetMaxInputSize(int max_input_size) = 0; #if SB_API_VERSION >= 15 virtual void GetInfo(SbPlayer player, SbPlayerInfo* out_player_info) = 0; @@ -124,6 +125,7 @@ class DefaultSbPlayerInterface final : public SbPlayerInterface { int height) override; bool SetPlaybackRate(SbPlayer player, double playback_rate) override; void SetVolume(SbPlayer player, double volume) override; + bool SetMaxInputSize(int max_input_size) override; #if SB_API_VERSION >= 15 void GetInfo(SbPlayer player, SbPlayerInfo* out_player_info) override; #else // SB_API_VERSION >= 15 diff --git a/cobalt/media/media_module.cc b/cobalt/media/media_module.cc index 06e7e260bd09..51266c0f25f1 100644 --- a/cobalt/media/media_module.cc +++ b/cobalt/media/media_module.cc @@ -198,6 +198,9 @@ bool MediaModule::SetConfiguration(const std::string& name, int32 value) { LOG(INFO) << (value ? "Enabling" : "Disabling") << " media metrics collection."; return true; + } else if (name == "MaxInputSize") { + LOG(ERROR) << "Video max_input_size is set to " << value << " via h5vcc."; + return sbplayer_interface_->SetMaxInputSize(value); #if SB_API_VERSION >= 15 } else if (name == "AudioWriteDurationLocal" && value > 0) { audio_write_duration_local_ = value; diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java index 8164adda1569..dbc1e53db56c 100644 --- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java +++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java @@ -618,6 +618,7 @@ public static void createVideoMediaCodecBridge( MediaCrypto crypto, ColorInfo colorInfo, int tunnelModeAudioSessionId, + int maxInputSize, CreateMediaCodecBridgeResult outCreateMediaCodecBridgeResult) { MediaCodec mediaCodec = null; outCreateMediaCodecBridgeResult.mMediaCodecBridge = null; @@ -776,6 +777,20 @@ public static void createVideoMediaCodecBridge( } } + if (maxInputSize != -1) { + try { + mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize); + Log.i( + TAG, + "KEY_MAX_INPUT_SIZE is " + + mediaFormat.getInteger(android.media.MediaFormat.KEY_MAX_INPUT_SIZE) + + " after setting it to " + + maxInputSize + + '.'); + } catch (Exception e) { + Log.w(TAG, "MediaFormat.getInteger(KEY_MAX_INPUT_SIZE) failed with exception: ", e); + } + } if (!bridge.configureVideo( mediaFormat, surface, crypto, 0, maxWidth, maxHeight, outCreateMediaCodecBridgeResult)) { Log.e(TAG, "Failed to configure video codec."); @@ -1177,9 +1192,9 @@ private void maybeSetMaxInputSize(MediaFormat format) { return; } // Estimate the maximum input size assuming three channel 4:2:0 subsampled input frames. - int maxInputSize = (maxPixels * 3) / (2 * minCompressionRatio); - format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize); try { + int maxInputSize = (maxPixels * 3) / (2 * minCompressionRatio); + format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize); Log.i( TAG, "KEY_MAX_INPUT_SIZE is " diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn index 2cfd29c7db6a..2f244453f5f0 100644 --- a/starboard/android/shared/BUILD.gn +++ b/starboard/android/shared/BUILD.gn @@ -361,6 +361,7 @@ static_library("starboard_platform") { "player_destroy.cc", "player_get_preferred_output_mode.cc", "player_set_bounds.cc", + "player_set_max_input_size.cc", "player_set_playback_rate.cc", "sanitizer_options.cc", "socket_get_interface_address.cc", @@ -420,6 +421,7 @@ static_library("starboard_platform") { "//starboard/shared/starboard/player/player_destroy.cc", "//starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc", "//starboard/shared/starboard/player/player_set_bounds.cc", + "//starboard/shared/starboard/player/player_set_max_input_size.cc", "//starboard/shared/starboard/player/player_set_playback_rate.cc", ] diff --git a/starboard/android/shared/media_codec_bridge.cc b/starboard/android/shared/media_codec_bridge.cc index 705ae1f4958a..807d044c6da1 100644 --- a/starboard/android/shared/media_codec_bridge.cc +++ b/starboard/android/shared/media_codec_bridge.cc @@ -226,6 +226,7 @@ scoped_ptr MediaCodecBridge::CreateVideoMediaCodecBridge( bool require_secured_decoder, bool require_software_codec, int tunnel_mode_audio_session_id, + int max_input_size, bool force_big_endian_hdr_metadata, std::string* error_message) { SB_DCHECK(error_message); @@ -316,14 +317,14 @@ scoped_ptr MediaCodecBridge::CreateVideoMediaCodecBridge( "(JLjava/lang/String;Ljava/lang/String;IIIIILandroid/view/Surface;" "Landroid/media/MediaCrypto;" "Ldev/cobalt/media/MediaCodecBridge$ColorInfo;" - "I" + "II" "Ldev/cobalt/media/MediaCodecBridge$CreateMediaCodecBridgeResult;)" "V", reinterpret_cast(native_media_codec_bridge.get()), j_mime.Get(), j_decoder_name.Get(), width_hint, height_hint, fps, max_width.value_or(-1), max_height.value_or(-1), j_surface, j_media_crypto, j_color_info.Get(), tunnel_mode_audio_session_id, - j_create_media_codec_bridge_result.Get()); + max_input_size, j_create_media_codec_bridge_result.Get()); jobject j_media_codec_bridge = env->CallObjectMethodOrAbort( j_create_media_codec_bridge_result.Get(), "mediaCodecBridge", diff --git a/starboard/android/shared/media_codec_bridge.h b/starboard/android/shared/media_codec_bridge.h index cbee93a60003..51ca7eccc814 100644 --- a/starboard/android/shared/media_codec_bridge.h +++ b/starboard/android/shared/media_codec_bridge.h @@ -172,6 +172,7 @@ class MediaCodecBridge { bool require_secured_decoder, bool require_software_codec, int tunnel_mode_audio_session_id, + int max_input_size, bool force_big_endian_hdr_metadata, std::string* error_message); diff --git a/starboard/android/shared/media_decoder.cc b/starboard/android/shared/media_decoder.cc index 280d2fdeb4a3..560afd9bdfc9 100644 --- a/starboard/android/shared/media_decoder.cc +++ b/starboard/android/shared/media_decoder.cc @@ -117,6 +117,7 @@ MediaDecoder::MediaDecoder(Host* host, bool require_software_codec, const FrameRenderedCB& frame_rendered_cb, int tunnel_mode_audio_session_id, + int max_input_size, bool force_big_endian_hdr_metadata, std::string* error_message) : media_type_(kSbMediaTypeVideo), @@ -134,7 +135,7 @@ MediaDecoder::MediaDecoder(Host* host, media_codec_bridge_ = MediaCodecBridge::CreateVideoMediaCodecBridge( video_codec, width_hint, height_hint, fps, max_width, max_height, this, j_output_surface, j_media_crypto, color_metadata, require_secured_decoder, - require_software_codec, tunnel_mode_audio_session_id, + require_software_codec, tunnel_mode_audio_session_id, max_input_size, force_big_endian_hdr_metadata, error_message); if (!media_codec_bridge_) { SB_LOG(ERROR) << "Failed to create video media codec bridge with error: " diff --git a/starboard/android/shared/media_decoder.h b/starboard/android/shared/media_decoder.h index bfb94f494541..f248e43affb9 100644 --- a/starboard/android/shared/media_decoder.h +++ b/starboard/android/shared/media_decoder.h @@ -96,6 +96,7 @@ class MediaDecoder bool require_software_codec, const FrameRenderedCB& frame_rendered_cb, int tunnel_mode_audio_session_id, + int max_input_size, bool force_big_endian_hdr_metadata, std::string* error_message); ~MediaDecoder(); diff --git a/starboard/android/shared/media_is_video_supported.cc b/starboard/android/shared/media_is_video_supported.cc index c6e0462d750b..b6e4a74c4f15 100644 --- a/starboard/android/shared/media_is_video_supported.cc +++ b/starboard/android/shared/media_is_video_supported.cc @@ -92,6 +92,12 @@ bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec, false)) { MaxMediaCodecOutputBuffersLookupTable::GetInstance()->SetEnabled(false); } + + // Allow the web app to set the maximum size of input buffer + // (MediaFormat.KEY_MAX_INPUT_SIZE) + if (!mime_type->ValidateIntParameter("maxinputsize")) { + return false; + } } if (must_support_tunnel_mode && decode_to_texture_required) { diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h index b6e3efc84b30..f15018415ac5 100644 --- a/starboard/android/shared/player_components_factory.h +++ b/starboard/android/shared/player_components_factory.h @@ -47,6 +47,10 @@ #include "starboard/shared/starboard/player/filter/video_renderer_internal_impl.h" #include "starboard/shared/starboard/player/filter/video_renderer_sink.h" +// Specifies max_input_size to set MediaFormat.KEY_MAX_INPUT_SIZE on ATV. +// Use -1 as default value +inline int kSbMediaMaxInputSize = -1; + namespace starboard { namespace android { namespace shared { @@ -231,6 +235,25 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: return scoped_ptr(); } + // Set max_input_size with a non-zero value to overwrite + // MediaFormat.KEY_MAX_INPUT_SIZE. Use -1 as default value. + int max_input_size = kSbMediaMaxInputSize; + SB_LOG_IF(ERROR, max_input_size != -1) + << "max_input_size is " << max_input_size; + if (!creation_parameters.video_mime().empty()) { + MimeType video_mime_type(creation_parameters.video_mime()); + if (!video_mime_type.is_valid() || + !video_mime_type.ValidateIntParameter("maxinputsize")) { + return scoped_ptr(); + } + + max_input_size = video_mime_type.GetParamIntValue("maxinputsize", + kSbMediaMaxInputSize); + SB_LOG_IF(INFO, max_input_size != -1) + << "Mime attribute \"maxinputsize\" is set to: " << max_input_size + << "."; + } + scoped_ptr<::starboard::shared::starboard::player::filter::VideoRenderer> video_renderer; if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) { @@ -239,7 +262,7 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: scoped_ptr video_decoder = CreateVideoDecoder( creation_parameters, kTunnelModeAudioSessionId, - kForceSecurePipelineUnderTunnelMode, error_message); + kForceSecurePipelineUnderTunnelMode, max_input_size, error_message); if (video_decoder) { using starboard::shared::starboard::player::filter::VideoRendererImpl; @@ -294,6 +317,13 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: "Invalid video MIME: '" + std::string(video_mime) + "'"; return false; } + + if (!video_mime_type.is_valid() || + !video_mime_type.ValidateIntParameter("maxinputsize")) { + *error_message = + "Invalid video MIME: '" + std::string(video_mime) + "'"; + return false; + } } int tunnel_mode_audio_session_id = -1; @@ -401,14 +431,21 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: SB_DCHECK(video_render_algorithm); SB_DCHECK(video_renderer_sink); SB_DCHECK(error_message); + // Set max_input_size with a non-zero value to overwrite + // MediaFormat.KEY_MAX_INPUT_SIZE. Use -1 as default value. + int max_input_size = video_mime_type.GetParamIntValue( + "maxinputsize", kSbMediaMaxInputSize); + SB_LOG_IF(ERROR, max_input_size != -1) + << "max_input_size is " << max_input_size; if (tunnel_mode_audio_session_id == -1) { force_secure_pipeline_under_tunnel_mode = false; } - scoped_ptr video_decoder_impl = CreateVideoDecoder( - creation_parameters, tunnel_mode_audio_session_id, - force_secure_pipeline_under_tunnel_mode, error_message); + scoped_ptr video_decoder_impl = + CreateVideoDecoder(creation_parameters, tunnel_mode_audio_session_id, + force_secure_pipeline_under_tunnel_mode, + max_input_size, error_message); if (video_decoder_impl) { *video_render_algorithm = video_decoder_impl->GetRenderAlgorithm(); *video_renderer_sink = video_decoder_impl->GetSink(); @@ -452,6 +489,7 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: const CreationParameters& creation_parameters, int tunnel_mode_audio_session_id, bool force_secure_pipeline_under_tunnel_mode, + int max_input_size, std::string* error_message) { bool force_big_endian_hdr_metadata = false; if (!creation_parameters.video_mime().empty()) { @@ -473,7 +511,7 @@ class PlayerComponentsFactory : public starboard::shared::starboard::player:: creation_parameters.max_video_capabilities(), tunnel_mode_audio_session_id, force_secure_pipeline_under_tunnel_mode, kForceResetSurfaceUnderTunnelMode, force_big_endian_hdr_metadata, - error_message)); + max_input_size, error_message)); if (creation_parameters.video_codec() == kSbMediaVideoCodecAv1 || video_decoder->is_decoder_created()) { return video_decoder.Pass(); diff --git a/starboard/android/shared/player_set_max_input_size.cc b/starboard/android/shared/player_set_max_input_size.cc new file mode 100644 index 000000000000..a4045722bd6a --- /dev/null +++ b/starboard/android/shared/player_set_max_input_size.cc @@ -0,0 +1,30 @@ +// Copyright 2023 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 "starboard/player.h" + +#include "starboard/android/shared/player_components_factory.h" +#include "starboard/common/log.h" +#include "starboard/shared/starboard/player/player_internal.h" + +bool SbPlayerSetMaxInputSize(int max_input_size) { + if (max_input_size != -1 && max_input_size < 3110400) { + SB_DLOG(ERROR) << "Setting max_input_size " << max_input_size + << " is small than default value "; + return false; + } + kSbMediaMaxInputSize = max_input_size; + SB_LOG(ERROR) << "Set max_input_size to " << kSbMediaMaxInputSize; + return true; +} diff --git a/starboard/android/shared/video_decoder.cc b/starboard/android/shared/video_decoder.cc index dccc57b0f3d9..ab4da9825ab0 100644 --- a/starboard/android/shared/video_decoder.cc +++ b/starboard/android/shared/video_decoder.cc @@ -356,6 +356,7 @@ VideoDecoder::VideoDecoder(const VideoStreamInfo& video_stream_info, bool force_secure_pipeline_under_tunnel_mode, bool force_reset_surface_under_tunnel_mode, bool force_big_endian_hdr_metadata, + int max_input_size, std::string* error_message) : video_codec_(video_stream_info.codec), drm_system_(static_cast(drm_system)), @@ -364,6 +365,7 @@ VideoDecoder::VideoDecoder(const VideoStreamInfo& video_stream_info, decode_target_graphics_context_provider), max_video_capabilities_(max_video_capabilities), tunnel_mode_audio_session_id_(tunnel_mode_audio_session_id), + max_input_size_(max_input_size), force_reset_surface_under_tunnel_mode_( force_reset_surface_under_tunnel_mode), has_new_texture_available_(false), @@ -710,8 +712,8 @@ bool VideoDecoder::InitializeCodec(const VideoStreamInfo& video_stream_info, j_output_surface, drm_system_, color_metadata_ ? &*color_metadata_ : nullptr, require_software_codec_, std::bind(&VideoDecoder::OnTunnelModeFrameRendered, this, _1), - tunnel_mode_audio_session_id_, force_big_endian_hdr_metadata_, - error_message)); + tunnel_mode_audio_session_id_, max_input_size_, + force_big_endian_hdr_metadata_, error_message)); if (media_decoder_->is_valid()) { if (error_cb_) { media_decoder_->Initialize( diff --git a/starboard/android/shared/video_decoder.h b/starboard/android/shared/video_decoder.h index 184265649992..40bcf3027586 100644 --- a/starboard/android/shared/video_decoder.h +++ b/starboard/android/shared/video_decoder.h @@ -70,6 +70,7 @@ class VideoDecoder bool force_secure_pipeline_under_tunnel_mode, bool force_reset_surface_under_tunnel_mode, bool force_big_endian_hdr_metadata, + int max_input_size, std::string* error_message); ~VideoDecoder() override; @@ -146,6 +147,9 @@ class VideoDecoder const int tunnel_mode_audio_session_id_ = -1; + // Allow to configure maximum input buffer size + const int max_input_size_; + // Force resetting the video surface after tunnel mode playback, which // prevents video distortion on some devices. const bool force_reset_surface_under_tunnel_mode_; diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc index c0134d22a875..3365559e8a91 100644 --- a/starboard/elf_loader/exported_symbols.cc +++ b/starboard/elf_loader/exported_symbols.cc @@ -256,6 +256,7 @@ ExportedSymbols::ExportedSymbols() { REGISTER_SYMBOL(SbPlayerSetBounds); REGISTER_SYMBOL(SbPlayerSetPlaybackRate); REGISTER_SYMBOL(SbPlayerSetVolume); + REGISTER_SYMBOL(SbPlayerSetMaxInputSize); REGISTER_SYMBOL(SbPlayerWriteEndOfStream); #if SB_API_VERSION >= 15 REGISTER_SYMBOL(SbPlayerWriteSamples); diff --git a/starboard/player.h b/starboard/player.h index 0d3c85a4a41c..c370fbdf5f1f 100644 --- a/starboard/player.h +++ b/starboard/player.h @@ -614,6 +614,9 @@ SB_EXPORT bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate); // value of |1.0| means that it should be played at full volume. SB_EXPORT void SbPlayerSetVolume(SbPlayer player, double volume); +// TODO(borongchen): add description +SB_EXPORT bool SbPlayerSetMaxInputSize(int max_input_size); + // Gets a snapshot of the current player state and writes it to // |out_player_info|. This function may be called very frequently and is // expected to be inexpensive. diff --git a/starboard/shared/starboard/player/buildfiles.gni b/starboard/shared/starboard/player/buildfiles.gni index c2fbafe489b8..a95b5e27fd6f 100644 --- a/starboard/shared/starboard/player/buildfiles.gni +++ b/starboard/shared/starboard/player/buildfiles.gni @@ -33,6 +33,7 @@ common_player_sources = [ "//starboard/shared/starboard/player/player_internal.h", "//starboard/shared/starboard/player/player_seek.cc", "//starboard/shared/starboard/player/player_set_bounds.cc", + "//starboard/shared/starboard/player/player_set_max_input_size.cc", "//starboard/shared/starboard/player/player_set_playback_rate.cc", "//starboard/shared/starboard/player/player_set_volume.cc", "//starboard/shared/starboard/player/player_worker.cc", diff --git a/starboard/shared/starboard/player/player_create.cc b/starboard/shared/starboard/player/player_create.cc index 1e16806db637..e7027d2ebde2 100644 --- a/starboard/shared/starboard/player/player_create.cc +++ b/starboard/shared/starboard/player/player_create.cc @@ -47,6 +47,7 @@ SbPlayer SbPlayerCreate(SbWindow window, SbPlayerErrorFunc player_error_func, void* context, SbDecodeTargetGraphicsContextProvider* provider) { + SB_LOG(ERROR) << "Brown: SbPlayerCreate()"; if (!player_error_func) { SB_LOG(ERROR) << "|player_error_func| cannot be null."; return kSbPlayerInvalid; @@ -210,6 +211,7 @@ SbPlayer SbPlayerCreate(SbWindow window, UpdateActiveSessionPlatformPlaybackState(kPlaying); + SB_LOG(ERROR) << "Brown: FilterBasedPlayerWorkerHandler()"; starboard::scoped_ptr handler( new FilterBasedPlayerWorkerHandler(creation_param, provider)); diff --git a/starboard/shared/starboard/player/player_set_max_input_size.cc b/starboard/shared/starboard/player/player_set_max_input_size.cc new file mode 100644 index 000000000000..b1a197d467d7 --- /dev/null +++ b/starboard/shared/starboard/player/player_set_max_input_size.cc @@ -0,0 +1,23 @@ +// Copyright 2023 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 "starboard/player.h" + +#include "starboard/common/log.h" +#include "starboard/shared/starboard/player/player_internal.h" + +bool SbPlayerSetMaxInputSize(int max_input_size) { + SB_DLOG(WARNING) << "Set max_input_size only on ATV"; + return false; +}