#include "media/gpu/android/codec_image_group.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread.h"
#include "gpu/command_buffer/service/ref_counted_lock_for_test.h"
#include "gpu/config/gpu_finch_features.h"
#include "media/base/android/mock_android_overlay.h"
#include "media/gpu/android/codec_surface_bundle.h"
#include "media/gpu/android/mock_codec_image.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
constexpr gfx::Size kMockImageSize(100, 100);
class CodecImageGroupWithDestructionHook : public CodecImageGroup {
public:
CodecImageGroupWithDestructionHook(
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<CodecSurfaceBundle> surface_bundle)
: CodecImageGroup(std::move(task_runner),
std::move(surface_bundle),
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr) {}
void SetDestructionCallback(base::OnceClosure cb) {
destruction_cb_ = std::move(cb);
}
private:
~CodecImageGroupWithDestructionHook() override {
if (destruction_cb_)
std::move(destruction_cb_).Run();
}
base::OnceClosure destruction_cb_;
};
}
class CodecImageGroupTest : public testing::Test {
public:
CodecImageGroupTest() = default;
void SetUp() override {
gpu_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
}
void TearDown() override {}
struct Record {
scoped_refptr<CodecSurfaceBundle> surface_bundle;
scoped_refptr<CodecImageGroupWithDestructionHook> image_group;
MockAndroidOverlay* overlay() const {
return static_cast<MockAndroidOverlay*>(surface_bundle->overlay());
}
};
Record CreateImageGroup() {
std::unique_ptr<MockAndroidOverlay> overlay =
std::make_unique<MockAndroidOverlay>();
EXPECT_CALL(*overlay.get(), MockAddSurfaceDestroyedCallback());
Record rec;
rec.surface_bundle =
base::MakeRefCounted<CodecSurfaceBundle>(std::move(overlay));
rec.image_group = base::MakeRefCounted<CodecImageGroupWithDestructionHook>(
gpu_task_runner_, rec.surface_bundle);
return rec;
}
MOCK_METHOD1(OnCodecImageDestroyed, void(CodecImage*));
base::test::TaskEnvironment env_;
scoped_refptr<base::TestSimpleTaskRunner> gpu_task_runner_;
};
TEST_F(CodecImageGroupTest, GroupRegistersForOverlayDestruction) {
Record rec = CreateImageGroup();
testing::Mock::VerifyAndClearExpectations(this);
ASSERT_FALSE(rec.surface_bundle->HasOneRef());
}
TEST_F(CodecImageGroupTest, SurfaceBundleWithoutOverlayDoesntCrash) {
scoped_refptr<CodecSurfaceBundle> surface_bundle =
base::MakeRefCounted<CodecSurfaceBundle>();
scoped_refptr<CodecImageGroup> image_group =
base::MakeRefCounted<CodecImageGroup>(
gpu_task_runner_, surface_bundle,
features::NeedThreadSafeAndroidMedia()
? base::MakeRefCounted<gpu::RefCountedLockForTest>()
: nullptr);
}
TEST_F(CodecImageGroupTest, ImagesRetainRefToGroup) {
Record rec = CreateImageGroup();
bool was_destroyed = false;
rec.image_group->SetDestructionCallback(
base::BindOnce([](bool* flag) -> void { *flag = true; }, &was_destroyed));
scoped_refptr<CodecImage> image = new MockCodecImage(kMockImageSize);
rec.image_group->AddCodecImage(image.get());
rec.image_group = nullptr;
ASSERT_FALSE(was_destroyed);
image = nullptr;
ASSERT_TRUE(was_destroyed);
}
TEST_F(CodecImageGroupTest, ImageGroupDropsForwardsSurfaceDestruction) {
Record rec = CreateImageGroup();
scoped_refptr<MockCodecImage> image_1 = new MockCodecImage(kMockImageSize);
scoped_refptr<MockCodecImage> image_2 = new MockCodecImage(kMockImageSize);
rec.image_group->AddCodecImage(image_1.get());
rec.image_group->AddCodecImage(image_2.get());
EXPECT_CALL(*image_1.get(), ReleaseCodecBuffer()).Times(0);
EXPECT_CALL(*image_2.get(), ReleaseCodecBuffer()).Times(0);
rec.overlay()->OnSurfaceDestroyed();
env_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*image_1.get(), ReleaseCodecBuffer()).Times(1);
EXPECT_CALL(*image_2.get(), ReleaseCodecBuffer()).Times(1);
gpu_task_runner_->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(this);
ASSERT_TRUE(rec.surface_bundle->HasOneRef());
}
}