#include "media/capture/video/video_capture_buffer_pool.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/media/video_capture_controller.h"
#include "media/base/video_frame.h"
#include "media/capture/video/video_capture_buffer_pool_impl.h"
#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_MAC)
#include "ui/gfx/mac/io_surface.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <dxgi1_2.h>
#include <mfapi.h>
#include "media/base/win/dxgi_device_manager.h"
#endif
namespace content {
namespace {
size_t ImageAllocationSize(const media::VideoCaptureFormat& format) { … }
}
static constexpr media::VideoPixelFormat kCapturePixelFormats[] = …;
static constexpr media::VideoCaptureBufferType kVideoCaptureBufferTypes[] = …;
static constexpr int kTestBufferPoolSize = …;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
static constexpr gfx::Size kDefaultTextureSize = gfx::Size(1080, 720);
static constexpr int kInvalidId = -1;
static constexpr media::VideoPixelFormat kDefaultPixelFormat =
media::VideoPixelFormat::PIXEL_FORMAT_NV12;
static const media::VideoCaptureFormat kDefaultFormat =
media::VideoCaptureFormat(kDefaultTextureSize, 30, kDefaultPixelFormat);
static const gfx::ColorSpace kDefaultColorSpace = gfx::ColorSpace();
#endif
class VideoCaptureBufferPoolTest
: public testing::TestWithParam<
std::tuple<media::VideoPixelFormat, media::VideoCaptureBufferType>> { … };
TEST_P(VideoCaptureBufferPoolTest, BufferPool) { … }
#if BUILDFLAG(IS_WIN)
namespace {
gfx::GpuMemoryBufferHandle CreateHandle(ID3D11Device* d3d11_device) {
EXPECT_TRUE(d3d11_device != nullptr);
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = kDefaultTextureSize.width();
desc.Height = kDefaultTextureSize.height();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_NV12;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.SampleDesc.Count = 1;
desc.BindFlags = 0;
desc.MiscFlags =
D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
ID3D11Texture2D* texture = nullptr;
HRESULT hr = d3d11_device->CreateTexture2D(&desc, nullptr, &texture);
EXPECT_HRESULT_SUCCEEDED(hr);
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = texture->QueryInterface(IID_PPV_ARGS(&dxgi_resource));
EXPECT_HRESULT_SUCCEEDED(hr);
HANDLE texture_handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&texture_handle);
EXPECT_HRESULT_SUCCEEDED(hr);
gfx::GpuMemoryBufferHandle result;
result.type = gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE;
result.dxgi_handle.Set(texture_handle);
result.dxgi_token = gfx::DXGIHandleToken();
return result;
}
}
TEST_P(VideoCaptureBufferPoolTest, BufferPoolExternalWin) {
auto handle0 = CreateHandle(d3d11_device_);
auto handle1 = CreateHandle(d3d11_device_);
auto handle2 = CreateHandle(d3d11_device_);
int buffer_id_to_drop;
int buffer_id0 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle0), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id0),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id0, kInvalidId);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id0, 1);
pool_->RelinquishProducerReservation(buffer_id0);
int buffer_id1 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle1), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id1, kInvalidId);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1, 1);
pool_->RelinquishProducerReservation(buffer_id1);
pool_->RelinquishConsumerHold(buffer_id1, 1);
int buffer_id1_reuse = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id1), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1_reuse),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_EQ(buffer_id1, buffer_id1_reuse);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1_reuse, 1);
pool_->RelinquishProducerReservation(buffer_id1_reuse);
int buffer_id1_new = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id1), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1_new),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id1, buffer_id1_new);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1_new, 1);
pool_->RelinquishProducerReservation(buffer_id1_new);
pool_->RelinquishConsumerHold(buffer_id1_new, 1);
pool_->RelinquishConsumerHold(buffer_id1_reuse, 1);
int buffer_id2 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle2), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id2),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id0, buffer_id2);
EXPECT_NE(buffer_id1, buffer_id2);
EXPECT_NE(buffer_id1_new, buffer_id2);
EXPECT_EQ(buffer_id_to_drop, buffer_id1_new);
EXPECT_NE(buffer_id2, kInvalidId);
pool_->RelinquishConsumerHold(buffer_id0, 1);
int buffer_id0_reuse = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id0), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id0_reuse),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_EQ(buffer_id0, buffer_id0_reuse);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
}
#endif
#if BUILDFLAG(IS_MAC)
namespace {
gfx::GpuMemoryBufferHandle CreateIOSurfaceHandle() {
gfx::GpuMemoryBufferHandle result;
result.type = gfx::GpuMemoryBufferType::IO_SURFACE_BUFFER;
result.id = gfx::GpuMemoryBufferHandle::kInvalidId;
result.io_surface =
gfx::CreateIOSurface(kDefaultTextureSize, gfx::BufferFormat::BGRA_8888);
return result;
}
}
TEST_P(VideoCaptureBufferPoolTest, BufferPoolExternal) {
auto handle0 = CreateIOSurfaceHandle();
auto handle1 = CreateIOSurfaceHandle();
auto handle2 = CreateIOSurfaceHandle();
int buffer_id_to_drop;
int buffer_id0 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle0), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id0),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id0, kInvalidId);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
EXPECT_FALSE(IOSurfaceIsInUse(
pool_->GetGpuMemoryBufferHandle(buffer_id0).io_surface.get()));
pool_->HoldForConsumers(buffer_id0, 1);
EXPECT_TRUE(IOSurfaceIsInUse(
pool_->GetGpuMemoryBufferHandle(buffer_id0).io_surface.get()));
pool_->RelinquishProducerReservation(buffer_id0);
int buffer_id1 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle1), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id1, kInvalidId);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1, 1);
pool_->RelinquishProducerReservation(buffer_id1);
pool_->RelinquishConsumerHold(buffer_id1, 1);
int buffer_id1_reuse = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id1), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1_reuse),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_EQ(buffer_id1, buffer_id1_reuse);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1_reuse, 1);
pool_->RelinquishProducerReservation(buffer_id1_reuse);
int buffer_id1_new = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id1), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id1_new),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id1, buffer_id1_new);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
pool_->HoldForConsumers(buffer_id1_new, 1);
pool_->RelinquishProducerReservation(buffer_id1_new);
pool_->RelinquishConsumerHold(buffer_id1_new, 1);
pool_->RelinquishConsumerHold(buffer_id1_reuse, 1);
int buffer_id2 = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
std::move(handle2), kDefaultFormat, kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id2),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_NE(buffer_id0, buffer_id2);
EXPECT_NE(buffer_id1, buffer_id2);
EXPECT_NE(buffer_id1_new, buffer_id2);
EXPECT_NE(buffer_id2, kInvalidId);
EXPECT_EQ(buffer_id_to_drop, buffer_id1_new);
pool_->RelinquishConsumerHold(buffer_id0, 1);
int buffer_id0_reuse = kInvalidId;
EXPECT_EQ(pool_->ReserveIdForExternalBuffer(
media::CapturedExternalVideoBuffer(
pool_->GetGpuMemoryBufferHandle(buffer_id0), kDefaultFormat,
kDefaultColorSpace),
kDefaultTextureSize, &buffer_id_to_drop, &buffer_id0_reuse),
media::VideoCaptureDevice::Client::ReserveResult::kSucceeded);
EXPECT_EQ(buffer_id0, buffer_id0_reuse);
EXPECT_EQ(buffer_id_to_drop, kInvalidId);
}
#endif
INSTANTIATE_TEST_SUITE_P(…);
}