blob: c7b1952f000028e2ff25a3a106a76d166f17c442 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "media/base/mock_filters.h"
#include "media/base/video_frame.h"
#include "media/video/gpu_video_accelerator_factories.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::WithArgs;
namespace blink {
namespace {
constexpr uint32_t kDefaultBitrate = 1280 * 720;
constexpr gfx::Size k720p{1280, 720};
constexpr gfx::Size k360p{640, 360};
constexpr size_t kChunkSize = 1234;
media::VideoEncoderOutput DefaultEncoderOutput() {
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kChunkSize);
output.size = kChunkSize;
output.key_frame = true;
return output;
}
MATCHER_P3(MatchEncoderOptions,
bitrate,
frame_size,
content_hint,
"encoder option matcher") {
return arg.bitrate.has_value() &&
arg.bitrate->mode() == media::Bitrate::Mode::kVariable &&
arg.bitrate->target_bps() == base::checked_cast<uint32_t>(bitrate) &&
*arg.content_hint == content_hint && arg.frame_size == frame_size;
}
MATCHER_P(MatchEncodeOption, key_frame, "encode option matcher") {
return arg.key_frame == key_frame && !arg.quantizer.has_value();
}
MATCHER_P(MatchStringSize, data_size, "encode data size matcher") {
return arg.size() == static_cast<size_t>(data_size);
}
MATCHER_P2(MatchVideoParams,
visible_rect_size,
video_codec,
"video_params matcher") {
return arg.visible_rect_size == visible_rect_size && arg.codec == video_codec;
}
MATCHER_P(MatchErrorCode, code, "error code matcher") {
return arg.code() == code;
}
} // namespace
// Wraps MockVideoEncoder to not delete the pointer of MockVideoEncoder by
// the std::unique_ptr.
class MockVideoEncoderWrapper : public media::VideoEncoder {
public:
explicit MockVideoEncoderWrapper(media::MockVideoEncoder* const mock_encoder,
base::OnceClosure dtor_cb)
: mock_encoder_(mock_encoder), dtor_cb_(std::move(dtor_cb)) {
CHECK(mock_encoder_);
}
~MockVideoEncoderWrapper() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(dtor_cb_).Run();
}
void Initialize(media::VideoCodecProfile profile,
const Options& options,
EncoderInfoCB info_cb,
OutputCB output_cb,
EncoderStatusCB done_cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return mock_encoder_->Initialize(profile, options, info_cb, output_cb,
std::move(done_cb));
}
void Encode(scoped_refptr<media::VideoFrame> frame,
const EncodeOptions& options,
EncoderStatusCB done_cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return mock_encoder_->Encode(std::move(frame), options, std::move(done_cb));
}
void ChangeOptions(const Options& options,
OutputCB output_cb,
EncoderStatusCB done_cb) override {
NOTREACHED();
}
void Flush(EncoderStatusCB done_cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return mock_encoder_->Flush(std::move(done_cb));
}
private:
const raw_ptr<media::MockVideoEncoder> mock_encoder_;
base::OnceClosure dtor_cb_;
SEQUENCE_CHECKER(sequence_checker_);
};
class MediaRecorderEncoderWrapperTest
: public ::testing::TestWithParam<media::VideoCodecProfile> {
public:
MediaRecorderEncoderWrapperTest()
: profile_(GetParam()),
codec_(media::VideoCodecProfileToVideoCodec(profile_)) {}
~MediaRecorderEncoderWrapperTest() override {
EXPECT_CALL(mock_encoder_, Dtor);
}
protected:
MOCK_METHOD(void, CreateEncoder, (), ());
MOCK_METHOD(void, OnError, (), ());
MOCK_METHOD(void, MockVideoEncoderWrapperDtor, (), ());
std::unique_ptr<media::VideoEncoder> CreateMockVideoEncoder(
media::GpuVideoAcceleratorFactories* /*gpu_factories*/) {
CreateEncoder();
return std::make_unique<MockVideoEncoderWrapper>(
&mock_encoder_,
base::BindOnce(
&MediaRecorderEncoderWrapperTest::MockVideoEncoderWrapperDtor,
base::Unretained(this)));
}
void CreateEncoderWrapper(bool is_screencast) {
encoder_wrapper_ = std::make_unique<MediaRecorderEncoderWrapper>(
scheduler::GetSingleThreadTaskRunnerForTesting(), profile_,
kDefaultBitrate, is_screencast,
/*gpu_factories=*/nullptr,
WTF::BindRepeating(
&MediaRecorderEncoderWrapperTest::CreateMockVideoEncoder,
base::Unretained(this)),
WTF::BindRepeating(&MediaRecorderEncoderWrapperTest::OnEncodedVideo,
base::Unretained(this)),
WTF::BindRepeating(&MediaRecorderEncoderWrapperTest::OnError,
base::Unretained(this)));
EXPECT_EQ(is_screencast,
encoder_wrapper_->IsScreenContentEncodingForTesting());
auto metrics_provider =
std::make_unique<media::MockVideoEncoderMetricsProvider>();
mock_metrics_provider_ = metrics_provider.get();
encoder_wrapper_->metrics_provider_ = std::move(metrics_provider);
SetupSuccessful720pEncoderInitialization();
}
// EncodeFrame is a private function of MediaRecorderEncoderWrapper.
// It can be called only in MediaRecorderEncoderWrapperTest.
void EncodeFrame(scoped_refptr<media::VideoFrame> frame,
base::TimeTicks capture_timestamp) {
encoder_wrapper_->EncodeFrame(std::move(frame), capture_timestamp, false);
}
MOCK_METHOD(
void,
OnEncodedVideo,
(const media::Muxer::VideoParameters& params,
std::string encoded_data,
std::string encoded_alpha,
std::optional<media::VideoEncoder::CodecDescription> codec_description,
base::TimeTicks capture_timestamp,
bool is_key_frame),
());
void SetupSuccessful720pEncoderInitialization() {
ON_CALL(mock_encoder_,
Initialize(
profile_,
MatchEncoderOptions(kDefaultBitrate, k720p,
media::VideoEncoder::ContentHint::Camera),
_, _, _))
.WillByDefault(WithArgs<3, 4>(
[this](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
this->output_cb = output_callback;
std::move(initialize_done_cb)
.Run(media::EncoderStatus::Codes::kOk);
}));
ON_CALL(mock_encoder_, Encode)
.WillByDefault(WithArgs<2>(
[this](media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output = DefaultEncoderOutput();
this->output_cb.Run(std::move(output), std::nullopt);
}));
ON_CALL(*mock_metrics_provider_,
MockInitialize(profile_, k720p, false,
media::SVCScalabilityMode::kL1T1))
.WillByDefault(Return());
}
test::TaskEnvironment task_environment_;
const media::VideoCodecProfile profile_;
const media::VideoCodec codec_;
media::VideoEncoder::OutputCB output_cb;
media::MockVideoEncoder mock_encoder_;
raw_ptr<media::MockVideoEncoderMetricsProvider, DanglingUntriaged>
mock_metrics_provider_;
std::unique_ptr<MediaRecorderEncoderWrapper> encoder_wrapper_;
};
TEST_P(MediaRecorderEncoderWrapperTest, InitializesAndEncodesOneFrame) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(*this, CreateEncoder);
EXPECT_CALL(*mock_metrics_provider_, MockInitialize);
EXPECT_CALL(mock_encoder_, Initialize);
EXPECT_CALL(mock_encoder_, Encode);
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(0), _, _,
/*key_frame=*/true));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest,
InitializesWithScreenCastAndEncodesOneFrame) {
CreateEncoderWrapper(true);
InSequence s;
EXPECT_CALL(*this, CreateEncoder);
EXPECT_CALL(*mock_metrics_provider_, MockInitialize);
ON_CALL(
mock_encoder_,
Initialize(profile_,
MatchEncoderOptions(kDefaultBitrate, k720p,
media::VideoEncoder::ContentHint::Screen),
_, _, _))
.WillByDefault(WithArgs<3, 4>(
[this](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
this->output_cb = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Encode);
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(0), _, _,
/*key_frame=*/true));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest,
EncodesTwoFramesWithoutRecreatingEncoder) {
CreateEncoderWrapper(false);
InSequence s;
const auto capture_timestamp1 = base::TimeTicks::Now();
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
// OnEncodedVideo to check capture_timestamp1.
EXPECT_CALL(*this,
OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize), MatchStringSize(0), _,
capture_timestamp1, /*key_frame=*/true));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p), capture_timestamp1);
const base::TimeTicks capture_timestamp2 =
capture_timestamp1 + base::Microseconds(1);
// Encode to check key_frame=false, and OnEncodedVideo to check
// key_frame=false and capture_timestamp2.
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(WithArgs<2>(
[this](media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output = DefaultEncoderOutput();
output.key_frame = false;
this->output_cb.Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this,
OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize), MatchStringSize(0), _,
capture_timestamp2, /*key_frame=*/false));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p), capture_timestamp2);
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest,
EncodeTwoFramesAndDelayEncodeDoneAndOutputCB) {
CreateEncoderWrapper(false);
InSequence s;
media::VideoEncoder::EncoderStatusCB encode_done_cb1;
const auto capture_timestamp1 = base::TimeTicks::Now();
const base::TimeTicks capture_timestamp2 =
capture_timestamp1 + base::Microseconds(1);
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([&encode_done_cb1](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
encode_done_cb1 = std::move(encode_done_cb);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(WithArgs<2>(
[this, encode_done_cb1_ptr = &encode_done_cb1](
media::VideoEncoder::EncoderStatusCB encode_done_cb2) {
std::move(*encode_done_cb1_ptr)
.Run(media::EncoderStatus::Codes::kOk);
std::move(encode_done_cb2).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output1 = DefaultEncoderOutput();
media::VideoEncoderOutput output2 = DefaultEncoderOutput();
output2.key_frame = false;
this->output_cb.Run(std::move(output1), std::nullopt);
this->output_cb.Run(std::move(output2), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this,
OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize), MatchStringSize(0), _,
capture_timestamp1, /*key_frame=*/true));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this,
OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize), MatchStringSize(0), _,
capture_timestamp2, /*key_frame=*/false));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p), capture_timestamp1);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p), capture_timestamp2);
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, RecreatesEncoderOnNewResolution) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(mock_encoder_, Flush)
.WillOnce(
WithArgs<0>([](media::VideoEncoder::EncoderStatusCB flush_done_cb) {
std::move(flush_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(*this, CreateEncoder);
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
EXPECT_CALL(
*mock_metrics_provider_,
MockInitialize(profile_, k360p, false, media::SVCScalabilityMode::kL1T1));
EXPECT_CALL(
mock_encoder_,
Initialize(profile_,
MatchEncoderOptions(kDefaultBitrate, k360p,
media::VideoEncoder::ContentHint::Camera),
_, _, _))
.WillOnce(WithArgs<3, 4>(
[this](media::VideoEncoder::OutputCB output_cb,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
this->output_cb = output_cb;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(WithArgs<2>(
[this](media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output = DefaultEncoderOutput();
this->output_cb.Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k360p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(0), _, _,
/*key_frame=*/true));
EncodeFrame(media::VideoFrame::CreateBlackFrame(k360p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, HandlesInitializeFailure) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(
mock_encoder_,
Initialize(profile_,
MatchEncoderOptions(kDefaultBitrate, k720p,
media::VideoEncoder::ContentHint::Camera),
_, _, _))
.WillOnce(WithArgs<4>(
[](media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
std::move(initialize_done_cb)
.Run(media::EncoderStatus::Codes::kEncoderInitializationError);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderInitializationError)));
EXPECT_CALL(*this, OnError);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, HandlesEncodeFailure) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(mock_encoder_, Encode(_, MatchEncodeOption(false), _))
.WillOnce(
WithArgs<2>([](media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb)
.Run(media::EncoderStatus::Codes::kEncoderFailedEncode);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderFailedEncode)));
EXPECT_CALL(*this, OnError);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, HandlesFlushFailure) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(mock_encoder_, Flush)
.WillOnce(
WithArgs<0>([](media::VideoEncoder::EncoderStatusCB flush_done_cb) {
std::move(flush_done_cb)
.Run(media::EncoderStatus::Codes::kEncoderFailedFlush);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderFailedFlush)));
EXPECT_CALL(*this, OnError);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EncodeFrame(media::VideoFrame::CreateBlackFrame(k360p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, NotCallOnEncodedVideoCBIfEncodeFail) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(mock_encoder_, Encode(_, MatchEncodeOption(false), _))
.WillOnce(WithArgs<2>(
[this](media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb)
.Run(media::EncoderStatus::Codes::kEncoderFailedEncode);
media::VideoEncoderOutput output = DefaultEncoderOutput();
this->output_cb.Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderFailedEncode)));
EXPECT_CALL(*this, OnError);
EXPECT_CALL(*this, OnEncodedVideo).Times(0);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest,
NotErrorCallbackTwiceByTwiceEncodeDoneFailure) {
CreateEncoderWrapper(false);
InSequence s;
media::VideoEncoder::EncoderStatusCB encode_done_cb1;
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([&encode_done_cb1](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
encode_done_cb1 = std::move(encode_done_cb);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(WithArgs<2>(
[encode_done_cb1_ptr = &encode_done_cb1](
media::VideoEncoder::EncoderStatusCB encode_done_cb2) {
std::move(*encode_done_cb1_ptr)
.Run(media::EncoderStatus::Codes::kEncoderFailedEncode);
std::move(encode_done_cb2)
.Run(media::EncoderStatus::Codes::kEncoderFailedEncode);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderFailedEncode)));
EXPECT_CALL(*this, OnError);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, IgnoresEncodeAfterFailure) {
CreateEncoderWrapper(false);
InSequence s;
EXPECT_CALL(
mock_encoder_,
Initialize(profile_,
MatchEncoderOptions(kDefaultBitrate, k720p,
media::VideoEncoder::ContentHint::Camera),
_, _, _))
.WillOnce(WithArgs<4>(
[](media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
std::move(initialize_done_cb)
.Run(media::EncoderStatus::Codes::kEncoderInitializationError);
}));
EXPECT_CALL(*mock_metrics_provider_,
MockSetError(MatchErrorCode(
media::EncoderStatus::Codes::kEncoderInitializationError)));
EXPECT_CALL(*this, OnError);
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EncodeFrame(media::VideoFrame::CreateBlackFrame(k360p),
base::TimeTicks::Now());
EncodeFrame(media::VideoFrame::CreateBlackFrame(k720p),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor);
}
TEST_P(MediaRecorderEncoderWrapperTest, InitializesAndEncodesOneAlphaFrame) {
InSequence s;
if (codec_ != media::VideoCodec::kVP8 && codec_ != media::VideoCodec::kVP9) {
GTEST_SKIP() << "no alpha encoding is supported in"
<< media::GetCodecName(codec_);
}
CreateEncoderWrapper(false);
constexpr size_t kAlphaChunkSize = 2345;
EXPECT_CALL(*this, CreateEncoder).Times(2);
EXPECT_CALL(*mock_metrics_provider_, MockInitialize);
media::VideoEncoder::OutputCB yuv_output_cb;
media::VideoEncoder::OutputCB alpha_output_cb;
EXPECT_CALL(mock_encoder_, Initialize(profile_, _, _, _, _))
.WillOnce(WithArgs<3, 4>(
[&](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
yuv_output_cb = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Initialize(profile_, _, _, _, _))
.WillOnce(WithArgs<3, 4>(
[&](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
alpha_output_cb = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([yuv_output_cb_ptr = &yuv_output_cb](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kChunkSize);
output.size = kChunkSize;
output.key_frame = true;
yuv_output_cb_ptr->Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([alpha_output_cb_ptr = &alpha_output_cb](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kAlphaChunkSize);
output.size = kAlphaChunkSize;
output.key_frame = true;
alpha_output_cb_ptr->Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(kAlphaChunkSize), _, _,
/*key_frame=*/true));
EncodeFrame(media::VideoFrame::CreateZeroInitializedFrame(
media::VideoPixelFormat::PIXEL_FORMAT_I420A, k720p,
gfx::Rect(k720p), k720p, base::TimeDelta()),
base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor).Times(2);
}
TEST_P(MediaRecorderEncoderWrapperTest,
InitializesAndEncodesOneOpaqueFrameAndOneAlphaFrame) {
InSequence s;
if (codec_ != media::VideoCodec::kVP8 && codec_ != media::VideoCodec::kVP9) {
GTEST_SKIP() << "no alpha encoding is supported in"
<< media::GetCodecName(codec_);
}
media::VideoEncoder::OutputCB yuv_output_cb1;
CreateEncoderWrapper(false);
EXPECT_CALL(*this, CreateEncoder);
EXPECT_CALL(*mock_metrics_provider_, MockInitialize);
EXPECT_CALL(mock_encoder_, Initialize(profile_, _, _, _, _))
.WillOnce(WithArgs<3, 4>(
[&](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
yuv_output_cb1 = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([yuv_output_cb_ptr = &yuv_output_cb1](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kChunkSize);
output.size = kChunkSize;
output.key_frame = true;
yuv_output_cb_ptr->Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(0), _, _,
/*key_frame=*/true));
EXPECT_CALL(mock_encoder_, Flush)
.WillOnce(
WithArgs<0>([](media::VideoEncoder::EncoderStatusCB flush_done_cb) {
std::move(flush_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
// Encode Opaque frame.
constexpr size_t kAlphaChunkSize = 2345;
EXPECT_CALL(*this, CreateEncoder).Times(2);
EXPECT_CALL(*mock_metrics_provider_, MockInitialize);
media::VideoEncoder::OutputCB yuv_output_cb2;
media::VideoEncoder::OutputCB alpha_output_cb;
EXPECT_CALL(mock_encoder_, Initialize(profile_, _, _, _, _))
.WillOnce(WithArgs<3, 4>(
[&](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
yuv_output_cb2 = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Initialize(profile_, _, _, _, _))
.WillOnce(WithArgs<3, 4>(
[&](media::VideoEncoder::OutputCB output_callback,
media::VideoEncoder::EncoderStatusCB initialize_done_cb) {
alpha_output_cb = output_callback;
std::move(initialize_done_cb).Run(media::EncoderStatus::Codes::kOk);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([yuv_output_cb_ptr = &yuv_output_cb2](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kChunkSize);
output.size = kChunkSize;
output.key_frame = true;
yuv_output_cb_ptr->Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(mock_encoder_, Encode)
.WillOnce(
WithArgs<2>([alpha_output_cb_ptr = &alpha_output_cb](
media::VideoEncoder::EncoderStatusCB encode_done_cb) {
std::move(encode_done_cb).Run(media::EncoderStatus::Codes::kOk);
media::VideoEncoderOutput output;
output.data = std::make_unique<uint8_t[]>(kAlphaChunkSize);
output.size = kAlphaChunkSize;
output.key_frame = true;
alpha_output_cb_ptr->Run(std::move(output), std::nullopt);
}));
EXPECT_CALL(*mock_metrics_provider_, MockIncrementEncodedFrameCount);
EXPECT_CALL(*this, OnEncodedVideo(MatchVideoParams(k720p, codec_),
MatchStringSize(kChunkSize),
MatchStringSize(kAlphaChunkSize), _, _,
/*key_frame=*/true));
auto opaque_frame = media::VideoFrame::CreateZeroInitializedFrame(
media::VideoPixelFormat::PIXEL_FORMAT_I420, k720p, gfx::Rect(k720p),
k720p, base::TimeDelta());
auto alpha_frame = media::VideoFrame::CreateZeroInitializedFrame(
media::VideoPixelFormat::PIXEL_FORMAT_I420A, k720p, gfx::Rect(k720p),
k720p, base::TimeDelta());
EncodeFrame(std::move(opaque_frame), base::TimeTicks::Now());
EncodeFrame(std::move(alpha_frame), base::TimeTicks::Now());
EXPECT_CALL(*this, MockVideoEncoderWrapperDtor).Times(2);
}
INSTANTIATE_TEST_SUITE_P(CodecProfile,
MediaRecorderEncoderWrapperTest,
::testing::Values(media::H264PROFILE_MIN,
media::VP8PROFILE_MIN,
media::VP9PROFILE_MIN,
media::AV1PROFILE_MIN));
} // namespace blink