Commit 9c732cbc authored by Henrik Boström's avatar Henrik Boström Committed by Commit Bot

[macOS Capture] Lazily instantiate takePhoto()'s _stillImageOutput.

Per https://crbug.com/1116241, always instantiating and adding the
AVCaptureStillImageOutput to the capture session does, in some cases,
result in high CPU load due to the macOS process VTDecoderXPCService
converting pixel formats and producing MJPEG. But this work is only
needed if the ImageCapture.takePhoto() API is invoked, which most
MediaStreamTrack-using web pages never use.

This CL lazily instantiates _stillImageOutput when takePhoto() is
called and keeps it alive until 60 seconds of time has passed without
another takePhoto() call. We keep it alive like this because
instantiation involves noticeable delays that is avoided if takePhoto()
is called after instantiation.

The delays are due to two reasons:
1. Delays inherent to instantiating AVCaptureStillImageOutput.
2. This CL introduces a async delay of 3 seconds after instantiation.
   If we don't wait before taking the photo, the 3A has not stabilized
   and the photo is too dark.
Because performance is critical to the success of WebRTC applications,
the pros are believed to outweigh the cons.

To achieve its goal, this CL:
- Makes it possible to mock the frame receiver.
- Adds helper utils.
- Adds video_capture_device_avfoundation_mac_unittest.mm.
- Replaces a thread checker with a task runner to allow posting tasks.
- Introduces MOCK_TIME in the tests.

