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

[macOS Capture] Move FindBestCaptureFormat out of utils.

This function is only used by video_capture_device_avfoundation_mac.h
and unittest.

By moving this to the modern capture implementation, it makes sense to
not longer take "Class implementation" as argument. This function now
only works with the intended implementation.

Bug: chromium:1124647
Change-Id: Ia3a3802ddd523fd6501f00333e0fbaf7e9a5956b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2410035
Commit-Queue: Henrik Boström <hbos@chromium.org>
Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Cr-Commit-Position: refs/heads/master@{#807434}
parent 7ed2465b
...@@ -15,6 +15,18 @@ ...@@ -15,6 +15,18 @@
#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"
namespace media {
// Find the best capture format from |formats| for the specified dimensions and
// frame rate. Returns an element of |formats|, or nil.
AVCaptureDeviceFormat* CAPTURE_EXPORT
FindBestCaptureFormat(NSArray<AVCaptureDeviceFormat*>* formats,
int width,
int height,
float frame_rate);
} // namespace media
// TODO(crbug.com/1126690): rename this file to be suffixed by the // TODO(crbug.com/1126690): rename this file to be suffixed by the
// "next generation" moniker. // "next generation" moniker.
CAPTURE_EXPORT CAPTURE_EXPORT
......
...@@ -50,6 +50,82 @@ std::string MacFourCCToString(OSType fourcc) { ...@@ -50,6 +50,82 @@ std::string MacFourCCToString(OSType fourcc) {
} // anonymous namespace } // anonymous namespace
namespace media {
AVCaptureDeviceFormat* FindBestCaptureFormat(
NSArray<AVCaptureDeviceFormat*>* formats,
int width,
int height,
float frame_rate) {
AVCaptureDeviceFormat* bestCaptureFormat = nil;
VideoPixelFormat bestPixelFormat = VideoPixelFormat::PIXEL_FORMAT_UNKNOWN;
bool bestMatchesFrameRate = false;
Float64 bestMaxFrameRate = 0;
for (AVCaptureDeviceFormat* captureFormat in formats) {
const FourCharCode fourcc =
CMFormatDescriptionGetMediaSubType([captureFormat formatDescription]);
VideoPixelFormat pixelFormat =
[VideoCaptureDeviceAVFoundation FourCCToChromiumPixelFormat:fourcc];
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(
[captureFormat formatDescription]);
Float64 maxFrameRate = 0;
bool matchesFrameRate = false;
for (AVFrameRateRange* frameRateRange in
[captureFormat videoSupportedFrameRateRanges]) {
maxFrameRate = std::max(maxFrameRate, [frameRateRange maxFrameRate]);
matchesFrameRate |= [frameRateRange minFrameRate] <= frame_rate &&
frame_rate <= [frameRateRange maxFrameRate];
}
// If the pixel format is unsupported by our code, then it is not useful.
if (pixelFormat == VideoPixelFormat::PIXEL_FORMAT_UNKNOWN)
continue;
// If our CMSampleBuffers will have a different size than the native
// capture, then we will not be the fast path.
if (dimensions.width != width || dimensions.height != height)
continue;
// Prefer a capture format that handles the requested framerate to one
// that doesn't.
if (bestCaptureFormat) {
if (bestMatchesFrameRate && !matchesFrameRate)
continue;
if (matchesFrameRate && !bestMatchesFrameRate)
bestCaptureFormat = nil;
}
// Prefer a capture format with a lower maximum framerate, under the
// assumption that that may have lower power consumption.
if (bestCaptureFormat) {
if (bestMaxFrameRate < maxFrameRate)
continue;
if (maxFrameRate < bestMaxFrameRate)
bestCaptureFormat = nil;
}
// Finally, compare according to Chromium preference.
if (bestCaptureFormat) {
if (VideoCaptureFormat::ComparePixelFormatPreference(bestPixelFormat,
pixelFormat)) {
continue;
}
}
bestCaptureFormat = captureFormat;
bestPixelFormat = pixelFormat;
bestMaxFrameRate = maxFrameRate;
bestMatchesFrameRate = matchesFrameRate;
}
VLOG(1) << "Selecting AVCaptureDevice format "
<< VideoPixelFormatToString(bestPixelFormat);
return bestCaptureFormat;
}
} // namespace media
@implementation VideoCaptureDeviceAVFoundation @implementation VideoCaptureDeviceAVFoundation
#pragma mark Class methods #pragma mark Class methods
...@@ -174,8 +250,7 @@ std::string MacFourCCToString(OSType fourcc) { ...@@ -174,8 +250,7 @@ std::string MacFourCCToString(OSType fourcc) {
_frameHeight = height; _frameHeight = height;
_frameRate = frameRate; _frameRate = frameRate;
_bestCaptureFormat.reset( _bestCaptureFormat.reset(
media::FindBestCaptureFormat([VideoCaptureDeviceAVFoundation class], media::FindBestCaptureFormat([_captureDevice formats], width, height,
[_captureDevice formats], width, height,
frameRate), frameRate),
base::scoped_policy::RETAIN); base::scoped_policy::RETAIN);
// Default to NV12, a pixel format commonly supported by web cameras. // Default to NV12, a pixel format commonly supported by web cameras.
......
...@@ -15,17 +15,6 @@ ...@@ -15,17 +15,6 @@
namespace media { namespace media {
// Find the best capture format from |formats| for the specified dimensions and
// frame rate. Returns an element of |formats|, or nil.
// |implementation| is a class implementing FourCCToChromiumPixelFormat, which
// our VideoCaptureDeviceAVFoundationProtocol implementations do.
AVCaptureDeviceFormat* CAPTURE_EXPORT
FindBestCaptureFormat(Class implementation,
NSArray<AVCaptureDeviceFormat*>* formats,
int width,
int height,
float frame_rate);
// Returns a dictionary of capture devices with friendly name and unique id. // Returns a dictionary of capture devices with friendly name and unique id.
// VideoCaptureDeviceMac should call this function to fetch the list of devices // VideoCaptureDeviceMac should call this function to fetch the list of devices
// available in the system; this method returns the list of device names that // available in the system; this method returns the list of device names that
......
...@@ -163,79 +163,6 @@ base::scoped_nsobject<NSDictionary> GetDeviceNames() { ...@@ -163,79 +163,6 @@ base::scoped_nsobject<NSDictionary> GetDeviceNames() {
} }
} // namespace } // namespace
AVCaptureDeviceFormat* FindBestCaptureFormat(
Class implementation,
NSArray<AVCaptureDeviceFormat*>* formats,
int width,
int height,
float frame_rate) {
AVCaptureDeviceFormat* bestCaptureFormat = nil;
VideoPixelFormat bestPixelFormat = VideoPixelFormat::PIXEL_FORMAT_UNKNOWN;
bool bestMatchesFrameRate = false;
Float64 bestMaxFrameRate = 0;
for (AVCaptureDeviceFormat* captureFormat in formats) {
const FourCharCode fourcc =
CMFormatDescriptionGetMediaSubType([captureFormat formatDescription]);
VideoPixelFormat pixelFormat =
[implementation FourCCToChromiumPixelFormat:fourcc];
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(
[captureFormat formatDescription]);
Float64 maxFrameRate = 0;
bool matchesFrameRate = false;
for (AVFrameRateRange* frameRateRange in
[captureFormat videoSupportedFrameRateRanges]) {
maxFrameRate = std::max(maxFrameRate, [frameRateRange maxFrameRate]);
matchesFrameRate |= [frameRateRange minFrameRate] <= frame_rate &&
frame_rate <= [frameRateRange maxFrameRate];
}
// If the pixel format is unsupported by our code, then it is not useful.
if (pixelFormat == VideoPixelFormat::PIXEL_FORMAT_UNKNOWN)
continue;
// If our CMSampleBuffers will have a different size than the native
// capture, then we will not be the fast path.
if (dimensions.width != width || dimensions.height != height)
continue;
// Prefer a capture format that handles the requested framerate to one
// that doesn't.
if (bestCaptureFormat) {
if (bestMatchesFrameRate && !matchesFrameRate)
continue;
if (matchesFrameRate && !bestMatchesFrameRate)
bestCaptureFormat = nil;
}
// Prefer a capture format with a lower maximum framerate, under the
// assumption that that may have lower power consumption.
if (bestCaptureFormat) {
if (bestMaxFrameRate < maxFrameRate)
continue;
if (maxFrameRate < bestMaxFrameRate)
bestCaptureFormat = nil;
}
// Finally, compare according to Chromium preference.
if (bestCaptureFormat) {
if (VideoCaptureFormat::ComparePixelFormatPreference(bestPixelFormat,
pixelFormat)) {
continue;
}
}
bestCaptureFormat = captureFormat;
bestPixelFormat = pixelFormat;
bestMaxFrameRate = maxFrameRate;
bestMatchesFrameRate = matchesFrameRate;
}
VLOG(1) << "Selecting AVCaptureDevice format "
<< VideoPixelFormatToString(bestPixelFormat);
return bestCaptureFormat;
}
void ExtractBaseAddressAndLength(char** base_address, void ExtractBaseAddressAndLength(char** base_address,
size_t* length, size_t* length,
CMSampleBufferRef sample_buffer) { CMSampleBufferRef sample_buffer) {
......
...@@ -90,9 +90,6 @@ namespace media { ...@@ -90,9 +90,6 @@ namespace media {
// Test the behavior of the function FindBestCaptureFormat which is used to // Test the behavior of the function FindBestCaptureFormat which is used to
// determine the capture format. // determine the capture format.
TEST(VideoCaptureDeviceMacTest, FindBestCaptureFormat) { TEST(VideoCaptureDeviceMacTest, FindBestCaptureFormat) {
// We are only interested in the modern implementation here.
Class impl = [VideoCaptureDeviceAVFoundation class];
base::scoped_nsobject<FakeAVCaptureDeviceFormat> fmt_320_240_xyzw_30( base::scoped_nsobject<FakeAVCaptureDeviceFormat> fmt_320_240_xyzw_30(
[[FakeAVCaptureDeviceFormat alloc] initWithWidth:320 [[FakeAVCaptureDeviceFormat alloc] initWithWidth:320
height:240 height:240
...@@ -139,68 +136,68 @@ TEST(VideoCaptureDeviceMacTest, FindBestCaptureFormat) { ...@@ -139,68 +136,68 @@ TEST(VideoCaptureDeviceMacTest, FindBestCaptureFormat) {
AVCaptureDeviceFormat* result = nil; AVCaptureDeviceFormat* result = nil;
// If we can't find a valid format, we should return nil; // If we can't find a valid format, we should return nil;
result = FindBestCaptureFormat(impl, @[ fmt_320_240_xyzw_30 ], 320, 240, 30); result = FindBestCaptureFormat(@[ fmt_320_240_xyzw_30 ], 320, 240, 30);
EXPECT_EQ(result, nil); EXPECT_EQ(result, nil);
// Can't find a matching resolution // Can't find a matching resolution
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_320_240_yuvs_30, fmt_320_240_2vuy_30 ],
impl, @[ fmt_320_240_yuvs_30, fmt_320_240_2vuy_30 ], 640, 480, 30); 640, 480, 30);
EXPECT_EQ(result, nil); EXPECT_EQ(result, nil);
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_320_240_2vuy_30, fmt_320_240_yuvs_30 ],
impl, @[ fmt_320_240_2vuy_30, fmt_320_240_yuvs_30 ], 640, 480, 30); 640, 480, 30);
EXPECT_EQ(result, nil); EXPECT_EQ(result, nil);
// Simple exact match. // Simple exact match.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_yuvs_30, fmt_320_240_yuvs_30 ],
impl, @[ fmt_640_480_yuvs_30, fmt_320_240_yuvs_30 ], 320, 240, 30); 320, 240, 30);
EXPECT_EQ(result, fmt_320_240_yuvs_30.get()); EXPECT_EQ(result, fmt_320_240_yuvs_30.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_320_240_yuvs_30, fmt_640_480_yuvs_30 ],
impl, @[ fmt_320_240_yuvs_30, fmt_640_480_yuvs_30 ], 320, 240, 30); 320, 240, 30);
EXPECT_EQ(result, fmt_320_240_yuvs_30.get()); EXPECT_EQ(result, fmt_320_240_yuvs_30.get());
// Different frame rate. // Different frame rate.
result = FindBestCaptureFormat(impl, @[ fmt_640_480_2vuy_30 ], 640, 480, 60); result = FindBestCaptureFormat(@[ fmt_640_480_2vuy_30 ], 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_30.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30.get());
// Prefer the same frame rate. // Prefer the same frame rate.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_60 ],
impl, @[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_60 ], 640, 480, 60); 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_60.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_2vuy_60, fmt_640_480_yuvs_30 ],
impl, @[ fmt_640_480_2vuy_60, fmt_640_480_yuvs_30 ], 640, 480, 60); 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_60.get());
// Prefer version with matching frame rate. // Prefer version with matching frame rate.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_60 ],
impl, @[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_60 ], 640, 480, 60); 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_60.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_2vuy_60, fmt_640_480_yuvs_30 ],
impl, @[ fmt_640_480_2vuy_60, fmt_640_480_yuvs_30 ], 640, 480, 60); 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_60.get());
// Prefer version with matching frame rate when there are multiple framerates. // Prefer version with matching frame rate when there are multiple framerates.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(
impl, @[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_30_60 ], 640, 480, 60); @[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_30_60 ], 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_30_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30_60.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(
impl, @[ fmt_640_480_2vuy_30_60, fmt_640_480_yuvs_30 ], 640, 480, 60); @[ fmt_640_480_2vuy_30_60, fmt_640_480_yuvs_30 ], 640, 480, 60);
EXPECT_EQ(result, fmt_640_480_2vuy_30_60.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30_60.get());
// Prefer version with the lower maximum framerate when there are multiple // Prefer version with the lower maximum framerate when there are multiple
// framerates. // framerates.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(
impl, @[ fmt_640_480_2vuy_30, fmt_640_480_2vuy_30_60 ], 640, 480, 30); @[ fmt_640_480_2vuy_30, fmt_640_480_2vuy_30_60 ], 640, 480, 30);
EXPECT_EQ(result, fmt_640_480_2vuy_30.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(
impl, @[ fmt_640_480_2vuy_30_60, fmt_640_480_2vuy_30 ], 640, 480, 30); @[ fmt_640_480_2vuy_30_60, fmt_640_480_2vuy_30 ], 640, 480, 30);
EXPECT_EQ(result, fmt_640_480_2vuy_30.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30.get());
// Prefer the Chromium format order. // Prefer the Chromium format order.
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_30 ],
impl, @[ fmt_640_480_yuvs_30, fmt_640_480_2vuy_30 ], 640, 480, 30); 640, 480, 30);
EXPECT_EQ(result, fmt_640_480_2vuy_30.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30.get());
result = FindBestCaptureFormat( result = FindBestCaptureFormat(@[ fmt_640_480_2vuy_30, fmt_640_480_yuvs_30 ],
impl, @[ fmt_640_480_2vuy_30, fmt_640_480_yuvs_30 ], 640, 480, 30); 640, 480, 30);
EXPECT_EQ(result, fmt_640_480_2vuy_30.get()); EXPECT_EQ(result, fmt_640_480_2vuy_30.get());
} }
......
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