#include "media/gpu/android/video_frame_factory_impl.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "gpu/command_buffer/service/mock_texture_owner.h"
#include "gpu/command_buffer/service/ref_counted_lock_for_test.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/android/test_destruction_observable.h"
#include "media/base/limits.h"
#include "media/gpu/android/codec_buffer_wait_coordinator.h"
#include "media/gpu/android/maybe_render_early_manager.h"
#include "media/gpu/android/mock_codec_image.h"
#include "media/gpu/android/mock_shared_image_video_provider.h"
#include "media/gpu/android/shared_image_video_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::test::RunOnceCallback;
using testing::_;
using testing::Return;
using testing::SaveArg;
namespace gpu {
class CommandBufferStub;
}
namespace media {
class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager {
public:
MOCK_METHOD1(SetSurfaceBundle, void(scoped_refptr<CodecSurfaceBundle>));
MOCK_METHOD1(AddCodecImage, void(scoped_refptr<CodecImageHolder>));
MOCK_METHOD0(MaybeRenderEarly, void());
};
class MockFrameInfoHelper : public FrameInfoHelper,
public DestructionObservable {
public:
void GetFrameInfo(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
FrameInfoReadyCB cb) override {
FrameInfo info;
info.coded_size = buffer_renderer->size();
info.visible_rect = gfx::Rect(info.coded_size);
std::move(cb).Run(std::move(buffer_renderer), info);
}
MOCK_CONST_METHOD0(IsStalled, bool());
};
class VideoFrameFactoryImplTest : public testing::Test {
public:
VideoFrameFactoryImplTest()
: task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
auto image_provider = std::make_unique<MockSharedImageVideoProvider>();
image_provider_raw_ = image_provider.get();
auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>();
mre_manager_raw_ = mre_manager.get();
auto info_helper = std::make_unique<MockFrameInfoHelper>();
info_helper_raw_ = info_helper.get();
impl_ = std::make_unique<VideoFrameFactoryImpl>(
task_runner_, gpu_preferences_, std::move(image_provider),
std::move(mre_manager), std::move(info_helper),
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr);
auto texture_owner =
base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>();
auto codec_buffer_wait_coordinator =
base::MakeRefCounted<CodecBufferWaitCoordinator>(
std::move(texture_owner),
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr);
impl_->SetCodecBufferWaitCorrdinatorForTesting(
std::move(codec_buffer_wait_coordinator));
}
~VideoFrameFactoryImplTest() override = default;
struct {
gfx::Size coded_size{100, 100};
gfx::Rect visible_rect{coded_size};
gfx::Size natural_size{coded_size};
gfx::ColorSpace color_space{gfx::ColorSpace::CreateSRGBLinear()};
} video_frame_params_;
void RequestVideoFrame() {
auto output_buffer = CodecOutputBuffer::CreateForTesting(
0, video_frame_params_.coded_size, video_frame_params_.color_space,
std::nullopt);
ASSERT_TRUE(VideoFrame::IsValidConfig(
PIXEL_FORMAT_ARGB, VideoFrame::STORAGE_OPAQUE,
video_frame_params_.coded_size, video_frame_params_.visible_rect,
video_frame_params_.natural_size));
output_buffer_raw_ = output_buffer.get();
output_buffer_raw_ = output_buffer.get();
EXPECT_CALL(*image_provider_raw_, MockRequestImage());
impl_->CreateVideoFrame(std::move(output_buffer), base::TimeDelta(),
video_frame_params_.natural_size,
base::NullCallback(), output_cb_.Get());
base::RunLoop().RunUntilIdle();
}
SharedImageVideoProvider::ImageRecord MakeImageRecord(
bool* release_cb_called_flag = nullptr) {
SharedImageVideoProvider::ImageRecord record;
gpu::SharedImageMetadata metadata;
metadata.format = viz::SinglePlaneFormat::kRGBA_8888;
metadata.size = video_frame_params_.coded_size;
metadata.color_space = video_frame_params_.color_space;
metadata.surface_origin = kTopLeft_GrSurfaceOrigin;
metadata.alpha_type = kOpaque_SkAlphaType;
metadata.usage = gpu::SharedImageUsageSet();
record.shared_image = gpu::ClientSharedImage::CreateForTesting(metadata);
if (release_cb_called_flag)
*release_cb_called_flag = false;
record.release_cb = base::BindOnce(
[](bool* flag, const gpu::SyncToken&) {
if (flag)
*flag = true;
},
base::Unretained(release_cb_called_flag));
auto codec_image =
base::MakeRefCounted<MockCodecImage>(gfx::Size(100, 100));
record.codec_image_holder = base::MakeRefCounted<CodecImageHolder>(
task_runner_, codec_image,
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr);
return record;
}
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
std::unique_ptr<VideoFrameFactoryImpl> impl_;
raw_ptr<MockMaybeRenderEarlyManager> mre_manager_raw_ = nullptr;
raw_ptr<MockSharedImageVideoProvider> image_provider_raw_ = nullptr;
raw_ptr<MockFrameInfoHelper> info_helper_raw_ = nullptr;
raw_ptr<CodecOutputBuffer> output_buffer_raw_ = nullptr;
base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_;
std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_;
gpu::GpuPreferences gpu_preferences_;
};
TEST_F(VideoFrameFactoryImplTest, ImageProviderInitFailure) {
EXPECT_CALL(*image_provider_raw_, Initialize_(_))
.Times(1)
.WillOnce(RunOnceCallback<0>(nullptr));
base::MockCallback<VideoFrameFactory::InitCB> init_cb;
EXPECT_CALL(init_cb, Run(scoped_refptr<gpu::TextureOwner>(nullptr)));
impl_->Initialize(VideoFrameFactory::OverlayMode::kDontRequestPromotionHints,
init_cb.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest,
SetSurfaceBundleForwardsToMaybeRenderEarlyManager) {
scoped_refptr<CodecSurfaceBundle> surface_bundle =
base::MakeRefCounted<CodecSurfaceBundle>(
base::MakeRefCounted<NiceMock<gpu::MockTextureOwner>>(),
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr);
EXPECT_CALL(*mre_manager_raw_, SetSurfaceBundle(surface_bundle));
impl_->SetSurfaceBundle(surface_bundle);
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, CreateVideoFrameFailsIfUnsupportedFormat) {
gfx::Size coded_size(limits::kMaxDimension + 1, limits::kMaxDimension + 1);
gfx::Rect visible_rect(coded_size);
gfx::Size natural_size(0, 0);
auto output_buffer = CodecOutputBuffer::CreateForTesting(
0, coded_size, gfx::ColorSpace(), std::nullopt);
ASSERT_FALSE(VideoFrame::IsValidConfig(PIXEL_FORMAT_ARGB,
VideoFrame::STORAGE_OPAQUE, coded_size,
visible_rect, natural_size));
base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb;
EXPECT_CALL(output_cb, Run(scoped_refptr<VideoFrame>(nullptr)));
EXPECT_CALL(*image_provider_raw_, MockRequestImage()).Times(0);
impl_->CreateVideoFrame(std::move(output_buffer), base::TimeDelta(),
natural_size, base::NullCallback(), output_cb.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, CreateVideoFrameSucceeds) {
RequestVideoFrame();
scoped_refptr<VideoFrame> frame;
EXPECT_CALL(output_cb_, Run(_)).WillOnce(SaveArg<0>(&frame));
bool release_cb_called_flag = false;
auto record = MakeImageRecord(&release_cb_called_flag);
scoped_refptr<CodecImage> codec_image(
record.codec_image_holder->codec_image_raw());
image_provider_raw_->ProvideOneRequestedImage(&record);
base::RunLoop().RunUntilIdle();
EXPECT_NE(frame, nullptr);
EXPECT_EQ(codec_image->get_codec_output_buffer_for_testing(),
output_buffer_raw_);
EXPECT_EQ(frame->coded_size(), video_frame_params_.coded_size);
EXPECT_EQ(frame->natural_size(), video_frame_params_.natural_size);
EXPECT_EQ(frame->visible_rect(), video_frame_params_.visible_rect);
EXPECT_EQ(frame->ColorSpace(), video_frame_params_.color_space);
EXPECT_FALSE(release_cb_called_flag);
frame = nullptr;
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(release_cb_called_flag);
}
TEST_F(VideoFrameFactoryImplTest,
DestroyingFactoryDuringVideoFrameCreationDoesntCrash) {
RequestVideoFrame();
impl_ = nullptr;
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, IsStalled) {
EXPECT_CALL(*info_helper_raw_, IsStalled()).WillOnce(Return(false));
EXPECT_FALSE(impl_->IsStalled());
EXPECT_CALL(*info_helper_raw_, IsStalled()).WillOnce(Return(true));
EXPECT_TRUE(impl_->IsStalled());
}
}