// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "media/gpu/windows/mf_video_processor_accelerator.h"
#include <d3d11.h>
#include <mfapi.h>
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl_dxgi.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/media_util.h"
#include "media/base/win/mf_helpers.h"
#include "media/base/win/mf_initializer.h"
#include "media/gpu/windows/media_foundation_video_encode_accelerator_win.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
class MFVideoProcessorAcceleratorTest : public ::testing::Test {
protected:
void SetUp() override {
dxgi_device_man_ = DXGIDeviceManager::Create({0, 0});
ASSERT_TRUE(dxgi_device_man_);
d3d11_device_ = dxgi_device_man_->GetDevice();
}
void TearDown() override {
ASSERT_HRESULT_SUCCEEDED(MFUnlockDXGIDeviceManager());
}
HRESULT CreateTexture(UINT width,
UINT height,
DXGI_FORMAT format,
BYTE* image_buffer,
ID3D11Texture2D** texture_out) {
D3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = format;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
desc.MiscFlags =
D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = image_buffer;
data.SysMemPitch = width * sizeof(RGBQUAD);
data.SysMemSlicePitch = 0;
return d3d11_device_->CreateTexture2D(&desc, &data, texture_out);
}
std::vector<BYTE> CreateRGBCheckerboard(UINT width,
UINT height,
RGBQUAD color0,
RGBQUAD color1) {
std::vector<BYTE> image(width * height * 4);
for (UINT y = 0; y < height; y++) {
for (UINT x = 0; x < width; x++) {
bool use_color0 =
((x % 4) < 2 && (y % 4) < 2) || ((x % 4) >= 2 && (y % 4) >= 2);
RGBQUAD& color = use_color0 ? color0 : color1;
image[y * width * 4 + x * 4 + 0] = color.rgbBlue;
image[y * width * 4 + x * 4 + 1] = color.rgbGreen;
image[y * width * 4 + x * 4 + 2] = color.rgbRed;
image[y * width * 4 + x * 4 + 3] = color.rgbReserved;
}
}
return image;
}
std::unique_ptr<gfx::GpuMemoryBuffer>
TextureToGpuMemoryBuffer(ID3D11Texture2D* texture, int width, int height) {
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
HRESULT hr = texture->QueryInterface(IID_PPV_ARGS(&dxgi_resource));
if (FAILED(hr)) {
return nullptr;
}
HANDLE shared_handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
nullptr, &shared_handle);
if (FAILED(hr)) {
return nullptr;
}
gfx::GpuMemoryBufferHandle gmb_handle;
gmb_handle.dxgi_handle.Set(shared_handle);
gmb_handle.dxgi_token = gfx::DXGIHandleToken();
gmb_handle.type = gfx::DXGI_SHARED_HANDLE;
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
gpu::GpuMemoryBufferImplDXGI::CreateFromHandle(
std::move(gmb_handle), {width, height},
gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::GPU_READ,
base::NullCallback(), nullptr, nullptr);
return gmb;
}
template <typename F>
void ValidateResult(ID3D11Texture2D* texture,
UINT width,
UINT height,
F validation_func) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
ASSERT_EQ(desc.Width, width);
ASSERT_EQ(desc.Height, height);
Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ASSERT_HRESULT_SUCCEEDED(
d3d11_device_->CreateTexture2D(&desc, nullptr, &staging_texture));
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->CopyResource(staging_texture.Get(), texture);
D3D11_MAPPED_SUBRESOURCE mapped_subresource;
ASSERT_HRESULT_SUCCEEDED(d3d11_context->Map(
staging_texture.Get(), 0, D3D11_MAP_READ, 0, &mapped_subresource));
BYTE* image = reinterpret_cast<BYTE*>(mapped_subresource.pData);
validation_func(image);
d3d11_context->Unmap(staging_texture.Get(), 0);
}
template <typename F>
void ValidateResult(IMFMediaBuffer* buffer, UINT size, F validation_func) {
MediaBufferScopedPointer scoped_buffer(buffer);
ASSERT_EQ(scoped_buffer.current_length(), size);
validation_func(scoped_buffer.get());
}
scoped_refptr<DXGIDeviceManager> dxgi_device_man_;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
UINT device_reset_token_ = 0;
static const RGBQUAD kGreen;
static const RGBQUAD kMagenta;
// These values are for BT709 with 16-235 nominal range
static const BYTE kLumaGreen = 173;
static const BYTE kLumaMagenta = 79;
};
const RGBQUAD MFVideoProcessorAcceleratorTest::kGreen = {0, 255, 0, 0};
const RGBQUAD MFVideoProcessorAcceleratorTest::kMagenta = {255, 0, 255, 0};
// This is unpleasant, but GTEST_SKIP must be run inside the actual
// test call or the test will not get skipped.
#define CheckForVideoDevice() \
Microsoft::WRL::ComPtr<ID3D11VideoDevice> d3d11_video_device; \
HRESULT hr_has_video_device = d3d11_device_.As(&d3d11_video_device); \
if (FAILED(hr_has_video_device)) { \
GTEST_SKIP() \
<< " gpu accelerated video processing not available on this platform"; \
}
TEST_F(MFVideoProcessorAcceleratorTest, RGBToNV12) {
CheckForVideoDevice();
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<MediaFoundationVideoProcessorAccelerator> video_processor;
video_processor = std::make_unique<MediaFoundationVideoProcessorAccelerator>(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
MediaFoundationVideoProcessorAccelerator::Config config;
config.input_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.input_visible_size = {kWidth, kHeight};
config.input_color_space = VideoColorSpace::REC709();
config.output_format = VideoPixelFormat::PIXEL_FORMAT_NV12;
config.output_visible_size = {kWidth, kHeight};
config.output_color_space = VideoColorSpace::REC709();
ASSERT_TRUE(video_processor->Initialize(config, dxgi_device_man_,
std::make_unique<NullMediaLog>()));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
ASSERT_HRESULT_SUCCEEDED(CreateTexture(
kWidth, kHeight, DXGI_FORMAT_B8G8R8A8_UNORM, image.data(), &texture));
// Flush graphics pipeline so initial texture data is available when
// texture is accessed through shared handle.
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->Flush();
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
TextureToGpuMemoryBuffer(texture.Get(), kWidth, kHeight);
ASSERT_TRUE(gmb);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
{kWidth, kHeight}, {kWidth, kHeight}, std::move(gmb), timestamp);
Microsoft::WRL::ComPtr<IMFSample> sample;
ASSERT_HRESULT_SUCCEEDED(video_processor->Convert(frame, &sample));
Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer;
ASSERT_HRESULT_SUCCEEDED(sample->GetBufferByIndex(0, &media_buffer));
Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
ASSERT_HRESULT_SUCCEEDED(media_buffer.As(&dxgi_buffer));
Microsoft::WRL::ComPtr<ID3D11Texture2D> output_texture;
ASSERT_HRESULT_SUCCEEDED(
dxgi_buffer->GetResource(IID_PPV_ARGS(&output_texture)));
ValidateResult(output_texture.Get(), kWidth, kHeight, [](BYTE* image) {
EXPECT_NEAR(image[0], kLumaGreen, 1);
EXPECT_NEAR(image[2], kLumaMagenta, 1);
});
}
TEST_F(MFVideoProcessorAcceleratorTest, RGBResize) {
CheckForVideoDevice();
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<MediaFoundationVideoProcessorAccelerator> video_processor;
video_processor = std::make_unique<MediaFoundationVideoProcessorAccelerator>(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
MediaFoundationVideoProcessorAccelerator::Config config;
config.input_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.input_visible_size = {kWidth, kHeight};
config.input_color_space = VideoColorSpace::REC709();
config.output_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.output_visible_size = {kWidth / 2, kHeight / 2};
config.output_color_space = VideoColorSpace::REC709();
ASSERT_TRUE(video_processor->Initialize(config, dxgi_device_man_,
std::make_unique<NullMediaLog>()));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
ASSERT_HRESULT_SUCCEEDED(CreateTexture(
kWidth, kHeight, DXGI_FORMAT_B8G8R8A8_UNORM, image.data(), &texture));
// Flush graphics pipeline so initial texture data is available when
// texture is accessed through shared handle.
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->Flush();
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
TextureToGpuMemoryBuffer(texture.Get(), kWidth, kHeight);
ASSERT_TRUE(gmb);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
{kWidth, kHeight}, {kWidth, kHeight}, std::move(gmb), timestamp);
Microsoft::WRL::ComPtr<IMFSample> sample;
ASSERT_HRESULT_SUCCEEDED(video_processor->Convert(frame, &sample));
Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer;
ASSERT_HRESULT_SUCCEEDED(sample->GetBufferByIndex(0, &media_buffer));
Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
ASSERT_HRESULT_SUCCEEDED(media_buffer.As(&dxgi_buffer));
Microsoft::WRL::ComPtr<ID3D11Texture2D> output_texture;
ASSERT_HRESULT_SUCCEEDED(
dxgi_buffer->GetResource(IID_PPV_ARGS(&output_texture)));
ValidateResult(output_texture.Get(), kWidth / 2, kHeight / 2,
[](BYTE* image) {
EXPECT_EQ(image[0], 0);
EXPECT_EQ(image[1], 255);
EXPECT_EQ(image[2], 0);
EXPECT_EQ(image[4], 255);
EXPECT_EQ(image[5], 0);
EXPECT_EQ(image[6], 255);
});
}
TEST_F(MFVideoProcessorAcceleratorTest, RGBToNV12Resize) {
CheckForVideoDevice();
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<MediaFoundationVideoProcessorAccelerator> video_processor;
video_processor = std::make_unique<MediaFoundationVideoProcessorAccelerator>(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
MediaFoundationVideoProcessorAccelerator::Config config;
config.input_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.input_visible_size = {kWidth, kHeight};
config.input_color_space = VideoColorSpace::REC709();
config.output_format = VideoPixelFormat::PIXEL_FORMAT_NV12;
config.output_visible_size = {kWidth / 2, kHeight / 2};
config.output_color_space = VideoColorSpace::REC709();
ASSERT_TRUE(video_processor->Initialize(config, dxgi_device_man_,
std::make_unique<NullMediaLog>()));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
ASSERT_HRESULT_SUCCEEDED(CreateTexture(
kWidth, kHeight, DXGI_FORMAT_B8G8R8A8_UNORM, image.data(), &texture));
// Flush graphics pipeline so initial texture data is available when
// texture is accessed through shared handle.
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->Flush();
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
TextureToGpuMemoryBuffer(texture.Get(), kWidth, kHeight);
ASSERT_TRUE(gmb);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
{kWidth, kHeight}, {kWidth, kHeight}, std::move(gmb), timestamp);
Microsoft::WRL::ComPtr<IMFSample> sample;
ASSERT_HRESULT_SUCCEEDED(video_processor->Convert(frame, &sample));
Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer;
ASSERT_HRESULT_SUCCEEDED(sample->GetBufferByIndex(0, &media_buffer));
Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
ASSERT_HRESULT_SUCCEEDED(media_buffer.As(&dxgi_buffer));
Microsoft::WRL::ComPtr<ID3D11Texture2D> output_texture;
ASSERT_HRESULT_SUCCEEDED(
dxgi_buffer->GetResource(IID_PPV_ARGS(&output_texture)));
ValidateResult(output_texture.Get(), kWidth / 2, kHeight / 2,
[](BYTE* image) {
EXPECT_NEAR(image[0], kLumaGreen, 1);
EXPECT_NE(image[1], kLumaGreen);
});
}
TEST_F(MFVideoProcessorAcceleratorTest, RGBToNV12SizeChange) {
CheckForVideoDevice();
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
ASSERT_HRESULT_SUCCEEDED(d3d11_device_.As(&dxgi_device));
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
ASSERT_HRESULT_SUCCEEDED(dxgi_device->GetAdapter(&dxgi_adapter));
DXGI_ADAPTER_DESC dxgi_adapter_desc;
ASSERT_HRESULT_SUCCEEDED(dxgi_adapter->GetDesc(&dxgi_adapter_desc));
constexpr UINT kVendorID_Qualcomm = 0x5143;
constexpr UINT kVendorID_Qualcomm_ACPI = 0x4D4F4351;
if (dxgi_adapter_desc.VendorId == kVendorID_Qualcomm ||
dxgi_adapter_desc.VendorId == kVendorID_Qualcomm_ACPI) {
// This test crashes inside the graphics driver on the
// win11-arm64-dbg-tests run. Until the graphics driver
// is updated for this run, disable this test.
GTEST_SKIP()
<< " Test crashes on win11-arm64-dbg-tests run on older drivers";
}
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<MediaFoundationVideoProcessorAccelerator> video_processor;
video_processor = std::make_unique<MediaFoundationVideoProcessorAccelerator>(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
MediaFoundationVideoProcessorAccelerator::Config config;
config.input_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.input_visible_size = {kWidth, kHeight};
config.input_color_space = VideoColorSpace::REC709();
config.output_format = VideoPixelFormat::PIXEL_FORMAT_NV12;
config.output_visible_size = {kWidth, kHeight};
config.output_color_space = VideoColorSpace::REC709();
ASSERT_TRUE(video_processor->Initialize(config, dxgi_device_man_,
std::make_unique<NullMediaLog>()));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
ASSERT_HRESULT_SUCCEEDED(CreateTexture(kWidth * 2, kHeight * 2,
DXGI_FORMAT_B8G8R8A8_UNORM,
image.data(), &texture));
// Flush graphics pipeline so initial texture data is available when
// texture is accessed through shared handle.
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->Flush();
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
TextureToGpuMemoryBuffer(texture.Get(), kWidth, kHeight);
ASSERT_TRUE(gmb);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
{kWidth, kHeight}, {kWidth, kHeight}, std::move(gmb), timestamp);
Microsoft::WRL::ComPtr<IMFSample> sample;
ASSERT_HRESULT_SUCCEEDED(video_processor->Convert(frame, &sample));
Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer;
ASSERT_HRESULT_SUCCEEDED(sample->GetBufferByIndex(0, &media_buffer));
Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
ASSERT_HRESULT_SUCCEEDED(media_buffer.As(&dxgi_buffer));
Microsoft::WRL::ComPtr<ID3D11Texture2D> output_texture;
ASSERT_HRESULT_SUCCEEDED(
dxgi_buffer->GetResource(IID_PPV_ARGS(&output_texture)));
ValidateResult(output_texture.Get(), kWidth, kHeight, [](BYTE* image) {
EXPECT_NEAR(image[0], (kLumaGreen + kLumaMagenta) / 2, 10);
EXPECT_NEAR(image[1], (kLumaGreen + kLumaMagenta) / 2, 10);
});
}
TEST_F(MFVideoProcessorAcceleratorTest, RGBToNV12CPU) {
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<MediaFoundationVideoProcessorAccelerator> video_processor;
video_processor = std::make_unique<MediaFoundationVideoProcessorAccelerator>(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
MediaFoundationVideoProcessorAccelerator::Config config;
config.input_format = VideoPixelFormat::PIXEL_FORMAT_XRGB;
config.input_visible_size = {kWidth, kHeight};
config.input_color_space = VideoColorSpace::REC709();
config.output_format = VideoPixelFormat::PIXEL_FORMAT_NV12;
config.output_visible_size = {kWidth, kHeight};
config.output_color_space = VideoColorSpace::REC709();
ASSERT_TRUE(video_processor->Initialize(config, nullptr,
std::make_unique<NullMediaLog>()));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalData(
VideoPixelFormat::PIXEL_FORMAT_XRGB, {kWidth, kHeight},
gfx::Rect(0, 0, kWidth, kHeight), {kWidth, kHeight}, image.data(),
image.size(), timestamp);
Microsoft::WRL::ComPtr<IMFSample> sample;
ASSERT_HRESULT_SUCCEEDED(video_processor->Convert(frame, &sample));
Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer;
ASSERT_HRESULT_SUCCEEDED(sample->GetBufferByIndex(0, &media_buffer));
ValidateResult(media_buffer.Get(), kWidth * kHeight * 3 / 2, [](BYTE* image) {
EXPECT_NEAR(image[0], kLumaGreen, 1);
EXPECT_NEAR(image[2], kLumaMagenta, 1);
});
}
class MockEncoderClient : public VideoEncodeAccelerator::Client {
public:
void RequireBitstreamBuffers(unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size) override {}
void BitstreamBufferReady(int32_t bitstream_buffer_id,
const BitstreamBufferMetadata& metadata) override {}
void NotifyErrorStatus(const EncoderStatus& status) override {
status_ = status;
}
void NotifyEncoderInfoChange(const VideoEncoderInfo& info) override {}
EncoderStatus GetLastStatus() { return status_; }
private:
EncoderStatus status_ = EncoderStatus::Codes::kOk;
};
#ifdef USE_WITH_ENCODER_TEST
TEST_F(MFVideoProcessorAcceleratorTest, RGBToH264) {
base::test::SingleThreadTaskEnvironment task_environment;
const UINT kWidth = 128;
const UINT kHeight = 128;
std::unique_ptr<VideoEncodeAccelerator> video_encoder;
video_encoder = base::WrapUnique<VideoEncodeAccelerator>(
new MediaFoundationVideoEncodeAccelerator(
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds(), {0}));
VideoEncodeAccelerator::Config config(
VideoPixelFormat::PIXEL_FORMAT_XRGB, {kWidth, kHeight}, H264PROFILE_MAIN,
Bitrate::ConstantBitrate(4000000u), 30,
VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer,
VideoEncodeAccelerator::Config::ContentType::kDisplay);
MockEncoderClient client;
ASSERT_TRUE(video_encoder->Initialize(config, &client,
std::make_unique<NullMediaLog>()));
const int32_t kBitstreamBufferId = 1;
const int32_t kShMemSize = 16384;
auto shmem = base::UnsafeSharedMemoryRegion::Create(kShMemSize);
BitstreamBuffer buffer(kBitstreamBufferId, std::move(shmem), kShMemSize,
0 /* offset */, base::TimeDelta());
video_encoder->UseOutputBitstreamBuffer(std::move(buffer));
std::vector<BYTE> image =
CreateRGBCheckerboard(kWidth, kHeight, kGreen, kMagenta);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
ASSERT_HRESULT_SUCCEEDED(CreateTexture(kWidth * 2, kHeight * 2,
DXGI_FORMAT_B8G8R8A8_UNORM,
image.data(), &texture));
// Flush graphics pipeline so initial texture data is available when
// texture is accessed through shared handle.
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_context;
d3d11_device_->GetImmediateContext(&d3d11_context);
d3d11_context->Flush();
std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
TextureToGpuMemoryBuffer(texture.Get(), kWidth, kHeight);
ASSERT_TRUE(gmb);
auto timestamp = base::Milliseconds(0);
auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
{kWidth, kHeight}, {kWidth, kHeight}, std::move(gmb), timestamp);
video_encoder->Encode(frame, false);
video_encoder = nullptr;
ASSERT_EQ(client.GetLastStatus(), EncoderStatus::Codes::kOk);
}
#endif
} // namespace media