From 9e683ce88cc1206d68fde45ec395a21dc1b5c556 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Mon, 4 Sep 2023 03:13:05 +0000 Subject: [PATCH] WebCodecs/VideoEncoder: Inject VideoEncoderMetricsProvider This CL collects the video encoder usage in webcodecs by using VideoEncoderMetricsProvider. Bug: b:275663480 Test: Media.VideoEncoderMetrics is shown in chrome://ukm in https://w3c.github.io/webcodecs/samples/capture-to-file/capture-to-file.html Change-Id: I82ff8a82216ba004c3bae4aa1c0c8ff9933b03ad Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4613331 Reviewed-by: Eugene Zemtsov Commit-Queue: Hirokazu Honda Cr-Commit-Position: refs/heads/main@{#1191920} --- .../modules/webcodecs/video_encoder.cc | 123 +++++++-- .../modules/webcodecs/video_encoder.h | 17 +- .../modules/webcodecs/video_encoder_test.cc | 254 ++++++++++++++++-- 3 files changed, 342 insertions(+), 52 deletions(-) diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc index e195a5c490051..2c3e732deaa95 100644 --- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc +++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc @@ -31,12 +31,14 @@ #include "media/base/video_encoder.h" #include "media/base/video_util.h" #include "media/media_buildflags.h" +#include "media/mojo/clients/mojo_video_encoder_metrics_provider.h" #include "media/video/gpu_video_accelerator_factories.h" #include "media/video/h264_level_limits.h" #include "media/video/offloading_video_encoder.h" #include "media/video/video_encode_accelerator_adapter.h" #include "media/video/video_encoder_fallback.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/task_type.h" @@ -61,6 +63,8 @@ #include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_support.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_video_pixel_format.h" #include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/streams/readable_stream.h" #include "third_party/blink/renderer/core/streams/writable_stream.h" #include "third_party/blink/renderer/modules/event_modules.h" @@ -670,7 +674,9 @@ std::unique_ptr CreateOpenH264VideoEncoder() { // with a weak |this|. It's needed in to avoid a persistent reference cycle. std::unique_ptr VideoEncoder::CreateSoftwareVideoEncoder( VideoEncoder* self, + bool fallback, media::VideoCodec codec) { + DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_); if (!self) return nullptr; std::unique_ptr result; @@ -690,31 +696,41 @@ std::unique_ptr VideoEncoder::CreateSoftwareVideoEncoder( } if (!result) return nullptr; + if (fallback) { + CHECK(self->encoder_metrics_provider_); + self->encoder_metrics_provider_->Initialize( + self->active_config_->profile, self->active_config_->options.frame_size, + /*is_hardware_encoder=*/false, + self->active_config_->options.scalability_mode.value_or( + media::SVCScalabilityMode::kL1T1)); + } return std::make_unique(std::move(result)); } std::unique_ptr VideoEncoder::CreateMediaVideoEncoder( const ParsedConfig& config, - media::GpuVideoAcceleratorFactories* gpu_factories) { + media::GpuVideoAcceleratorFactories* gpu_factories, + bool& is_platform_encoder) { + is_platform_encoder = true; if (config.hw_pref == HardwarePreference::kPreferHardware || config.hw_pref == HardwarePreference::kNoPreference || MayHaveOSSoftwareEncoder(config.profile)) { auto result = CreateAcceleratedVideoEncoder(config.profile, config.options, gpu_factories, config.hw_pref); - if (config.hw_pref == HardwarePreference::kPreferHardware) { return result; } else if (result) { // 'no-preference' or 'prefer-software' and we have OS software encoders. return std::make_unique( - std::move(result), - ConvertToBaseOnceCallback(CrossThreadBindOnce( - &VideoEncoder::CreateSoftwareVideoEncoder, - MakeUnwrappingCrossThreadWeakHandle(this), config.codec))); + std::move(result), ConvertToBaseOnceCallback(CrossThreadBindOnce( + &VideoEncoder::CreateSoftwareVideoEncoder, + MakeUnwrappingCrossThreadWeakHandle(this), + /*fallback=*/true, config.codec))); } } - return CreateSoftwareVideoEncoder(this, config.codec); + is_platform_encoder = false; + return CreateSoftwareVideoEncoder(this, /*fallback=*/false, config.codec); } void VideoEncoder::ContinueConfigureWithGpuFactories( @@ -723,15 +739,15 @@ void VideoEncoder::ContinueConfigureWithGpuFactories( DCHECK(active_config_); DCHECK_EQ(request->type, Request::Type::kConfigure); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - media_encoder_ = CreateMediaVideoEncoder(*active_config_, gpu_factories); + bool is_platform_encoder = false; + media_encoder_ = CreateMediaVideoEncoder(*active_config_, gpu_factories, + is_platform_encoder); if (!media_encoder_) { - HandleError(logger_->MakeOperationError( - "Encoder creation error.", - media::EncoderStatus( - media::EncoderStatus::Codes::kEncoderInitializationError, - "Unable to create encoder (most likely unsupported " - "codec/acceleration requirement combination)"))); + ReportError("Encoder creation error.", + media::EncoderStatus( + media::EncoderStatus::Codes::kEncoderInitializationError, + "Unable to create encoder (most likely unsupported " + "codec/acceleration requirement combination)")); request->EndTracing(); return; } @@ -771,8 +787,7 @@ void VideoEncoder::ContinueConfigureWithGpuFactories( break; } - self->HandleError( - self->logger_->MakeOperationError(error_message, std::move(status))); + self->ReportError(error_message.c_str(), std::move(status)); } else { UMA_HISTOGRAM_ENUMERATION("Blink.WebCodecs.VideoEncoder.Codec", codec, media::VideoCodec::kMaxValue); @@ -782,7 +797,14 @@ void VideoEncoder::ContinueConfigureWithGpuFactories( self->blocking_request_in_progress_ = false; self->ProcessRequests(); }; - + if (!encoder_metrics_provider_) { + encoder_metrics_provider_ = CreateVideoEncoderMetricsProvider(); + } + encoder_metrics_provider_->Initialize( + active_config_->profile, active_config_->options.frame_size, + is_platform_encoder, + active_config_->options.scalability_mode.value_or( + media::SVCScalabilityMode::kL1T1)); media_encoder_->Initialize( active_config_->profile, active_config_->options, std::move(info_cb), std::move(output_cb), @@ -791,6 +813,26 @@ void VideoEncoder::ContinueConfigureWithGpuFactories( MakeUnwrappingCrossThreadHandle(request), active_config_->codec))); } +std::unique_ptr +VideoEncoder::CreateVideoEncoderMetricsProvider() const { + mojo::PendingRemote + video_encoder_metrics_provider; + LocalDOMWindow* window = DomWindow(); + LocalFrame* local_frame = window ? window->GetFrame() : nullptr; + // There is no DOM frame if WebCodecs runs in a service worker. + if (local_frame) { + local_frame->GetBrowserInterfaceBroker().GetInterface( + video_encoder_metrics_provider.InitWithNewPipeAndPassReceiver()); + } else { + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( + video_encoder_metrics_provider.InitWithNewPipeAndPassReceiver()); + } + return base::MakeRefCounted( + media::mojom::VideoEncoderUseCase::kWebCodecs, + std::move(video_encoder_metrics_provider)) + ->CreateVideoEncoderMetricsProvider(); +} + bool VideoEncoder::CanReconfigure(ParsedConfig& original_config, ParsedConfig& new_config) { // Reconfigure is intended for things that don't require changing underlying @@ -814,6 +856,20 @@ void VideoEncoder::Trace(Visitor* visitor) const { Base::Trace(visitor); } +void VideoEncoder::ReportError(const char* error_message, + const media::EncoderStatus& status) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CHECK(!status.is_ok()); + // ReportError() can be called before |encoder_metrics_provider_| is created + // in media::VideoEncoder::Initialize() (e.g. there is no available + // media::VideoEncoder). Since the case is about webrtc::VideoEncoder failure, + // we don't record it. + if (encoder_metrics_provider_) { + encoder_metrics_provider_->SetError(status); + } + HandleError(logger_->MakeOperationError(error_message, status)); +} + bool VideoEncoder::ReadyToProcessNextRequest() { if (active_encodes_ >= max_active_encodes_) return false; @@ -1079,8 +1135,7 @@ void VideoEncoder::OnEncodeDone(Request* request, media::EncoderStatus status) { active_encodes_--; if (!status.is_ok()) { - HandleError( - logger_->MakeEncodingError("Encoding error.", std::move(status))); + ReportError("Encoding error.", std::move(status)); } request->EndTracing(); ProcessRequests(); @@ -1091,7 +1146,6 @@ void VideoEncoder::ProcessConfigure(Request* request) { DCHECK_EQ(request->type, Request::Type::kConfigure); DCHECK(active_config_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - request->StartTracing(); blocking_request_in_progress_ = true; @@ -1114,7 +1168,6 @@ void VideoEncoder::ProcessReconfigure(Request* request) { DCHECK(active_config_); DCHECK(media_encoder_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - request->StartTracing(); auto reconf_done_callback = [](VideoEncoder* self, Request* req, @@ -1142,6 +1195,7 @@ void VideoEncoder::ProcessReconfigure(Request* request) { auto flush_done_callback = [](VideoEncoder* self, Request* req, decltype(reconf_done_callback) reconf_callback, + bool is_platform_encoder, media::EncoderStatus status) { if (!self || self->reset_count_ != req->reset_count) { req->EndTracing(/*aborted=*/true); @@ -1149,8 +1203,7 @@ void VideoEncoder::ProcessReconfigure(Request* request) { } DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_); if (!status.is_ok()) { - self->HandleError(self->logger_->MakeOperationError( - "Encoder initialization error.", std::move(status))); + self->ReportError("Encoder initialization error.", std::move(status)); self->blocking_request_in_progress_ = false; req->EndTracing(); return; @@ -1165,6 +1218,15 @@ void VideoEncoder::ProcessReconfigure(Request* request) { MakeUnwrappingCrossThreadHandle(self->active_config_.Get()), self->reset_count_)); + if (!self->encoder_metrics_provider_) { + self->encoder_metrics_provider_ = + self->CreateVideoEncoderMetricsProvider(); + } + self->encoder_metrics_provider_->Initialize( + self->active_config_->profile, self->active_config_->options.frame_size, + is_platform_encoder, + self->active_config_->options.scalability_mode.value_or( + media::SVCScalabilityMode::kL1T1)); self->first_output_after_configure_ = true; self->media_encoder_->ChangeOptions( self->active_config_->options, std::move(output_cb), @@ -1174,14 +1236,15 @@ void VideoEncoder::ProcessReconfigure(Request* request) { }; blocking_request_in_progress_ = true; - media_encoder_->Flush(WTF::BindOnce(flush_done_callback, - MakeUnwrappingCrossThreadWeakHandle(this), - MakeUnwrappingCrossThreadHandle(request), - std::move(reconf_done_callback))); + media_encoder_->Flush(WTF::BindOnce( + flush_done_callback, MakeUnwrappingCrossThreadWeakHandle(this), + MakeUnwrappingCrossThreadHandle(request), std::move(reconf_done_callback), + is_platform_encoder_)); } void VideoEncoder::OnMediaEncoderInfoChanged( const media::VideoEncoderInfo& encoder_info) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (encoder_info.is_hardware_accelerated) ApplyCodecPressure(); else @@ -1193,6 +1256,7 @@ void VideoEncoder::OnMediaEncoderInfoChanged( log->SetProperty( encoder_info.is_hardware_accelerated); + is_platform_encoder_ = encoder_info.is_hardware_accelerated; max_active_encodes_ = ComputeMaxActiveEncodes(encoder_info.frame_delay, encoder_info.input_capacity); // We may have increased our capacity for active encodes. @@ -1204,6 +1268,7 @@ void VideoEncoder::CallOutputCallback( uint32_t reset_count, media::VideoEncoderOutput output, absl::optional codec_desc) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(active_config); if (!script_state_->ContextIsValid() || !output_callback_ || state_.AsEnum() != V8CodecState::Enum::kConfigured || @@ -1299,6 +1364,8 @@ void VideoEncoder::CallOutputCallback( metadata->setDecoderConfig(decoder_config); } + encoder_metrics_provider_->IncrementEncodedFrameCount(); + TRACE_EVENT_BEGIN1(kCategory, GetTraceNames()->output.c_str(), "timestamp", chunk->timestamp()); diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.h b/third_party/blink/renderer/modules/webcodecs/video_encoder.h index d108dd69abd17..232580fc804fb 100644 --- a/third_party/blink/renderer/modules/webcodecs/video_encoder.h +++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.h @@ -22,6 +22,7 @@ namespace media { class GpuVideoAcceleratorFactories; +class VideoEncoderMetricsProvider; class VideoEncoder; struct VideoEncoderOutput; } // namespace media @@ -88,6 +89,12 @@ class MODULES_EXPORT VideoEncoder : public EncoderBase { // GarbageCollected override. void Trace(Visitor*) const override; + void ReportError(const char* error_message, + const media::EncoderStatus& status); + + std::unique_ptr encoder_metrics_provider_ + GUARDED_BY_CONTEXT(sequence_checker_); + protected: using Base = EncoderBase; using ParsedConfig = VideoEncoderTraits::ParsedConfig; @@ -113,6 +120,7 @@ class MODULES_EXPORT VideoEncoder : public EncoderBase { scoped_refptr result_frame); static std::unique_ptr CreateSoftwareVideoEncoder( VideoEncoder* self, + bool fallback, media::VideoCodec codec); ParsedConfig* ParseConfig(const VideoEncoderConfig*, @@ -120,9 +128,13 @@ class MODULES_EXPORT VideoEncoder : public EncoderBase { bool VerifyCodecSupport(ParsedConfig*, ExceptionState&) override; // Virtual for UTs. + // Returns the VideoEncoder. virtual std::unique_ptr CreateMediaVideoEncoder( const ParsedConfig& config, - media::GpuVideoAcceleratorFactories* gpu_factories); + media::GpuVideoAcceleratorFactories* gpu_factories, + bool& is_platform_encoder); + virtual std::unique_ptr + CreateVideoEncoderMetricsProvider() const; void ContinueConfigureWithGpuFactories( Request* request, @@ -153,6 +165,9 @@ class MODULES_EXPORT VideoEncoder : public EncoderBase { // The current upper limit on |active_encodes_|. int max_active_encodes_; + // True if a running video encoder is hardware accelerated. + bool is_platform_encoder_ = false; + // Per-frame metadata to be applied to outputs, linked by timestamp. struct FrameMetadata { base::TimeDelta duration; diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc index 7a37f84ed369b..0e7019f3c629f 100644 --- a/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc +++ b/third_party/blink/renderer/modules/webcodecs/video_encoder_test.cc @@ -33,10 +33,18 @@ namespace blink { namespace { using testing::_; +using testing::ByMove; +using testing::DoAll; using testing::Invoke; using testing::Return; +using testing::SaveArg; using testing::WithArgs; +ACTION_P(RunClosure, closure) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask(FROM_HERE, + std::move(closure)); +} + class MockVideoEncoder : public VideoEncoder { public: MockVideoEncoder(ScriptState* script_state, @@ -48,8 +56,13 @@ class MockVideoEncoder : public VideoEncoder { MOCK_METHOD(std::unique_ptr, CreateMediaVideoEncoder, (const ParsedConfig& config, - media::GpuVideoAcceleratorFactories* gpu_factories), + media::GpuVideoAcceleratorFactories* gpu_factories, + bool& is_platform_encoder), (override)); + MOCK_METHOD(std::unique_ptr, + CreateVideoEncoderMetricsProvider, + (), + (const)); // CallOnMediaENcoderInfoChanged() is necessary for VideoEncoderTest to call // VideoEncoder::OnMediaEncoderInfoChanged() because the function is a private @@ -66,11 +79,13 @@ class VideoEncoderTest : public testing::Test { ~VideoEncoderTest() override = default; }; +constexpr gfx::Size kEncodeSize(80, 60); + VideoEncoderConfig* CreateConfig() { auto* config = MakeGarbageCollected(); config->setCodec("vp8"); - config->setWidth(80); - config->setHeight(60); + config->setWidth(kEncodeSize.width()); + config->setHeight(kEncodeSize.height()); return config; } @@ -201,16 +216,17 @@ TEST_F(VideoEncoderTest, CodecReclamation) { auto media_encoder = std::make_unique(); media::MockVideoEncoder* mock_media_encoder = media_encoder.get(); - EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _)) - .WillOnce(::testing::DoAll( - ::testing::Invoke([encoder = encoder]() { - media::VideoEncoderInfo info; - info.implementation_name = "MockEncoderName"; - info.is_hardware_accelerated = true; - encoder->CallOnMediaEncoderInfoChanged(info); - }), - ::testing::Return(::testing::ByMove(std::move(media_encoder))))); - + EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _, _)) + .WillOnce(DoAll(Invoke([encoder = encoder]() { + media::VideoEncoderInfo info; + info.implementation_name = "MockEncoderName"; + info.is_hardware_accelerated = true; + encoder->CallOnMediaEncoderInfoChanged(info); + }), + Return(ByMove(std::move(media_encoder))))); + EXPECT_CALL(*encoder, CreateVideoEncoderMetricsProvider()) + .WillOnce(Return(ByMove( + std::make_unique()))); EXPECT_CALL(*mock_media_encoder, Initialize(_, _, _, _, _)) .WillOnce(WithArgs<4>( Invoke([quit_closure = run_loop.QuitClosure()]( @@ -240,16 +256,14 @@ TEST_F(VideoEncoderTest, CodecReclamation) { auto media_encoder = std::make_unique(); media::MockVideoEncoder* mock_media_encoder = media_encoder.get(); - EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _)) - .WillOnce(::testing::DoAll( - ::testing::Invoke([encoder = encoder]() { - media::VideoEncoderInfo info; - info.implementation_name = "MockEncoderName"; - info.is_hardware_accelerated = false; - encoder->CallOnMediaEncoderInfoChanged(info); - }), - ::testing::Return(::testing::ByMove(std::move(media_encoder))))); - + EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _, _)) + .WillOnce(DoAll(Invoke([encoder = encoder]() { + media::VideoEncoderInfo info; + info.implementation_name = "MockEncoderName"; + info.is_hardware_accelerated = false; + encoder->CallOnMediaEncoderInfoChanged(info); + }), + Return(ByMove(std::move(media_encoder))))); EXPECT_CALL(*mock_media_encoder, Initialize(_, _, _, _, _)) .WillOnce(WithArgs<4>( Invoke([quit_closure = run_loop.QuitClosure()]( @@ -272,6 +286,200 @@ TEST_F(VideoEncoderTest, CodecReclamation) { ASSERT_EQ(0u, decoder_pressure_manager->pressure_for_testing()); } +TEST_F( + VideoEncoderTest, + ConfigureAndEncode_CallVideoEncoderMetricsProviderInitializeAndIncrementEncodedFrameCount) { + V8TestingScope v8_scope; + auto& es = v8_scope.GetExceptionState(); + auto* script_state = v8_scope.GetScriptState(); + + MockFunctionScope mock_function(script_state); + + // Create a video encoder. + auto* init = + CreateInit(mock_function.ExpectCall(), mock_function.ExpectNoCall()); + auto* encoder = CreateMockEncoder(script_state, init, es); + + auto* config = CreateConfig(); + base::RunLoop run_loop; + media::VideoEncoder::OutputCB output_cb; + auto media_encoder = std::make_unique(); + media::MockVideoEncoder* mock_media_encoder = media_encoder.get(); + auto encoder_metrics_provider = + std::make_unique(); + media::MockVideoEncoderMetricsProvider* mock_encoder_metrics_provider = + encoder_metrics_provider.get(); + EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _, _)) + .WillOnce(DoAll(Invoke([encoder = encoder]() { + media::VideoEncoderInfo info; + info.implementation_name = "MockEncoderName"; + info.is_hardware_accelerated = false; + encoder->CallOnMediaEncoderInfoChanged(info); + }), + Return(ByMove(std::move(media_encoder))))); + EXPECT_CALL(*encoder, CreateVideoEncoderMetricsProvider()) + .WillOnce(Return(ByMove(std::move(encoder_metrics_provider)))); + EXPECT_CALL( + *mock_encoder_metrics_provider, + MockInitialize(media::VideoCodecProfile::VP8PROFILE_ANY, kEncodeSize, + false, media::SVCScalabilityMode::kL1T1)); + EXPECT_CALL(*mock_media_encoder, Initialize(_, _, _, _, _)) + .WillOnce(DoAll( + SaveArg<3>(&output_cb), + WithArgs<4>(Invoke([quit_closure = run_loop.QuitClosure()]( + media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, WTF::BindOnce(std::move(done_cb), + media::EncoderStatus::Codes::kOk)); + })))); + encoder->configure(config, es); + EXPECT_CALL(*mock_media_encoder, Encode(_, _, _)) + .WillOnce( + WithArgs<2>(Invoke([output_cb = &output_cb]( + media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, WTF::BindOnce(std::move(done_cb), + media::EncoderStatus::Codes::kOk)); + media::VideoEncoderOutput out; + out.data = std::make_unique(100); + out.size = 100; + out.key_frame = true; + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, + WTF::BindOnce(*output_cb, std::move(out), absl::nullopt)); + }))); + + EXPECT_CALL(*mock_encoder_metrics_provider, MockIncrementEncodedFrameCount()) + .WillOnce([quit_closure = run_loop.QuitClosure()] { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, std::move(quit_closure)); + }); + encoder->encode( + MakeVideoFrame(script_state, config->width(), config->height(), 1), + MakeGarbageCollected(), es); + run_loop.Run(); +} + +TEST_F(VideoEncoderTest, + ConfigureTwice_CallVideoEncoderMetricsProviderInitializeTwice) { + V8TestingScope v8_scope; + auto& es = v8_scope.GetExceptionState(); + auto* script_state = v8_scope.GetScriptState(); + + MockFunctionScope mock_function(script_state); + + // Create a video encoder. + auto* init = + CreateInit(mock_function.ExpectNoCall(), mock_function.ExpectNoCall()); + auto* encoder = CreateMockEncoder(script_state, init, es); + + auto* config = CreateConfig(); + base::RunLoop run_loop; + media::VideoEncoder::OutputCB output_cb; + auto media_encoder = std::make_unique(); + media::MockVideoEncoder* mock_media_encoder = media_encoder.get(); + auto encoder_metrics_provider = + std::make_unique(); + media::MockVideoEncoderMetricsProvider* mock_encoder_metrics_provider = + encoder_metrics_provider.get(); + EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _, _)) + .WillOnce(DoAll(Invoke([encoder = encoder]() { + media::VideoEncoderInfo info; + info.implementation_name = "MockEncoderName"; + info.is_hardware_accelerated = false; + encoder->CallOnMediaEncoderInfoChanged(info); + }), + Return(ByMove(std::move(media_encoder))))); + EXPECT_CALL(*encoder, CreateVideoEncoderMetricsProvider()) + .WillOnce(Return(ByMove(std::move(encoder_metrics_provider)))); + EXPECT_CALL( + *mock_encoder_metrics_provider, + MockInitialize(media::VideoCodecProfile::VP8PROFILE_ANY, kEncodeSize, + false, media::SVCScalabilityMode::kL1T1)); + EXPECT_CALL(*mock_media_encoder, Initialize(_, _, _, _, _)) + .WillOnce(DoAll( + SaveArg<3>(&output_cb), + WithArgs<4>(Invoke([](media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, WTF::BindOnce(std::move(done_cb), + media::EncoderStatus::Codes::kOk)); + })))); + encoder->configure(config, es); + EXPECT_CALL(*mock_media_encoder, Flush(_)) + .WillOnce([](media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, WTF::BindOnce(std::move(done_cb), + media::EncoderStatus::Codes::kOk)); + }); + EXPECT_CALL( + *mock_encoder_metrics_provider, + MockInitialize(media::VideoCodecProfile::VP8PROFILE_ANY, kEncodeSize, + false, media::SVCScalabilityMode::kL1T1)); + EXPECT_CALL(*mock_media_encoder, ChangeOptions(_, _, _)) + .WillOnce( + WithArgs<2>(Invoke([quit_closure = run_loop.QuitClosure()]( + media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, WTF::BindOnce(std::move(done_cb), + media::EncoderStatus::Codes::kOk)); + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, std::move(quit_closure)); + }))); + encoder->configure(config, es); + run_loop.Run(); +} + +TEST_F(VideoEncoderTest, + InitializeFailure_CallVieoEncoderMetricsProviderSetError) { + V8TestingScope v8_scope; + auto& es = v8_scope.GetExceptionState(); + auto* script_state = v8_scope.GetScriptState(); + + MockFunctionScope mock_function(script_state); + + // Create a video encoder. + auto* init = + CreateInit(mock_function.ExpectNoCall(), mock_function.ExpectCall()); + auto* encoder = CreateMockEncoder(script_state, init, es); + + auto* config = CreateConfig(); + base::RunLoop run_loop; + media::VideoEncoder::OutputCB output_cb; + auto media_encoder = std::make_unique(); + media::MockVideoEncoder* mock_media_encoder = media_encoder.get(); + auto encoder_metrics_provider = + std::make_unique(); + media::MockVideoEncoderMetricsProvider* mock_encoder_metrics_provider = + encoder_metrics_provider.get(); + EXPECT_CALL(*encoder, CreateMediaVideoEncoder(_, _, _)) + .WillOnce(DoAll(Invoke([encoder = encoder]() { + media::VideoEncoderInfo info; + info.implementation_name = "MockEncoderName"; + info.is_hardware_accelerated = false; + encoder->CallOnMediaEncoderInfoChanged(info); + }), + Return(ByMove(std::move(media_encoder))))); + EXPECT_CALL(*encoder, CreateVideoEncoderMetricsProvider()) + .WillOnce(Return(ByMove(std::move(encoder_metrics_provider)))); + EXPECT_CALL( + *mock_encoder_metrics_provider, + MockInitialize(media::VideoCodecProfile::VP8PROFILE_ANY, kEncodeSize, + false, media::SVCScalabilityMode::kL1T1)); + EXPECT_CALL(*mock_media_encoder, Initialize(_, _, _, _, _)) + .WillOnce( + WithArgs<4>(Invoke([quit_closure = run_loop.QuitClosure()]( + media::VideoEncoder::EncoderStatusCB done_cb) { + scheduler::GetSequencedTaskRunnerForTesting()->PostTask( + FROM_HERE, + WTF::BindOnce( + std::move(done_cb), + media::EncoderStatus::Codes::kEncoderUnsupportedConfig)); + }))); + EXPECT_CALL(*mock_encoder_metrics_provider, MockSetError(_)) + .WillOnce(RunClosure(run_loop.QuitClosure())); + encoder->configure(config, es); + run_loop.Run(); +} } // namespace } // namespace blink