Bug: chromium:1124652, chromium:1116241
Change-Id: I91ebcb45d44fda3fcdb613b8907a10dfa89a9615
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2388100Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Reviewed-by: default avatarccameron <ccameron@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806083}
parent c2510c09
...@@ -392,6 +392,11 @@ test("capture_unittests") { ...@@ -392,6 +392,11 @@ test("capture_unittests") {
"video/linux/camera_config_chromeos_unittest.cc", "video/linux/camera_config_chromeos_unittest.cc",
"video/linux/v4l2_capture_delegate_unittest.cc", "video/linux/v4l2_capture_delegate_unittest.cc",
"video/linux/video_capture_device_factory_linux_unittest.cc", "video/linux/video_capture_device_factory_linux_unittest.cc",
"video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h",
"video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.mm",
"video/mac/test/video_capture_test_utils_mac.h",
"video/mac/test/video_capture_test_utils_mac.mm",
"video/mac/video_capture_device_avfoundation_mac_unittest.mm",
"video/mac/video_capture_device_factory_mac_unittest.mm", "video/mac/video_capture_device_factory_mac_unittest.mm",
"video/mac/video_capture_device_mac_unittest.mm", "video/mac/video_capture_device_mac_unittest.mm",
"video/video_capture_device_client_unittest.cc", "video/video_capture_device_client_unittest.cc",
......
// 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.
#ifndef MEDIA_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
#define MEDIA_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
#include "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
class MockVideoCaptureDeviceAVFoundationFrameReceiver
: public VideoCaptureDeviceAVFoundationFrameReceiver {
public:
MockVideoCaptureDeviceAVFoundationFrameReceiver();
~MockVideoCaptureDeviceAVFoundationFrameReceiver() override;
MOCK_METHOD(void,
ReceiveFrame,
(const uint8_t* video_frame,
int video_frame_length,
const VideoCaptureFormat& frame_format,
const gfx::ColorSpace color_space,
int aspect_numerator,
int aspect_denominator,
base::TimeDelta timestamp),
(override));
MOCK_METHOD(void,
OnPhotoTaken,
(const uint8_t* image_data,
size_t image_length,
const std::string& mime_type),
(override));
MOCK_METHOD(void, OnPhotoError, (), (override));
MOCK_METHOD(void,
ReceiveError,
(VideoCaptureError error,
const base::Location& from_here,
const std::string& reason),
(override));
};
} // namespace media
#endif // MEDIA_CAPTURE_VIDEO_MAC_TEST_MOCK_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_FRAME_RECEIVER_MAC_H_
\ No newline at end of file
// 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.
#import "media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h"
namespace media {
MockVideoCaptureDeviceAVFoundationFrameReceiver::
MockVideoCaptureDeviceAVFoundationFrameReceiver() = default;
MockVideoCaptureDeviceAVFoundationFrameReceiver::
~MockVideoCaptureDeviceAVFoundationFrameReceiver() = default;
} // namespace media
// 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.
#ifndef MEDIA_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
#define MEDIA_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
#import <Foundation/Foundation.h>
#include <memory>
#include "base/callback_forward.h"
#include "media/capture/video/mac/video_capture_device_factory_mac.h"
namespace media {
// Video capture code on MacOSX must run on a CFRunLoop enabled thread
// for interaction with AVFoundation.
// In order to make the test case run on the actual message loop that has
// been created for this thread, we need to run it inside a RunLoop. This is
// required, because on MacOS the capture code must run on a CFRunLoop
// enabled message loop.
void RunTestCase(base::OnceClosure test_case);
std::vector<VideoCaptureDeviceInfo> GetDevicesInfo(
VideoCaptureDeviceFactoryMac* video_capture_device_factory);
// If there are no devices, nil is returned.
NSString* GetFirstDeviceId();
} // namespace media
#endif // MEDIA_CAPTURE_VIDEO_MAC_TEST_VIDEO_CAPTURE_TEST_UTILS_MAC_H_
// 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.
#import "media/capture/video/mac/test/video_capture_test_utils_mac.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/capture/video/mac/video_capture_device_factory_mac.h"
namespace media {
void RunTestCase(base::OnceClosure test_case) {
base::test::TaskEnvironment task_environment(
base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(
[](base::RunLoop* run_loop, base::OnceClosure* test_case) {
std::move(*test_case).Run();
run_loop->Quit();
},
&run_loop, &test_case));
run_loop.Run();
}
std::vector<VideoCaptureDeviceInfo> GetDevicesInfo(
VideoCaptureDeviceFactoryMac* video_capture_device_factory) {
std::vector<VideoCaptureDeviceInfo> descriptors;
base::RunLoop run_loop;
video_capture_device_factory->GetDevicesInfo(base::BindLambdaForTesting(
[&descriptors, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
descriptors = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
return descriptors;
}
NSString* GetFirstDeviceId() {
VideoCaptureDeviceFactoryMac video_capture_device_factory;
std::vector<VideoCaptureDeviceInfo> devices_info =
GetDevicesInfo(&video_capture_device_factory);
if (devices_info.empty())
return nil;
return [NSString
stringWithUTF8String:devices_info.front().descriptor.device_id.c_str()];
}
} // namespace media
...@@ -16,6 +16,27 @@ ...@@ -16,6 +16,27 @@
namespace media { namespace media {
class VideoCaptureDeviceMac; class VideoCaptureDeviceMac;
class CAPTURE_EXPORT VideoCaptureDeviceAVFoundationFrameReceiver {
public:
virtual ~VideoCaptureDeviceAVFoundationFrameReceiver() = default;
virtual void ReceiveFrame(const uint8_t* video_frame,
int video_frame_length,
const VideoCaptureFormat& frame_format,
const gfx::ColorSpace color_space,
int aspect_numerator,
int aspect_denominator,
base::TimeDelta timestamp) = 0;
virtual void OnPhotoTaken(const uint8_t* image_data,
size_t image_length,
const std::string& mime_type) = 0;
virtual void OnPhotoError() = 0;
virtual void ReceiveError(VideoCaptureError error,
const base::Location& from_here,
const std::string& reason) = 0;
};
} // namespace media } // namespace media
// Class used by VideoCaptureDeviceMac (VCDM) for video and image capture using // Class used by VideoCaptureDeviceMac (VCDM) for video and image capture using
...@@ -47,6 +68,7 @@ class VideoCaptureDeviceMac; ...@@ -47,6 +68,7 @@ class VideoCaptureDeviceMac;
// the VideoCaptureDeviceAVFoundation object. // the VideoCaptureDeviceAVFoundation object.
// //
// //
CAPTURE_EXPORT
@interface VideoCaptureDeviceAVFoundation @interface VideoCaptureDeviceAVFoundation
: NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> { : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> {
@private @private
...@@ -59,7 +81,7 @@ class VideoCaptureDeviceMac; ...@@ -59,7 +81,7 @@ class VideoCaptureDeviceMac;
base::scoped_nsobject<AVCaptureDeviceFormat> _bestCaptureFormat; base::scoped_nsobject<AVCaptureDeviceFormat> _bestCaptureFormat;
base::Lock _lock; // Protects concurrent setting and using |frameReceiver_|. base::Lock _lock; // Protects concurrent setting and using |frameReceiver_|.
media::VideoCaptureDeviceMac* _frameReceiver; // weak. media::VideoCaptureDeviceAVFoundationFrameReceiver* _frameReceiver; // weak.
base::scoped_nsobject<AVCaptureSession> _captureSession; base::scoped_nsobject<AVCaptureSession> _captureSession;
...@@ -71,16 +93,27 @@ class VideoCaptureDeviceMac; ...@@ -71,16 +93,27 @@ class VideoCaptureDeviceMac;
// An AVDataOutput specialized for taking pictures out of |captureSession_|. // An AVDataOutput specialized for taking pictures out of |captureSession_|.
base::scoped_nsobject<AVCaptureStillImageOutput> _stillImageOutput; base::scoped_nsobject<AVCaptureStillImageOutput> _stillImageOutput;
size_t _takePhotoStartedCount;
size_t _takePhotoPendingCount;
size_t _takePhotoCompletedCount;
bool _stillImageOutputWarmupCompleted;
std::unique_ptr<base::WeakPtrFactory<VideoCaptureDeviceAVFoundation>>
_weakPtrFactoryForTakePhoto;
base::ThreadChecker _main_thread_checker; // For testing.
base::RepeatingCallback<void()> _onStillImageOutputStopped;
scoped_refptr<base::SingleThreadTaskRunner> _mainThreadTaskRunner;
} }
// Initializes the instance and the underlying capture session and registers the // Initializes the instance and the underlying capture session and registers the
// frame receiver. // frame receiver.
- (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver; - (id)initWithFrameReceiver:
(media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
// Sets the frame receiver. // Sets the frame receiver.
- (void)setFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver; - (void)setFrameReceiver:
(media::VideoCaptureDeviceAVFoundationFrameReceiver*)frameReceiver;
// Sets which capture device to use by name, retrieved via |deviceNames|. Once // Sets which capture device to use by name, retrieved via |deviceNames|. Once
// the deviceId is known, the library objects are created if needed and // the deviceId is known, the library objects are created if needed and
...@@ -111,6 +144,9 @@ class VideoCaptureDeviceMac; ...@@ -111,6 +144,9 @@ class VideoCaptureDeviceMac;
// -stopCapture. // -stopCapture.
- (void)takePhoto; - (void)takePhoto;
- (void)setOnStillImageOutputStoppedForTesting:
(base::RepeatingCallback<void()>)onStillImageOutputStopped;
@end @end
#endif // MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_ #endif // MEDIA_CAPTURE_VIDEO_MAC_VIDEO_CAPTURE_DEVICE_AVFOUNDATION_MAC_H_
// 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/test/gmock_callback_support.h"
#import "media/capture/video/mac/test/mock_video_capture_device_avfoundation_frame_receiver_mac.h"
#import "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"
using testing::_;
namespace media {
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];
}));
}
} // namespace media
...@@ -7,48 +7,18 @@ ...@@ -7,48 +7,18 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#import "media/capture/video/mac/test/video_capture_test_utils_mac.h"
#include "media/capture/video/mac/video_capture_device_mac.h" #include "media/capture/video/mac/video_capture_device_mac.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace media { namespace media {
// Video capture code on MacOSX must run on a CFRunLoop enabled thread
// for interaction with AVFoundation.
// In order to make the test case run on the actual message loop that has
// been created for this thread, we need to run it inside a RunLoop. This is
// required, because on MacOS the capture code must run on a CFRunLoop
// enabled message loop.
void RunTestCase(base::OnceClosure test_case) {
base::test::TaskEnvironment task_environment(
base::test::TaskEnvironment::MainThreadType::UI);
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(
[](base::RunLoop* run_loop, base::OnceClosure* test_case) {
std::move(*test_case).Run();
run_loop->Quit();
},
&run_loop, &test_case));
run_loop.Run();
}
void GetDevicesInfo(VideoCaptureDeviceFactoryMac* video_capture_device_factory,
std::vector<VideoCaptureDeviceInfo>* descriptors) {
base::RunLoop run_loop;
video_capture_device_factory->GetDevicesInfo(base::BindLambdaForTesting(
[descriptors, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
*descriptors = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
}
TEST(VideoCaptureDeviceFactoryMacTest, ListDevicesAVFoundation) { TEST(VideoCaptureDeviceFactoryMacTest, ListDevicesAVFoundation) {
RunTestCase(base::BindOnce([]() { RunTestCase(base::BindOnce([]() {
VideoCaptureDeviceFactoryMac video_capture_device_factory; VideoCaptureDeviceFactoryMac video_capture_device_factory;
std::vector<VideoCaptureDeviceInfo> devices_info; std::vector<VideoCaptureDeviceInfo> devices_info =
GetDevicesInfo(&video_capture_device_factory, &devices_info); GetDevicesInfo(&video_capture_device_factory);
if (devices_info.empty()) { if (devices_info.empty()) {
DVLOG(1) << "No camera available. Exiting test."; DVLOG(1) << "No camera available. Exiting test.";
return; return;
...@@ -64,8 +34,8 @@ TEST(VideoCaptureDeviceFactoryMacTest, ListDevicesWithNoPanTiltZoomSupport) { ...@@ -64,8 +34,8 @@ TEST(VideoCaptureDeviceFactoryMacTest, ListDevicesWithNoPanTiltZoomSupport) {
RunTestCase(base::BindOnce([]() { RunTestCase(base::BindOnce([]() {
VideoCaptureDeviceFactoryMac video_capture_device_factory; VideoCaptureDeviceFactoryMac video_capture_device_factory;
std::vector<VideoCaptureDeviceInfo> devices_info; std::vector<VideoCaptureDeviceInfo> devices_info =
GetDevicesInfo(&video_capture_device_factory, &devices_info); GetDevicesInfo(&video_capture_device_factory);
if (devices_info.empty()) { if (devices_info.empty()) {
DVLOG(1) << "No camera available. Exiting test."; DVLOG(1) << "No camera available. Exiting test.";
return; return;
......
...@@ -19,11 +19,10 @@ ...@@ -19,11 +19,10 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
#include "media/capture/video/video_capture_device.h" #include "media/capture/video/video_capture_device.h"
#include "media/capture/video_capture_types.h" #include "media/capture/video_capture_types.h"
@class VideoCaptureDeviceAVFoundation;
namespace base { namespace base {
class SingleThreadTaskRunner; class SingleThreadTaskRunner;
} }
...@@ -52,7 +51,9 @@ namespace media { ...@@ -52,7 +51,9 @@ namespace media {
// Called by VideoCaptureManager to open, close and start, stop Mac video // Called by VideoCaptureManager to open, close and start, stop Mac video
// capture devices. // capture devices.
class VideoCaptureDeviceMac : public VideoCaptureDevice { class VideoCaptureDeviceMac
: public VideoCaptureDevice,
public VideoCaptureDeviceAVFoundationFrameReceiver {
public: public:
explicit VideoCaptureDeviceMac( explicit VideoCaptureDeviceMac(
const VideoCaptureDeviceDescriptor& device_descriptor); const VideoCaptureDeviceDescriptor& device_descriptor);
...@@ -78,19 +79,19 @@ class VideoCaptureDeviceMac : public VideoCaptureDevice { ...@@ -78,19 +79,19 @@ class VideoCaptureDeviceMac : public VideoCaptureDevice {
const gfx::ColorSpace color_space, const gfx::ColorSpace color_space,
int aspect_numerator, int aspect_numerator,
int aspect_denominator, int aspect_denominator,
base::TimeDelta timestamp); base::TimeDelta timestamp) override;
// Callbacks with the result of a still image capture, or in case of error, // Callbacks with the result of a still image capture, or in case of error,
// respectively. It's safe to call these methods from any thread. // respectively. It's safe to call these methods from any thread.
void OnPhotoTaken(const uint8_t* image_data, void OnPhotoTaken(const uint8_t* image_data,
size_t image_length, size_t image_length,
const std::string& mime_type); const std::string& mime_type) override;
void OnPhotoError(); void OnPhotoError() override;
// Forwarder to VideoCaptureDevice::Client::OnError(). // Forwarder to VideoCaptureDevice::Client::OnError().
void ReceiveError(VideoCaptureError error, void ReceiveError(VideoCaptureError error,
const base::Location& from_here, const base::Location& from_here,
const std::string& reason); const std::string& reason) override;
// Forwarder to VideoCaptureDevice::Client::OnLog(). // Forwarder to VideoCaptureDevice::Client::OnLog().
void LogMessage(const std::string& message); void LogMessage(const std::string& message);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment