diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc | 433 |
1 files changed, 335 insertions, 98 deletions
diff --git a/chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc b/chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc index 3a3a2cd256b..717318e8d68 100644 --- a/chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc +++ b/chromium/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc @@ -5,12 +5,20 @@ #include "third_party/blink/renderer/modules/media_capabilities/media_capabilities.h" #include <memory> +#include <sstream> #include <utility> +#include "base/feature_list.h" +#include "base/metrics/field_trial_params.h" #include "base/optional.h" +#include "media/base/media_switches.h" #include "media/base/mime_util.h" #include "media/base/supported_types.h" #include "media/filters/stream_parser_factory.h" +#include "media/learning/common/media_learning_tasks.h" +#include "media/learning/common/target_histogram.h" +#include "media/learning/mojo/public/mojom/learning_task_controller.mojom-blink.h" +#include "media/mojo/mojom/media_metrics_provider.mojom-blink.h" #include "media/mojo/mojom/media_types.mojom-blink.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h" @@ -20,6 +28,16 @@ #include "third_party/blink/public/platform/web_encrypted_media_request.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_key_system_track_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_capabilities_decoding_info.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_capabilities_info.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_capabilities_key_system_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_decoding_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_encoding_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_key_system_configuration.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_media_key_system_media_capability.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" @@ -28,18 +46,9 @@ #include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h" -#include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_configuration.h" -#include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_media_capability.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_keys_controller.h" -#include "third_party/blink/renderer/modules/media_capabilities/audio_configuration.h" -#include "third_party/blink/renderer/modules/media_capabilities/key_system_track_configuration.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_capabilities_decoding_info.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_capabilities_info.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_capabilities_key_system_configuration.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_configuration.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_decoding_configuration.h" -#include "third_party/blink/renderer/modules/media_capabilities/media_encoding_configuration.h" #include "third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/to_v8.h" #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h" @@ -58,11 +67,34 @@ namespace blink { namespace { +const double kLearningBadWindowThresholdDefault = 2; +const double kLearningNnrThresholdDefault = 3; + constexpr const char* kApplicationMimeTypePrefix = "application/"; constexpr const char* kAudioMimeTypePrefix = "audio/"; constexpr const char* kVideoMimeTypePrefix = "video/"; constexpr const char* kCodecsMimeTypeParam = "codecs"; +// Gets parameters for kMediaLearningSmoothnessExperiment field trial. Will +// provide sane defaults when field trial not enabled. Values of -1 indicate +// predictions from a given task should be ignored. + +// static +double GetLearningBadWindowThreshold() { + return base::GetFieldTrialParamByFeatureAsDouble( + media::kMediaLearningSmoothnessExperiment, + MediaCapabilities::kLearningBadWindowThresholdParamName, + kLearningBadWindowThresholdDefault); +} + +// static +double GetLearningNnrThreshold() { + return base::GetFieldTrialParamByFeatureAsDouble( + media::kMediaLearningSmoothnessExperiment, + MediaCapabilities::kLearningNnrThresholdParamName, + kLearningNnrThresholdDefault); +} + // Utility function that will create a MediaCapabilitiesDecodingInfo object with // all the values set to either true or false. MediaCapabilitiesDecodingInfo* CreateDecodingInfoWith(bool value) { @@ -110,7 +142,7 @@ class MediaCapabilitiesKeySystemAccessInitializer final // Query the client for smoothness and power efficiency of the video. It // will resolve the promise. std::move(get_perf_callback_) - .Run(std::move(resolver_), + .Run(resolver_.Get(), MakeGarbageCollected<MediaKeySystemAccess>(std::move(access))); } @@ -126,7 +158,7 @@ class MediaCapabilitiesKeySystemAccessInitializer final resolver_->Resolve(info); } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { MediaKeySystemAccessInitializerBase::Trace(visitor); } @@ -362,16 +394,17 @@ bool IsAudioCodecValid(const String& mime_type, // console. bool IsVideoCodecValid(const String& mime_type, const String& codec, + media::VideoCodec* out_video_codec, media::VideoCodecProfile* out_video_profile, String* console_warning) { - media::VideoCodec video_codec = media::kUnknownVideoCodec; uint8_t video_level = 0; media::VideoColorSpace video_color_space; bool is_video_codec_ambiguous = true; - if (!media::ParseVideoCodecString( - mime_type.Ascii(), codec.Ascii(), &is_video_codec_ambiguous, - &video_codec, out_video_profile, &video_level, &video_color_space)) { + if (!media::ParseVideoCodecString(mime_type.Ascii(), codec.Ascii(), + &is_video_codec_ambiguous, out_video_codec, + out_video_profile, &video_level, + &video_color_space)) { *console_warning = StringView("Failed to parse video contentType: ") + String{mime_type} + StringView("; codecs=") + String{codec}; @@ -395,6 +428,7 @@ bool IsAudioConfigurationSupported( const String& mime_type, const String& codec) { media::AudioCodec audio_codec = media::kUnknownAudioCodec; + media::AudioCodecProfile audio_profile = media::AudioCodecProfile::kUnknown; bool is_audio_codec_ambiguous = true; bool is_spatial_rendering = false; @@ -407,7 +441,8 @@ bool IsAudioConfigurationSupported( if (audio_config->hasSpatialRendering()) is_spatial_rendering = audio_config->spatialRendering(); - return media::IsSupportedAudioType({audio_codec, is_spatial_rendering}); + return media::IsSupportedAudioType( + {audio_codec, audio_profile, is_spatial_rendering}); } // Returns whether the VideoConfiguration is supported. @@ -468,11 +503,33 @@ bool ParseContentType(const String& content_type, } // anonymous namespace +const char MediaCapabilities::kLearningBadWindowThresholdParamName[] = + "bad_window_threshold"; + +const char MediaCapabilities::kLearningNnrThresholdParamName[] = + "nnr_threshold"; + MediaCapabilities::MediaCapabilities() = default; +void MediaCapabilities::Trace(blink::Visitor* visitor) { + visitor->Trace(pending_cb_map_); + ScriptWrappable::Trace(visitor); +} + +MediaCapabilities::PendingCallbackState::PendingCallbackState( + ScriptPromiseResolver* resolver, + MediaKeySystemAccess* access) + : resolver(resolver), key_system_access(access) {} + +void MediaCapabilities::PendingCallbackState::Trace(blink::Visitor* visitor) { + visitor->Trace(key_system_access); + visitor->Trace(resolver); +} + ScriptPromise MediaCapabilities::decodingInfo( ScriptState* script_state, - const MediaDecodingConfiguration* config) { + const MediaDecodingConfiguration* config, + ExceptionState& exception_state) { if (config->hasKeySystemConfiguration()) { UseCounter::Count( ExecutionContext::From(script_state), @@ -481,43 +538,40 @@ ScriptPromise MediaCapabilities::decodingInfo( String message; if (!IsValidMediaDecodingConfiguration(config, &message)) { - return ScriptPromise::Reject( - script_state, - V8ThrowException::CreateTypeError(script_state->GetIsolate(), message)); + exception_state.ThrowTypeError(message); + return ScriptPromise(); } if (config->hasVideo() && !IsValidVideoConfiguration(config->video())) { - return ScriptPromise::Reject( - script_state, V8ThrowException::CreateTypeError( - script_state->GetIsolate(), - "The video configuration dictionary is not valid.")); + exception_state.ThrowTypeError( + "The video configuration dictionary is not valid."); + return ScriptPromise(); } if (config->hasAudio() && !IsValidAudioConfiguration(config->audio())) { - return ScriptPromise::Reject( - script_state, V8ThrowException::CreateTypeError( - script_state->GetIsolate(), - "The audio configuration dictionary is not valid.")); + exception_state.ThrowTypeError( + "The audio configuration dictionary is not valid."); + return ScriptPromise(); } // Validation errors should return above. DCHECK(message.IsEmpty()); - String audio_mime; - String audio_codec; + String audio_mime_str; + String audio_codec_str; if (config->hasAudio()) { DCHECK(config->audio()->hasContentType()); - bool valid_content_type = ParseContentType(config->audio()->contentType(), - &audio_mime, &audio_codec); + bool valid_content_type = ParseContentType( + config->audio()->contentType(), &audio_mime_str, &audio_codec_str); DCHECK(valid_content_type); } - String video_mime; - String video_codec; + String video_mime_str; + String video_codec_str; if (config->hasVideo()) { DCHECK(config->video()->hasContentType()); - bool valid_content_type = ParseContentType(config->video()->contentType(), - &video_mime, &video_codec); + bool valid_content_type = ParseContentType( + config->video()->contentType(), &video_mime_str, &video_codec_str); DCHECK(valid_content_type); } @@ -525,8 +579,10 @@ ScriptPromise MediaCapabilities::decodingInfo( // that MSE support is not implied by EME support, so do it irrespective of // whether we have a KeySystem configuration. if (config->type() == "media-source") { - if ((config->hasAudio() && !CheckMseSupport(audio_mime, audio_codec)) || - (config->hasVideo() && !CheckMseSupport(video_mime, video_codec))) { + if ((config->hasAudio() && + !CheckMseSupport(audio_mime_str, audio_codec_str)) || + (config->hasVideo() && + !CheckMseSupport(video_mime_str, video_codec_str))) { // Unsupported EME queries should resolve with a null // MediaKeySystemAccess. return ScriptPromise::Cast( @@ -535,12 +591,14 @@ ScriptPromise MediaCapabilities::decodingInfo( } } + media::VideoCodec video_codec = media::kUnknownVideoCodec; media::VideoCodecProfile video_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN; if ((config->hasAudio() && - !IsAudioCodecValid(audio_mime, audio_codec, &message)) || + !IsAudioCodecValid(audio_mime_str, audio_codec_str, &message)) || (config->hasVideo() && - !IsVideoCodecValid(video_mime, video_codec, &video_profile, &message))) { + !IsVideoCodecValid(video_mime_str, video_codec_str, &video_codec, + &video_profile, &message))) { DCHECK(!message.IsEmpty()); if (ExecutionContext* execution_context = ExecutionContext::From(script_state)) { @@ -559,14 +617,15 @@ ScriptPromise MediaCapabilities::decodingInfo( if (config->hasKeySystemConfiguration()) { // GetEmeSupport() will call the VideoDecodePerfHistory service after // receiving info about support for the configuration for encrypted content. - return GetEmeSupport(script_state, video_profile, config); + return GetEmeSupport(script_state, video_codec, video_profile, config, + exception_state); } bool audio_supported = true; if (config->hasAudio()) { - audio_supported = - IsAudioConfigurationSupported(config->audio(), audio_mime, audio_codec); + audio_supported = IsAudioConfigurationSupported( + config->audio(), audio_mime_str, audio_codec_str); } // No need to check video capabilities if video not included in configuration @@ -581,8 +640,8 @@ ScriptPromise MediaCapabilities::decodingInfo( DCHECK(config->hasVideo()); // Return early for unsupported configurations. - if (!IsVideoConfigurationSupported(config->video(), video_mime, - video_codec)) { + if (!IsVideoConfigurationSupported(config->video(), video_mime_str, + video_codec_str)) { return ScriptPromise::Cast( script_state, ToV8(CreateDecodingInfoWith(false), script_state)); } @@ -594,7 +653,8 @@ ScriptPromise MediaCapabilities::decodingInfo( // undefined. See comment above Promise() in script_promise_resolver.h ScriptPromise promise = resolver->Promise(); - GetPerfInfo(video_profile, config->video(), resolver, nullptr /* access */); + GetPerfInfo(video_codec, video_profile, config->video(), resolver, + nullptr /* access */); return promise; } @@ -668,7 +728,49 @@ ScriptPromise MediaCapabilities::encodingInfo( return promise; } -bool MediaCapabilities::EnsureService(ExecutionContext* execution_context) { +bool MediaCapabilities::EnsureLearningPredictors( + ExecutionContext* execution_context) { + DCHECK(execution_context && !execution_context->IsContextDestroyed()); + + // One or both of these will have been bound in an earlier pass. + if (bad_window_predictor_ || nnr_predictor_) + return true; + + // MediaMetricsProvider currently only exposed via render frame. + // TODO(chcunningham): Expose in worker contexts pending outcome of + // media-learning experiments. + if (execution_context->IsWorkerGlobalScope()) + return false; + + scoped_refptr<base::SingleThreadTaskRunner> task_runner = + execution_context->GetTaskRunner(TaskType::kMediaElementEvent); + + mojo::Remote<media::mojom::blink::MediaMetricsProvider> metrics_provider; + execution_context->GetBrowserInterfaceBroker().GetInterface( + metrics_provider.BindNewPipeAndPassReceiver(task_runner)); + + if (!metrics_provider) + return false; + + if (GetLearningBadWindowThreshold() != -1.0) { + DCHECK_GE(GetLearningBadWindowThreshold(), 0); + metrics_provider->AcquireLearningTaskController( + media::learning::tasknames::kConsecutiveBadWindows, + bad_window_predictor_.BindNewPipeAndPassReceiver()); + } + + if (GetLearningNnrThreshold() != -1.0) { + DCHECK_GE(GetLearningNnrThreshold(), 0); + metrics_provider->AcquireLearningTaskController( + media::learning::tasknames::kConsecutiveNNRs, + nnr_predictor_.BindNewPipeAndPassReceiver()); + } + + return bad_window_predictor_ || nnr_predictor_; +} + +bool MediaCapabilities::EnsurePerfHistoryService( + ExecutionContext* execution_context) { if (decode_history_service_) return true; @@ -685,66 +787,63 @@ bool MediaCapabilities::EnsureService(ExecutionContext* execution_context) { ScriptPromise MediaCapabilities::GetEmeSupport( ScriptState* script_state, + media::VideoCodec video_codec, media::VideoCodecProfile video_profile, - const MediaDecodingConfiguration* configuration) { + const MediaDecodingConfiguration* configuration, + ExceptionState& exception_state) { DVLOG(3) << __func__; DCHECK(configuration->hasKeySystemConfiguration()); ExecutionContext* execution_context = ExecutionContext::From(script_state); DCHECK(execution_context); - Document* document = To<Document>(execution_context); + Document* document = Document::From(execution_context); // See context here: // https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes - if (!document->IsFeatureEnabled(mojom::FeaturePolicyFeature::kEncryptedMedia, - ReportOptions::kReportOnFailure)) { + if (!document->IsFeatureEnabled( + mojom::blink::FeaturePolicyFeature::kEncryptedMedia, + ReportOptions::kReportOnFailure)) { UseCounter::Count(document, WebFeature::kEncryptedMediaDisabledByFeaturePolicy); - document->AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript, - mojom::ConsoleMessageLevel::kWarning, - kEncryptedMediaFeaturePolicyConsoleWarning)); - return ScriptPromise::RejectWithDOMException( - script_state, MakeGarbageCollected<DOMException>( - DOMExceptionCode::kSecurityError, - "decodingInfo(): Creating MediaKeySystemAccess is " - "disabled by feature policy.")); + document->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kWarning, + kEncryptedMediaFeaturePolicyConsoleWarning)); + exception_state.ThrowSecurityError( + "decodingInfo(): Creating MediaKeySystemAccess is disabled by feature " + "policy."); + return ScriptPromise(); } // Calling context must have a real Document bound to a Page. This check is // ported from rMKSA (see http://crbug.com/456720). if (!document->GetPage()) { - return ScriptPromise::RejectWithDOMException( - script_state, - MakeGarbageCollected<DOMException>( - DOMExceptionCode::kInvalidStateError, - "The context provided is not associated with a page.")); + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + "The context provided is not associated with a page."); + return ScriptPromise(); } if (execution_context->IsWorkerGlobalScope()) { - return ScriptPromise::RejectWithDOMException( - script_state, - MakeGarbageCollected<DOMException>( - DOMExceptionCode::kInvalidStateError, - "Encrypted Media decoding info not available in Worker context.")); + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidStateError, + "Encrypted Media decoding info not available in Worker context."); + return ScriptPromise(); } if (!execution_context->IsSecureContext()) { - return ScriptPromise::RejectWithDOMException( - script_state, MakeGarbageCollected<DOMException>( - DOMExceptionCode::kSecurityError, - "Encrypted Media decoding info can only be " - "queried in a secure context.")); + exception_state.ThrowSecurityError( + "Encrypted Media decoding info can only be queried in a secure" + " context."); + return ScriptPromise(); } MediaCapabilitiesKeySystemConfiguration* key_system_config = configuration->keySystemConfiguration(); if (!key_system_config->hasKeySystem() || key_system_config->keySystem().IsEmpty()) { - return ScriptPromise::Reject( - script_state, - V8ThrowException::CreateTypeError( - script_state->GetIsolate(), "The key system String is not valid.")); + exception_state.ThrowTypeError("The key system String is not valid."); + return ScriptPromise(); } MediaKeySystemConfiguration* eme_config = @@ -814,7 +913,8 @@ ScriptPromise MediaCapabilities::GetEmeSupport( MakeGarbageCollected<MediaCapabilitiesKeySystemAccessInitializer>( script_state, key_system_config->keySystem(), config_vector, WTF::Bind(&MediaCapabilities::GetPerfInfo, WrapPersistent(this), - video_profile, WrapPersistent(configuration->video()))); + video_codec, video_profile, + WrapPersistent(configuration->video()))); // IMPORTANT: Acquire the promise before potentially synchronously resolving // it in the code that follows. Otherwise the promise returned to JS will be @@ -828,7 +928,8 @@ ScriptPromise MediaCapabilities::GetEmeSupport( return promise; } -void MediaCapabilities::GetPerfInfo(media::VideoCodecProfile video_profile, +void MediaCapabilities::GetPerfInfo(media::VideoCodec video_codec, + media::VideoCodecProfile video_profile, const VideoConfiguration* video_config, ScriptPromiseResolver* resolver, MediaKeySystemAccess* access) { @@ -852,40 +953,176 @@ void MediaCapabilities::GetPerfInfo(media::VideoCodecProfile video_profile, use_hw_secure_codecs = access->UseHardwareSecureCodecs(); } - if (!EnsureService(execution_context)) { - resolver->Resolve(WrapPersistent(CreateDecodingInfoWith(false))); + if (!EnsurePerfHistoryService(execution_context)) { + resolver->Resolve(WrapPersistent(CreateDecodingInfoWith(true))); return; } + const int callback_id = CreateCallbackId(); + pending_cb_map_.insert( + callback_id, + MakeGarbageCollected<MediaCapabilities::PendingCallbackState>(resolver, + access)); + + if (base::FeatureList::IsEnabled(media::kMediaLearningSmoothnessExperiment)) { + GetPerfInfo_ML(execution_context, callback_id, video_codec, video_profile, + video_config->width(), video_config->framerate()); + } + media::mojom::blink::PredictionFeaturesPtr features = media::mojom::blink::PredictionFeatures::New( static_cast<media::mojom::blink::VideoCodecProfile>(video_profile), - WebSize(video_config->width(), video_config->height()), + gfx::Size(video_config->width(), video_config->height()), video_config->framerate(), key_system, use_hw_secure_codecs); decode_history_service_->GetPerfInfo( - std::move(features), - WTF::Bind(&MediaCapabilities::OnPerfInfo, WrapPersistent(this), - WrapPersistent(resolver), WrapPersistent(access))); + std::move(features), WTF::Bind(&MediaCapabilities::OnPerfHistoryInfo, + WrapPersistent(this), callback_id)); } -void MediaCapabilities::OnPerfInfo(ScriptPromiseResolver* resolver, - MediaKeySystemAccess* access, - bool is_smooth, - bool is_power_efficient) { - if (!resolver->GetExecutionContext() || - resolver->GetExecutionContext()->IsContextDestroyed()) { +void MediaCapabilities::GetPerfInfo_ML(ExecutionContext* execution_context, + int callback_id, + media::VideoCodec video_codec, + media::VideoCodecProfile video_profile, + int width, + double framerate) { + DCHECK(execution_context && !execution_context->IsContextDestroyed()); + DCHECK(pending_cb_map_.Contains(callback_id)); + + if (!EnsureLearningPredictors(execution_context)) { + return; + } + + // FRAGILE: Order here MUST match order in + // WebMediaPlayerImpl::UpdateSmoothnessHelper(). + // TODO(chcunningham): refactor into something more robust. + Vector<media::learning::FeatureValue> ml_features( + {media::learning::FeatureValue(video_codec), + media::learning::FeatureValue(video_profile), + media::learning::FeatureValue(width), + media::learning::FeatureValue(framerate)}); + + if (bad_window_predictor_) { + bad_window_predictor_->PredictDistribution( + ml_features, WTF::Bind(&MediaCapabilities::OnBadWindowPrediction, + WrapPersistent(this), callback_id)); + } + + if (nnr_predictor_) { + nnr_predictor_->PredictDistribution( + ml_features, WTF::Bind(&MediaCapabilities::OnNnrPrediction, + WrapPersistent(this), callback_id)); + } +} + +void MediaCapabilities::ResolveCallbackIfReady(int callback_id) { + DCHECK(pending_cb_map_.Contains(callback_id)); + PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id); + + if (!pending_cb->db_is_power_efficient.has_value()) + return; + // Both db_* fields should be set simultaneously by the DB callback. + DCHECK(pending_cb->db_is_smooth.has_value()); + + if (nnr_predictor_ && !pending_cb->is_nnr_prediction_smooth.has_value()) + return; + + if (bad_window_predictor_ && + !pending_cb->is_bad_window_prediction_smooth.has_value()) + return; + + if (!pending_cb->resolver->GetExecutionContext() || + pending_cb->resolver->GetExecutionContext()->IsContextDestroyed()) { + // We're too late! Now that all the callbacks have provided state, its safe + // to erase the entry in the map. + pending_cb_map_.erase(callback_id); return; } Persistent<MediaCapabilitiesDecodingInfo> info( MediaCapabilitiesDecodingInfo::Create()); info->setSupported(true); - info->setSmooth(is_smooth); - info->setPowerEfficient(is_power_efficient); - info->setKeySystemAccess(access); + info->setKeySystemAccess(pending_cb->key_system_access); + info->setPowerEfficient(*pending_cb->db_is_power_efficient); + + // If ML experiment is running: AND available ML signals. + if (pending_cb->is_bad_window_prediction_smooth.has_value() || + pending_cb->is_nnr_prediction_smooth.has_value()) { + info->setSmooth( + pending_cb->is_bad_window_prediction_smooth.value_or(true) && + pending_cb->is_nnr_prediction_smooth.value_or(true)); + } else { + // Use DB when ML experiment not running. + info->setSmooth(*pending_cb->db_is_smooth); + } - resolver->Resolve(std::move(info)); + pending_cb->resolver->Resolve(std::move(info)); + pending_cb_map_.erase(callback_id); +} + +void MediaCapabilities::OnBadWindowPrediction( + int callback_id, + const base::Optional<::media::learning::TargetHistogram>& histogram) { + DCHECK(pending_cb_map_.Contains(callback_id)); + PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id); + + std::stringstream histogram_log; + if (!histogram) { + // No data, so optimistically assume zero bad windows. + pending_cb->is_bad_window_prediction_smooth = true; + histogram_log << "none"; + } else { + double histogram_average = histogram->Average(); + pending_cb->is_bad_window_prediction_smooth = + histogram_average <= GetLearningBadWindowThreshold(); + histogram_log << histogram_average; + } + + DVLOG(2) << __func__ << " bad_win_avg:" << histogram_log.str() + << " smooth_threshold (<=):" << GetLearningBadWindowThreshold(); + + ResolveCallbackIfReady(callback_id); +} + +void MediaCapabilities::OnNnrPrediction( + int callback_id, + const base::Optional<::media::learning::TargetHistogram>& histogram) { + DCHECK(pending_cb_map_.Contains(callback_id)); + PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id); + + std::stringstream histogram_log; + if (!histogram) { + // No data, so optimistically assume zero NNRs + pending_cb->is_nnr_prediction_smooth = true; + histogram_log << "none"; + } else { + double histogram_average = histogram->Average(); + pending_cb->is_nnr_prediction_smooth = + histogram_average <= GetLearningNnrThreshold(); + histogram_log << histogram_average; + } + + DVLOG(2) << __func__ << " nnr_avg:" << histogram_log.str() + << " smooth_threshold (<=):" << GetLearningNnrThreshold(); + + ResolveCallbackIfReady(callback_id); +} + +void MediaCapabilities::OnPerfHistoryInfo(int callback_id, + bool is_smooth, + bool is_power_efficient) { + DCHECK(pending_cb_map_.Contains(callback_id)); + PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id); + + pending_cb->db_is_smooth = is_smooth; + pending_cb->db_is_power_efficient = is_power_efficient; + + ResolveCallbackIfReady(callback_id); +} + +int MediaCapabilities::CreateCallbackId() { + ++last_callback_id_; + return last_callback_id_; } } // namespace blink |