blob: 2c6a3d55e7247b558d6d90eeeadaf158938fe9b9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
#include <memory>
#include "base/bind.h"
#include "base/mac/scoped_nsobject.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "media/base/video_types.h"
#include "media/capture/video/mac/sample_buffer_transformer_mac.h"
#include "media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h"
#include "media/capture/video/mac/test/pixel_buffer_test_utils_mac.h"
#include "media/capture/video/mac/test/video_capture_test_utils_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
using testing::_;
namespace media {
TEST(VideoCaptureDeviceAVFoundationMacTest,
OutputsNv12WithoutScalingByDefault) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kInCapturerScaling);
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
LOG(ERROR) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
bool has_received_first_frame = false;
base::RunLoop first_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_first_frame) {
// Ignore subsequent frames.
return;
}
EXPECT_EQ(frame.format.pixel_format, PIXEL_FORMAT_NV12);
EXPECT_TRUE(scaled_frames.empty());
has_received_first_frame = true;
first_frame_received.Quit();
}));
first_frame_received.Run();
[captureDevice stopCapture];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest,
SpecifiedScalingIsIgnoredWhenInCapturerScalingIsNotEnabled) {
base::test::ScopedFeatureList scoped_feature_list;
// By default, kInCapturerScaling is false.
EXPECT_FALSE(base::FeatureList::IsEnabled(kInCapturerScaling));
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
LOG(ERROR) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
std::vector<gfx::Size> scaled_resolutions;
scaled_resolutions.emplace_back(320, 240);
[captureDevice setScaledResolutions:scaled_resolutions];
ASSERT_TRUE([captureDevice startCapture]);
bool has_received_first_frame = false;
base::RunLoop first_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_first_frame) {
// Ignore subsequent frames.
return;
}
EXPECT_TRUE(scaled_frames.empty());
has_received_first_frame = true;
first_frame_received.Quit();
}));
first_frame_received.Run();
[captureDevice stopCapture];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest, SpecifiedScalingOutputsNv12) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kInCapturerScaling);
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
LOG(ERROR) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
std::vector<gfx::Size> scaled_resolutions;
scaled_resolutions.emplace_back(320, 240);
[captureDevice setScaledResolutions:scaled_resolutions];
ASSERT_TRUE([captureDevice startCapture]);
bool has_received_first_frame = false;
base::RunLoop first_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_first_frame) {
// Ignore subsequent frames.
return;
}
EXPECT_EQ(frame.format.pixel_format, PIXEL_FORMAT_NV12);
ASSERT_EQ(scaled_frames.size(), 1u);
EXPECT_EQ(scaled_frames[0].format.frame_size,
scaled_resolutions[0]);
EXPECT_EQ(scaled_frames[0].format.pixel_format,
PIXEL_FORMAT_NV12);
has_received_first_frame = true;
first_frame_received.Quit();
}));
first_frame_received.Run();
[captureDevice stopCapture];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest,
SpecifiedScalingCanChangeDuringCapture) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kInCapturerScaling);
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
LOG(ERROR) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
// Start capture without scaling and wait until frames are flowing.
[captureDevice setScaledResolutions:{}];
ASSERT_TRUE([captureDevice startCapture]);
bool has_received_first_frame = false;
base::RunLoop first_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_first_frame) {
// Ignore subsequent frames.
return;
}
EXPECT_TRUE(scaled_frames.empty());
has_received_first_frame = true;
first_frame_received.Quit();
}));
first_frame_received.Run();
// Specify scaling and wait for scaled frames to arrive.
std::vector<gfx::Size> scaled_resolutions;
scaled_resolutions.emplace_back(320, 240);
[captureDevice setScaledResolutions:scaled_resolutions];
bool has_received_scaled_frame = false;
base::RunLoop scaled_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_scaled_frame || scaled_frames.empty()) {
// Ignore subsequent frames.
return;
}
has_received_scaled_frame = true;
scaled_frame_received.Quit();
}));
scaled_frame_received.Run();
[captureDevice stopCapture];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest,
SpecifiedScalingUsesGoodSizesButNotBadSizes) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kInCapturerScaling);
RunTestCase(base::BindOnce([] {
VideoCaptureDeviceFactoryMac video_capture_device_factory;
std::vector<VideoCaptureDeviceInfo> device_infos =
GetDevicesInfo(&video_capture_device_factory);
if (device_infos.empty()) {
LOG(ERROR) << "No camera available. Exiting test.";
return;
}
const auto& device_info = device_infos.front();
NSString* deviceId =
base::SysUTF8ToNSString(device_info.descriptor.device_id);
VideoCaptureFormat camera_format = device_info.supported_formats.front();
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
// Capture at a lower resolution than we request to scale.
ASSERT_TRUE([captureDevice
setCaptureHeight:camera_format.frame_size.height()
width:camera_format.frame_size.width()
frameRate:camera_format.frame_rate]);
std::vector<gfx::Size> scaled_resolutions;
// Bad resolution because it causes upscale.
scaled_resolutions.emplace_back(camera_format.frame_size.width() * 2,
camera_format.frame_size.height() * 2);
// Bad resolution because it is the same as the captured resolution.
scaled_resolutions.push_back(camera_format.frame_size);
// Good resolution because it causes downscale in both dimensions.
scaled_resolutions.emplace_back(camera_format.frame_size.width() / 2,
camera_format.frame_size.height() / 2);
// Good resolution because it causes downscale in both dimensions.
scaled_resolutions.emplace_back(camera_format.frame_size.width() / 4,
camera_format.frame_size.height() / 4);
// Good resolution because it causes downscale in one dimension (stretch).
scaled_resolutions.emplace_back(camera_format.frame_size.width() / 2,
camera_format.frame_size.height());
[captureDevice setScaledResolutions:scaled_resolutions];
ASSERT_TRUE([captureDevice startCapture]);
bool has_received_first_frame = false;
base::RunLoop first_frame_received(
base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillRepeatedly(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
if (has_received_first_frame) {
// Normally we have time to stop capturing before multiple
// frames are received but in order for the test to be able to
// run on slow bots we are prepared for this method to be
// invoked any number of times. Frames subsequent the first one
// are ignored.
return;
}
EXPECT_EQ(scaled_frames.size(), 3u);
// The bad resolutions were ignored and the good resolutions are
// outputted in the requested order.
EXPECT_EQ(scaled_frames[0].format.frame_size,
scaled_resolutions[2]);
EXPECT_EQ(scaled_frames[1].format.frame_size,
scaled_resolutions[3]);
EXPECT_EQ(scaled_frames[2].format.frame_size,
scaled_resolutions[4]);
has_received_first_frame = true;
first_frame_received.Quit();
}));
first_frame_received.Run();
[captureDevice stopCapture];
}));
}
// This is approximately the same test as the one above except it does not rely
// on having a camera. Instead we mock-invoke processPixelBufferNV12IOSurface
// from the test as-if a camera had produced a frame.
TEST(VideoCaptureDeviceAVFoundationMacTest,
ProcessPixelBufferNV12IOSurfaceWithGoodAndBadScaling) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(kInCapturerScaling);
RunTestCase(base::BindOnce([] {
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
// Capture at a lower resolution than we request to scale.
gfx::Size capture_resolution(640, 360);
std::vector<gfx::Size> scaled_resolutions;
// Bad resolution because it causes upscale.
scaled_resolutions.emplace_back(capture_resolution.width() * 2,
capture_resolution.height() * 2);
// Bad resolution because it is the same as the captured resolution.
scaled_resolutions.push_back(capture_resolution);
// Good resolution because it causes downscale in one dimension (stretch).
scaled_resolutions.emplace_back(capture_resolution.width() / 2,
capture_resolution.height());
// Good resolution because it causes downscale in both dimensions.
scaled_resolutions.emplace_back(capture_resolution.width() / 2,
capture_resolution.height() / 2);
// Good resolution because it causes downscale in both dimensions.
scaled_resolutions.emplace_back(capture_resolution.width() / 4,
capture_resolution.height() / 4);
[captureDevice setScaledResolutions:scaled_resolutions];
// Create a blank NV12 pixel buffer that we pretend was captured.
VideoCaptureFormat capture_format(capture_resolution, 30,
PIXEL_FORMAT_NV12);
std::unique_ptr<ByteArrayPixelBuffer> yuvs_buffer =
CreateYuvsPixelBufferFromSingleRgbColor(
capture_resolution.width(), capture_resolution.height(), 0, 0, 0);
base::ScopedCFTypeRef<CVPixelBufferRef> pixelBuffer =
PixelBufferPool::Create(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
capture_resolution.width(),
capture_resolution.height(), 1)
->CreateBuffer();
DCHECK(PixelBufferTransferer().TransferImage(yuvs_buffer->pixel_buffer,
pixelBuffer));
[captureDevice
callLocked:base::BindLambdaForTesting([&] {
EXPECT_CALL(frame_receiver, ReceiveExternalGpuMemoryBufferFrame)
.WillOnce(testing::Invoke(
[&](CapturedExternalVideoBuffer frame,
std::vector<CapturedExternalVideoBuffer> scaled_frames,
base::TimeDelta timestamp) {
EXPECT_EQ(scaled_frames.size(), 3u);
// The bad resolutions were ignored and the good
// resolutions are outputted in the requested order.
EXPECT_EQ(scaled_frames[0].format.frame_size,
scaled_resolutions[2]);
EXPECT_EQ(scaled_frames[1].format.frame_size,
scaled_resolutions[3]);
EXPECT_EQ(scaled_frames[2].format.frame_size,
scaled_resolutions[4]);
}));
[captureDevice
processPixelBufferNV12IOSurface:pixelBuffer
captureFormat:capture_format
colorSpace:gfx::ColorSpace::CreateSRGB()
timestamp:base::TimeDelta()];
})];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest, TakePhoto) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, OnPhotoTaken)
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
[captureDevice takePhoto];
run_loop.Run();
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest, StopCaptureWhileTakingPhoto) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
EXPECT_CALL(frame_receiver, OnPhotoError())
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
[captureDevice takePhoto];
// There is no risk that takePhoto() has successfully finishes before
// stopCapture() because the takePhoto() call involes a PostDelayedTask()
// that cannot run until RunLoop::Run() below.
[captureDevice stopCapture];
run_loop.Run();
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest, MultiplePendingTakePhotos) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
size_t photos_taken_count = 0;
EXPECT_CALL(frame_receiver, OnPhotoTaken)
.WillRepeatedly([&photos_taken_count, &run_loop] {
++photos_taken_count;
if (photos_taken_count == 3) {
run_loop.Quit();
}
});
[captureDevice takePhoto];
[captureDevice takePhoto];
[captureDevice takePhoto];
run_loop.Run();
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest,
StopCaptureWhileMultiplePendingTakePhotos) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
size_t photo_error_count = 0;
EXPECT_CALL(frame_receiver, OnPhotoError)
.WillRepeatedly([&photo_error_count, &run_loop] {
++photo_error_count;
if (photo_error_count == 3) {
run_loop.Quit();
}
});
[captureDevice takePhoto];
[captureDevice takePhoto];
[captureDevice takePhoto];
// There is no risk that takePhoto() has successfully finishes before
// stopCapture() because the takePhoto() calls involes a PostDelayedTask()
// that cannot run until RunLoop::Run() below.
[captureDevice stopCapture];
run_loop.Run();
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest,
StopStillImageOutputWhenNoLongerTakingPhotos) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
[captureDevice
setOnStillImageOutputStoppedForTesting:run_loop.QuitClosure()];
base::TimeTicks start_time = base::TimeTicks::Now();
[captureDevice takePhoto];
// The RunLoop automatically advances mocked time when there are delayed
// tasks pending. This allows the test to run fast and still assert how much
// mocked time has elapsed.
run_loop.Run();
auto time_elapsed = base::TimeTicks::Now() - start_time;
// Still image output is not stopped until 60 seconds of inactivity, so the
// mocked time must have advanced at least this much.
EXPECT_GE(time_elapsed.InSeconds(), 60);
}));
}
// This test ensures we don't crash even if we leave operations pending.
TEST(VideoCaptureDeviceAVFoundationMacTest,
TakePhotoAndShutDownWithoutWaiting) {
RunTestCase(base::BindOnce([] {
NSString* deviceId = GetFirstDeviceId();
if (!deviceId) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
NSString* errorMessage = nil;
ASSERT_TRUE([captureDevice setCaptureDevice:deviceId
errorMessage:&errorMessage]);
ASSERT_TRUE([captureDevice startCapture]);
[captureDevice takePhoto];
}));
}
TEST(VideoCaptureDeviceAVFoundationMacTest, ForwardsOddPixelBufferResolution) {
// See crbug/1168112.
RunTestCase(base::BindOnce([] {
testing::NiceMock<MockVideoCaptureDeviceAVFoundationFrameReceiver>
frame_receiver;
base::scoped_nsobject<VideoCaptureDeviceAVFoundation> captureDevice(
[[VideoCaptureDeviceAVFoundation alloc]
initWithFrameReceiver:&frame_receiver]);
gfx::Size size(1280, 719);
VideoCaptureFormat format(size, 30, PIXEL_FORMAT_YUY2);
std::unique_ptr<ByteArrayPixelBuffer> buffer =
CreateYuvsPixelBufferFromSingleRgbColor(size.width(), size.height(), 0,
0, 0);
[captureDevice
callLocked:base::BindLambdaForTesting([&] {
EXPECT_CALL(frame_receiver, ReceiveFrame(_, _, format, _, _, _, _));
[captureDevice processPixelBufferPlanes:buffer->pixel_buffer
captureFormat:format
colorSpace:gfx::ColorSpace::CreateSRGB()
timestamp:base::TimeDelta()];
})];
}));
}
} // namespace media