Commit 2546f022 authored by Chris Cunningham's avatar Chris Cunningham Committed by Commit Bot

MediaCapabilities: check gpu video acceleration for powerEfficient

Feature disabled by default. Enable with command line:
--enable-features=MediaCapabilitiesQueryGpuFactories

Historically info.powerEfficient was determined by the
VideoDecodePerfHistory DB. This patch instead queries the GpuFactories
(proxy for hw decode) for to ask the question directly.

The critical difference is that the DB requires some past data to
be correct, while GpuFactories will know the answer without ever having
played a video.

Change-Id: I402bc03d008c73fa1bccf61beb61609efaf42ddf
Bug: 1105258
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2295684
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarChrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788421}
parent 66ade3e7
......@@ -242,6 +242,11 @@ const base::Feature kResumeBackgroundVideo {
#endif
};
// When enabled, MediaCapabilities will check with GPU Video Accelerator
// Factories to determine isPowerEfficient = true/false.
const base::Feature kMediaCapabilitiesQueryGpuFactories{
"MediaCapabilitiesQueryGpuFactories", base::FEATURE_DISABLED_BY_DEFAULT};
// Enable Media Capabilities with finch-parameters.
const base::Feature kMediaCapabilitiesWithParameters{
"MediaCapabilitiesWithParameters", base::FEATURE_ENABLED_BY_DEFAULT};
......
......@@ -134,6 +134,7 @@ MEDIA_EXPORT extern const base::Feature kInternalMediaSession;
MEDIA_EXPORT extern const base::Feature kKaleidoscope;
MEDIA_EXPORT extern const base::Feature kLiveCaption;
MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream;
MEDIA_EXPORT extern const base::Feature kMediaCapabilitiesQueryGpuFactories;
MEDIA_EXPORT extern const base::Feature kMediaCapabilitiesWithParameters;
MEDIA_EXPORT extern const base::Feature kMediaCastOverlayButton;
MEDIA_EXPORT extern const base::Feature kMediaEngagementBypassAutoplayPolicies;
......
......@@ -7,6 +7,8 @@ include_rules = [
"+media/mojo/mojom/media_types.mojom-blink.h",
"+media/mojo/mojom/video_decode_perf_history.mojom-blink.h",
"+media/mojo/mojom/media_metrics_provider.mojom-blink.h",
"+media/video/gpu_video_accelerator_factories.h",
"+media/video/supported_video_decoder_config.h",
"-third_party/blink/renderer/modules",
"+third_party/blink/renderer/modules/encryptedmedia",
"+third_party/blink/renderer/modules/media_capabilities",
......@@ -20,5 +22,6 @@ specific_include_rules = {
"+base/strings/string_number_conversions.h",
"+base/test/bind_test_util.h",
"+media/mojo/mojom/watch_time_recorder.mojom-blink.h",
"+media/video/mock_gpu_video_accelerator_factories.h",
],
}
\ No newline at end of file
......@@ -13,17 +13,22 @@
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/base/mime_util.h"
#include "media/base/supported_types.h"
#include "media/base/video_decoder_config.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 "media/video/gpu_video_accelerator_factories.h"
#include "media/video/supported_video_decoder_config.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"
#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_encrypted_media_client.h"
#include "third_party/blink/public/platform/web_encrypted_media_request.h"
......@@ -56,6 +61,7 @@
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/media_capabilities/web_media_capabilities_info.h"
#include "third_party/blink/renderer/platform/media_capabilities/web_media_configuration.h"
......@@ -63,6 +69,8 @@
#include "third_party/blink/renderer/platform/peerconnection/transmission_encoding_info_handler.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace blink {
......@@ -105,6 +113,12 @@ double GetLearningNnrThreshold() {
kLearningNnrThresholdDefault);
}
// static
bool UseGpuFactoriesForPowerEfficient() {
return base::FeatureList::IsEnabled(
media::kMediaCapabilitiesQueryGpuFactories);
}
// Utility function that will create a MediaCapabilitiesDecodingInfo object with
// all the values set to either true or false.
MediaCapabilitiesDecodingInfo* CreateDecodingInfoWith(bool value) {
......@@ -513,16 +527,14 @@ bool IsAudioConfigurationSupported(
// Returns whether the VideoConfiguration is supported.
// IsVideoCodecValid() MUST be called before.
bool IsVideoConfigurationSupported(
const blink::VideoConfiguration* video_config,
const String& mime_type,
const String& codec) {
bool IsVideoConfigurationSupported(const String& mime_type,
const String& codec,
media::VideoColorSpace video_color_space,
media::HdrMetadataType hdr_metadata_type) {
media::VideoCodec video_codec = media::kUnknownVideoCodec;
media::VideoCodecProfile video_profile;
uint8_t video_level = 0;
media::VideoColorSpace video_color_space;
bool is_video_codec_ambiguous = true;
media::HdrMetadataType hdr_metadata_type;
// Must succeed as IsVideoCodecValid() should have been called before.
bool parsed = media::ParseVideoCodecString(
......@@ -530,9 +542,6 @@ bool IsVideoConfigurationSupported(
&video_profile, &video_level, &video_color_space);
DCHECK(parsed && !is_video_codec_ambiguous);
ParseDynamicRangeConfigurations(video_config, &video_color_space,
&hdr_metadata_type);
return media::IsSupportedVideoType({video_codec, video_profile, video_level,
video_color_space, hdr_metadata_type});
}
......@@ -696,11 +705,19 @@ ScriptPromise MediaCapabilities::decodingInfo(
// Validation errors should return above.
DCHECK(message.IsEmpty());
media::VideoColorSpace video_color_space;
media::HdrMetadataType hdr_metadata_type = media::HdrMetadataType::kNone;
if (config->hasVideo()) {
ParseDynamicRangeConfigurations(config->video(), &video_color_space,
&hdr_metadata_type);
}
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_codec, video_profile, config,
request_time, exception_state);
return GetEmeSupport(script_state, video_codec, video_profile,
video_color_space, config, request_time,
exception_state);
}
bool audio_supported = true;
......@@ -722,8 +739,8 @@ ScriptPromise MediaCapabilities::decodingInfo(
DCHECK(config->hasVideo());
// Return early for unsupported configurations.
if (!IsVideoConfigurationSupported(config->video(), video_mime_str,
video_codec_str)) {
if (!IsVideoConfigurationSupported(video_mime_str, video_codec_str,
video_color_space, hdr_metadata_type)) {
return ScriptPromise::Cast(
script_state, ToV8(CreateDecodingInfoWith(false), script_state));
}
......@@ -735,8 +752,8 @@ ScriptPromise MediaCapabilities::decodingInfo(
// undefined. See comment above Promise() in script_promise_resolver.h
ScriptPromise promise = resolver->Promise();
GetPerfInfo(video_codec, video_profile, config->video(), request_time,
resolver, nullptr /* access */);
GetPerfInfo(video_codec, video_profile, video_color_space, config,
request_time, resolver, nullptr /* access */);
return promise;
}
......@@ -871,6 +888,7 @@ ScriptPromise MediaCapabilities::GetEmeSupport(
ScriptState* script_state,
media::VideoCodec video_codec,
media::VideoCodecProfile video_profile,
media::VideoColorSpace video_color_space,
const MediaDecodingConfiguration* configuration,
const base::TimeTicks& request_time,
ExceptionState& exception_state) {
......@@ -994,12 +1012,9 @@ ScriptPromise MediaCapabilities::GetEmeSupport(
MediaCapabilitiesKeySystemAccessInitializer* initializer =
MakeGarbageCollected<MediaCapabilitiesKeySystemAccessInitializer>(
script_state, key_system_config->keySystem(), config_vector,
WTF::Bind(
&MediaCapabilities::GetPerfInfo, WrapPersistent(this),
video_codec, video_profile,
WrapPersistent(configuration->hasVideo() ? configuration->video()
: nullptr),
request_time));
WTF::Bind(&MediaCapabilities::GetPerfInfo, WrapPersistent(this),
video_codec, video_profile, video_color_space,
WrapPersistent(configuration), request_time));
// IMPORTANT: Acquire the promise before potentially synchronously resolving
// it in the code that follows. Otherwise the promise returned to JS will be
......@@ -1014,16 +1029,19 @@ ScriptPromise MediaCapabilities::GetEmeSupport(
return promise;
}
void MediaCapabilities::GetPerfInfo(media::VideoCodec video_codec,
media::VideoCodecProfile video_profile,
const VideoConfiguration* video_config,
const base::TimeTicks& request_time,
ScriptPromiseResolver* resolver,
MediaKeySystemAccess* access) {
void MediaCapabilities::GetPerfInfo(
media::VideoCodec video_codec,
media::VideoCodecProfile video_profile,
media::VideoColorSpace video_color_space,
const MediaDecodingConfiguration* decoding_config,
const base::TimeTicks& request_time,
ScriptPromiseResolver* resolver,
MediaKeySystemAccess* access) {
ExecutionContext* execution_context = resolver->GetExecutionContext();
if (!execution_context || execution_context->IsContextDestroyed())
return;
const VideoConfiguration* video_config = decoding_config->video();
if (!video_config) {
// Audio-only is always smooth and power efficient.
MediaCapabilitiesDecodingInfo* info = CreateDecodingInfoWith(true);
......@@ -1065,6 +1083,11 @@ void MediaCapabilities::GetPerfInfo(media::VideoCodec video_codec,
decode_history_service_->GetPerfInfo(
std::move(features), WTF::Bind(&MediaCapabilities::OnPerfHistoryInfo,
WrapPersistent(this), callback_id));
if (UseGpuFactoriesForPowerEfficient()) {
GetGpuFactoriesSupport(callback_id, video_codec, video_profile,
video_color_space, decoding_config);
}
}
void MediaCapabilities::GetPerfInfo_ML(ExecutionContext* execution_context,
......@@ -1102,6 +1125,82 @@ void MediaCapabilities::GetPerfInfo_ML(ExecutionContext* execution_context,
}
}
void MediaCapabilities::GetGpuFactoriesSupport(
int callback_id,
media::VideoCodec video_codec,
media::VideoCodecProfile video_profile,
media::VideoColorSpace video_color_space,
const MediaDecodingConfiguration* decoding_config) {
DCHECK(UseGpuFactoriesForPowerEfficient());
DCHECK(decoding_config->hasVideo());
DCHECK(pending_cb_map_.Contains(callback_id));
ExecutionContext* execution_context =
pending_cb_map_.at(callback_id)->resolver->GetExecutionContext();
// Frame may become detached in the time it takes us to get callback for
// NotifyDecoderSupportKnown. In this case, report false as a means of clean
// shutdown.
if (!execution_context || execution_context->IsContextDestroyed()) {
OnGpuFactoriesSupport(callback_id, false);
return;
}
media::GpuVideoAcceleratorFactories* gpu_factories =
Platform::Current()->GetGpuFactories();
if (!gpu_factories) {
OnGpuFactoriesSupport(callback_id, false);
return;
}
if (!gpu_factories->IsDecoderSupportKnown()) {
gpu_factories->NotifyDecoderSupportKnown(
WTF::Bind(&MediaCapabilities::GetGpuFactoriesSupport,
WrapPersistent(this), callback_id, video_codec, video_profile,
video_color_space, WrapPersistent(decoding_config)));
return;
}
// TODO(chcunningham): Get the actual scheme and alpha mode from
// |decoding_config| once implemented (its already spec'ed).
media::EncryptionScheme encryption_scheme =
decoding_config->hasKeySystemConfiguration()
? media::EncryptionScheme::kCenc
: media::EncryptionScheme::kUnencrypted;
media::VideoDecoderConfig::AlphaMode alpha_mode =
media::VideoDecoderConfig::AlphaMode::kIsOpaque;
// A few things aren't known until demuxing time. These include: coded size,
// visible rect, and extra data. Make reasonable guesses below. Ideally the
// differences won't be make/break GPU acceleration support.
VideoConfiguration* video_config = decoding_config->video();
gfx::Size natural_size(video_config->width(), video_config->height());
media::VideoDecoderConfig config(
video_codec, video_profile, alpha_mode, video_color_space,
media::VideoTransformation(), natural_size /* coded_size */,
gfx::Rect(natural_size) /* visible_rect */, natural_size,
media::EmptyExtraData(), encryption_scheme);
static_assert(media::VideoDecoderImplementation::kAlternate ==
media::VideoDecoderImplementation::kMaxValue,
"Keep the array below in sync.");
media::VideoDecoderImplementation decoder_impls[] = {
media::VideoDecoderImplementation::kDefault,
media::VideoDecoderImplementation::kAlternate};
media::GpuVideoAcceleratorFactories::Supported supported =
media::GpuVideoAcceleratorFactories::Supported::kUnknown;
for (const auto& impl : decoder_impls) {
supported = gpu_factories->IsDecoderConfigSupported(impl, config);
DCHECK_NE(supported,
media::GpuVideoAcceleratorFactories::Supported::kUnknown);
if (supported == media::GpuVideoAcceleratorFactories::Supported::kTrue)
break;
}
OnGpuFactoriesSupport(
callback_id,
supported == media::GpuVideoAcceleratorFactories::Supported::kTrue);
}
void MediaCapabilities::ResolveCallbackIfReady(int callback_id) {
DCHECK(pending_cb_map_.Contains(callback_id));
PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
......@@ -1120,6 +1219,11 @@ void MediaCapabilities::ResolveCallbackIfReady(int callback_id) {
!pending_cb->is_bad_window_prediction_smooth.has_value())
return;
if (UseGpuFactoriesForPowerEfficient() &&
!pending_cb->is_gpu_factories_supported.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
......@@ -1132,7 +1236,12 @@ void MediaCapabilities::ResolveCallbackIfReady(int callback_id) {
MediaCapabilitiesDecodingInfo::Create());
info->setSupported(true);
info->setKeySystemAccess(pending_cb->key_system_access);
info->setPowerEfficient(*pending_cb->db_is_power_efficient);
if (UseGpuFactoriesForPowerEfficient()) {
info->setPowerEfficient(*pending_cb->is_gpu_factories_supported);
} else {
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() ||
......@@ -1224,6 +1333,17 @@ void MediaCapabilities::OnPerfHistoryInfo(int callback_id,
ResolveCallbackIfReady(callback_id);
}
void MediaCapabilities::OnGpuFactoriesSupport(int callback_id,
bool is_supported) {
DVLOG(2) << __func__ << " is_supported:" << is_supported;
DCHECK(pending_cb_map_.Contains(callback_id));
PendingCallbackState* pending_cb = pending_cb_map_.at(callback_id);
pending_cb->is_gpu_factories_supported = is_supported;
ResolveCallbackIfReady(callback_id);
}
int MediaCapabilities::CreateCallbackId() {
++last_callback_id_;
return last_callback_id_;
......
......@@ -60,6 +60,7 @@ class MODULES_EXPORT MediaCapabilities final : public ScriptWrappable {
base::Optional<bool> is_nnr_prediction_smooth;
base::Optional<bool> db_is_smooth;
base::Optional<bool> db_is_power_efficient;
base::Optional<bool> is_gpu_factories_supported;
base::TimeTicks request_time;
};
......@@ -74,6 +75,7 @@ class MODULES_EXPORT MediaCapabilities final : public ScriptWrappable {
ScriptPromise GetEmeSupport(ScriptState*,
media::VideoCodec,
media::VideoCodecProfile,
media::VideoColorSpace,
const MediaDecodingConfiguration*,
const base::TimeTicks& request_time,
ExceptionState&);
......@@ -81,7 +83,8 @@ class MODULES_EXPORT MediaCapabilities final : public ScriptWrappable {
// parallel request to GetPerfInfo_ML() when learning experiment is enabled.
void GetPerfInfo(media::VideoCodec,
media::VideoCodecProfile,
const VideoConfiguration*,
media::VideoColorSpace,
const MediaDecodingConfiguration*,
const base::TimeTicks& request_time,
ScriptPromiseResolver*,
MediaKeySystemAccess*);
......@@ -94,6 +97,15 @@ class MODULES_EXPORT MediaCapabilities final : public ScriptWrappable {
int width,
double framerate);
// Query media::GpuVideoAcceleratorFactories for support of hardware
// accelerate decode. Only called when |UseGpuFactoriesForPowerEfficient()|
// is true.
void GetGpuFactoriesSupport(int callback_id,
media::VideoCodec video_codec,
media::VideoCodecProfile video_profile,
media::VideoColorSpace,
const MediaDecodingConfiguration*);
// Callback for perf info from the VideoDecodePerfHistory service.
void OnPerfHistoryInfo(int callback_id,
bool is_smooth,
......@@ -109,6 +121,9 @@ class MODULES_EXPORT MediaCapabilities final : public ScriptWrappable {
int callback_id,
const base::Optional<::media::learning::TargetHistogram>& histogram);
// Callback for GetGpuFactoriesSupport().
void OnGpuFactoriesSupport(int callback_id, bool is_supported);
// Resolves the callback with associated |callback_id| and removes it from the
// |pending_callback_map_|.
void ResolveCallbackIfReady(int callback_id);
......
......@@ -9,6 +9,7 @@
#include <algorithm>
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "media/base/media_switches.h"
......@@ -20,6 +21,7 @@
#include "media/mojo/mojom/media_types.mojom-blink.h"
#include "media/mojo/mojom/video_decode_perf_history.mojom-blink.h"
#include "media/mojo/mojom/watch_time_recorder.mojom-blink.h"
#include "media/video/mock_gpu_video_accelerator_factories.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -27,11 +29,14 @@
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_capabilities_info.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_video_configuration.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
......@@ -43,7 +48,9 @@ using ::media::learning::FeatureValue;
using ::media::learning::ObservationCompletion;
using ::media::learning::TargetValue;
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::Unused;
namespace blink {
......@@ -213,6 +220,10 @@ class CallbackSaver {
nnr_cb_ = std::move(predict_cb);
}
void SaveGpuFactoriesNotifyCallback(base::OnceClosure cb) {
gpu_factories_notify_cb_ = std::move(cb);
}
MockPerfHistoryService::GetPerfInfoCallback& perf_history_cb() {
return perf_history_cb_;
}
......@@ -226,10 +237,23 @@ class CallbackSaver {
return nnr_cb_;
}
base::OnceClosure& gpu_factories_notify_cb() {
return gpu_factories_notify_cb_;
}
private:
MockPerfHistoryService::GetPerfInfoCallback perf_history_cb_;
MockLearningTaskControllerService::PredictDistributionCallback bad_window_cb_;
MockLearningTaskControllerService::PredictDistributionCallback nnr_cb_;
base::OnceClosure gpu_factories_notify_cb_;
};
class MockPlatform : public TestingPlatformSupport {
public:
MockPlatform() = default;
~MockPlatform() override = default;
MOCK_METHOD0(GetGpuFactories, media::GpuVideoAcceleratorFactories*());
};
// This would typically be a test fixture, but we need it to be
......@@ -300,14 +324,18 @@ class MediaCapabilitiesTestContext {
return nnr_service_.get();
}
MockPlatform& GetMockPlatform() { return *mock_platform_; }
void VerifyAndClearMockExpectations() {
testing::Mock::VerifyAndClearExpectations(GetPerfHistoryService());
testing::Mock::VerifyAndClearExpectations(GetNnrService());
testing::Mock::VerifyAndClearExpectations(GetBadWindowService());
testing::Mock::VerifyAndClearExpectations(&GetMockPlatform());
}
private:
V8TestingScope v8_scope_;
ScopedTestingPlatformSupport<MockPlatform> mock_platform_;
std::unique_ptr<MockPerfHistoryService> perf_history_service_;
std::unique_ptr<FakeMediaMetricsProvider> fake_metrics_provider_;
Persistent<MediaCapabilities> media_capabilities_;
......@@ -317,6 +345,7 @@ class MediaCapabilitiesTestContext {
// |kContentType|, |kCodec|, and |kCodecProfile| must match.
const char kContentType[] = "video/webm; codecs=\"vp09.00.10.08\"";
const char kAudioContentType[] = "audio/webm; codecs=\"opus\"";
const media::VideoCodecProfile kCodecProfile = media::VP9PROFILE_PROFILE0;
const media::VideoCodec kCodec = media::kCodecVP9;
const double kFramerate = 20.5;
......@@ -324,6 +353,16 @@ const int kWidth = 3840;
const int kHeight = 2160;
const int kBitrate = 2391000;
// Construct VideoConfig using the constants above.
MediaDecodingConfiguration* CreateAudioDecodingConfig() {
auto* audio_config = MakeGarbageCollected<AudioConfiguration>();
audio_config->setContentType(kAudioContentType);
auto* decoding_config = MakeGarbageCollected<MediaDecodingConfiguration>();
decoding_config->setType("media-source");
decoding_config->setAudio(audio_config);
return decoding_config;
}
// Construct VideoConfig using the constants above.
MediaDecodingConfiguration* CreateDecodingConfig() {
auto* video_config = MakeGarbageCollected<VideoConfiguration>();
......@@ -375,6 +414,7 @@ enum class PredictionType {
kDB,
kBadWindow,
kNnr,
kGpuFactories,
};
// Makes a TargetHistogram with single count at |target_value|.
......@@ -415,6 +455,10 @@ MlCallback(const Vector<media::learning::FeatureValue>& expected_features,
};
}
testing::Action<void(base::OnceClosure)> GpuFactoriesNotifyCallback() {
return [](base::OnceClosure cb) { std::move(cb).Run(); };
}
// Helper to constructs field trial params with given ML prediction thresholds.
base::FieldTrialParams MakeMlParams(double bad_window_threshold,
double nnr_threshold) {
......@@ -446,6 +490,16 @@ MediaCapabilitiesInfo* DecodingInfo(
} // namespace
TEST(MediaCapabilitiesTests, BasicAudio) {
MediaCapabilitiesTestContext context;
const MediaDecodingConfiguration* kDecodingConfig =
CreateAudioDecodingConfig();
MediaCapabilitiesInfo* info = DecodingInfo(kDecodingConfig, &context);
EXPECT_TRUE(info->supported());
EXPECT_TRUE(info->smooth());
EXPECT_TRUE(info->powerEfficient());
}
// Other tests will assume these match. Test to be sure they stay in sync.
TEST(MediaCapabilitiesTests, ConfigMatchesFeatures) {
const MediaDecodingConfiguration* kDecodingConfig = CreateDecodingConfig();
......@@ -531,6 +585,75 @@ TEST(MediaCapabilitiesTests, PredictWithJustDB) {
EXPECT_TRUE(info->powerEfficient());
}
TEST(MediaCapabilitiesTests, PredictPowerEfficientWithGpuFactories) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
// Enable GpuFactories for power predictions.
{media::kMediaCapabilitiesQueryGpuFactories},
// Disable ML predictions (may/may not be disabled by default).
{media::kMediaLearningSmoothnessExperiment});
MediaCapabilitiesTestContext context;
const auto* kDecodingConfig = CreateDecodingConfig();
const media::mojom::blink::PredictionFeatures kFeatures = CreateFeatures();
// Setup DB to return powerEfficient = false. We later verify that opposite
// response from GpuFactories overrides the DB.
EXPECT_CALL(*context.GetPerfHistoryService(), GetPerfInfo(_, _))
.WillOnce(DbCallback(kFeatures, /*smooth*/ false, /*power_eff*/ false));
auto mock_gpu_factories =
std::make_unique<media::MockGpuVideoAcceleratorFactories>(nullptr);
ON_CALL(context.GetMockPlatform(), GetGpuFactories())
.WillByDefault(Return(mock_gpu_factories.get()));
// First, lets simulate the scenario where we ask before support is known. The
// async path should notify us when the info arrives. We then get GpuFactroies
// again and learn the config is supported.
EXPECT_CALL(context.GetMockPlatform(), GetGpuFactories()).Times(2);
{
// InSequence because we EXPECT two calls to IsDecoderSupportKnown with
// different return values.
InSequence s;
EXPECT_CALL(*mock_gpu_factories, IsDecoderSupportKnown())
.WillOnce(Return(false));
EXPECT_CALL(*mock_gpu_factories, NotifyDecoderSupportKnown(_))
.WillOnce(GpuFactoriesNotifyCallback());
EXPECT_CALL(*mock_gpu_factories, IsDecoderSupportKnown())
.WillOnce(Return(true));
EXPECT_CALL(*mock_gpu_factories, IsDecoderConfigSupported(_, _))
.WillOnce(
Return(media::GpuVideoAcceleratorFactories::Supported::kTrue));
}
// Info should be powerEfficient, preferring response of GpuFactories over
// the DB.
MediaCapabilitiesInfo* info = DecodingInfo(kDecodingConfig, &context);
EXPECT_TRUE(info->powerEfficient());
EXPECT_FALSE(info->smooth());
context.VerifyAndClearMockExpectations();
testing::Mock::VerifyAndClearExpectations(mock_gpu_factories.get());
// Now expect a second query with support is already known to be false. Set
// DB to respond with the opposite answer.
EXPECT_CALL(*context.GetPerfHistoryService(), GetPerfInfo(_, _))
.WillOnce(DbCallback(kFeatures, /*smooth*/ false, /*power_eff*/ true));
EXPECT_CALL(context.GetMockPlatform(), GetGpuFactories());
EXPECT_CALL(*mock_gpu_factories, IsDecoderSupportKnown())
.WillOnce(Return(true));
EXPECT_CALL(*mock_gpu_factories, IsDecoderConfigSupported(_, _))
.WillRepeatedly(
Return(media::GpuVideoAcceleratorFactories::Supported::kFalse));
// Info should be NOT powerEfficient, preferring response of GpuFactories over
// the DB.
info = DecodingInfo(kDecodingConfig, &context);
EXPECT_FALSE(info->powerEfficient());
EXPECT_FALSE(info->smooth());
context.VerifyAndClearMockExpectations();
testing::Mock::VerifyAndClearExpectations(mock_gpu_factories.get());
}
// Test with smoothness predictions coming solely from "bad window" ML service.
TEST(MediaCapabilitiesTests, PredictWithBadWindowMLService) {
// Enable ML predictions with thresholds. -1 disables the NNR predictor.
......@@ -745,12 +868,18 @@ void RunCallbackPermutationTest(std::vector<PredictionType> callback_order) {
const double kBadWindowThreshold = 2;
const double kNnrThreshold = 3;
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
media::kMediaLearningSmoothnessExperiment,
MakeMlParams(kBadWindowThreshold, kNnrThreshold));
scoped_feature_list.InitWithFeaturesAndParameters(
// Enabled features w/ parameters
{{media::kMediaLearningSmoothnessExperiment,
MakeMlParams(kBadWindowThreshold, kNnrThreshold)},
{media::kMediaCapabilitiesQueryGpuFactories, {}}},
// Disabled features.
{});
MediaCapabilitiesTestContext context;
const auto* kDecodingConfig = CreateDecodingConfig();
auto mock_gpu_factories =
std::make_unique<media::MockGpuVideoAcceleratorFactories>(nullptr);
// DB and both ML services should be called. Save their callbacks.
CallbackSaver cb_saver;
......@@ -761,6 +890,26 @@ void RunCallbackPermutationTest(std::vector<PredictionType> callback_order) {
EXPECT_CALL(*context.GetNnrService(), PredictDistribution(_, _))
.WillOnce(Invoke(&cb_saver, &CallbackSaver::SaveNnrCallback));
// GpuFactories should also be called. Set it up to be async with arrival of
// support info. Save the "notify" callback.
EXPECT_CALL(context.GetMockPlatform(), GetGpuFactories())
.WillRepeatedly(Return(mock_gpu_factories.get()));
{
// InSequence because we EXPECT two calls to IsDecoderSupportKnown with
// different return values.
InSequence s;
EXPECT_CALL(*mock_gpu_factories, IsDecoderSupportKnown())
.WillOnce(Return(false));
EXPECT_CALL(*mock_gpu_factories, NotifyDecoderSupportKnown(_))
.WillOnce(
Invoke(&cb_saver, &CallbackSaver::SaveGpuFactoriesNotifyCallback));
EXPECT_CALL(*mock_gpu_factories, IsDecoderSupportKnown())
.WillOnce(Return(true));
EXPECT_CALL(*mock_gpu_factories, IsDecoderConfigSupported(_, _))
.WillRepeatedly(
Return(media::GpuVideoAcceleratorFactories::Supported::kFalse));
}
// Call decodingInfo() to kick off the calls to prediction services.
ScriptPromise promise = context.GetMediaCapabilities()->decodingInfo(
context.GetScriptState(), kDecodingConfig, context.GetExceptionState());
......@@ -769,7 +918,7 @@ void RunCallbackPermutationTest(std::vector<PredictionType> callback_order) {
// Callbacks should all be saved after mojo's pending tasks have run.
test::RunPendingTasks();
ASSERT_TRUE(cb_saver.perf_history_cb() && cb_saver.bad_window_cb() &&
cb_saver.nnr_cb());
cb_saver.nnr_cb() && cb_saver.gpu_factories_notify_cb());
// Complete callbacks in whatever order.
for (size_t i = 0; i < callback_order.size(); ++i) {
......@@ -784,9 +933,12 @@ void RunCallbackPermutationTest(std::vector<PredictionType> callback_order) {
case PredictionType::kNnr:
std::move(cb_saver.nnr_cb()).Run(MakeHistogram(kNnrThreshold + 0.5));
break;
case PredictionType::kGpuFactories:
std::move(cb_saver.gpu_factories_notify_cb()).Run();
break;
}
// Give mojo callbacks a chance to run.
// Give callbacks/tasks a chance to run.
test::RunPendingTasks();
// Promise should only be resolved once the final callback has run.
......@@ -805,15 +957,16 @@ void RunCallbackPermutationTest(std::vector<PredictionType> callback_order) {
// Smooth=false because NNR prediction exceeds threshold.
EXPECT_FALSE(info->smooth());
// DB predicted power_efficient = true.
EXPECT_TRUE(info->powerEfficient());
// DB predicted power_efficient = true, but GpuFactories overrides w/ false.
EXPECT_FALSE(info->powerEfficient());
}
// Test that decodingInfo() behaves correctly for all orderings/timings of the
// underlying prediction services.
TEST(MediaCapabilitiesTests, PredictionCallbackPermutations) {
std::vector<PredictionType> callback_order(
{PredictionType::kDB, PredictionType::kBadWindow, PredictionType::kNnr});
{PredictionType::kDB, PredictionType::kBadWindow, PredictionType::kNnr,
PredictionType::kGpuFactories});
do {
RunCallbackPermutationTest(callback_order);
} while (std::next_permutation(callback_order.begin(), callback_order.end()));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment