blob: 19afa337683bacbeb7b1a14e2cd539cf821b6f09 [file] [log] [blame]
/*
* Copyright 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "avfoundationformatmapper.h"
#import "WebRTC/RTCLogging.h"
// TODO(denicija): add support for higher frame rates.
// See http://crbug/webrtc/6355 for more info.
static const int kFramesPerSecond = 30;
static inline BOOL IsMediaSubTypeSupported(FourCharCode mediaSubType) {
return (mediaSubType == kCVPixelFormatType_420YpCbCr8PlanarFullRange ||
mediaSubType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
}
static inline BOOL IsFrameRateWithinRange(int fps, AVFrameRateRange* range) {
return range.minFrameRate <= fps && range.maxFrameRate >= fps;
}
// Returns filtered array of device formats based on predefined constraints our
// stack imposes.
static NSArray<AVCaptureDeviceFormat*>* GetEligibleDeviceFormats(
const AVCaptureDevice* device,
int supportedFps) {
NSMutableArray<AVCaptureDeviceFormat*>* eligibleDeviceFormats =
[NSMutableArray array];
for (AVCaptureDeviceFormat* format in device.formats) {
// Filter out subTypes that we currently don't support in the stack
FourCharCode mediaSubType =
CMFormatDescriptionGetMediaSubType(format.formatDescription);
if (!IsMediaSubTypeSupported(mediaSubType)) {
continue;
}
// Filter out frame rate ranges that we currently don't support in the stack
for (AVFrameRateRange* frameRateRange in format.videoSupportedFrameRateRanges) {
if (IsFrameRateWithinRange(supportedFps, frameRateRange)) {
[eligibleDeviceFormats addObject:format];
break;
}
}
}
return [eligibleDeviceFormats copy];
}
// Mapping from cricket::VideoFormat to AVCaptureDeviceFormat.
static AVCaptureDeviceFormat* GetDeviceFormatForVideoFormat(
const AVCaptureDevice* device,
const cricket::VideoFormat& videoFormat) {
AVCaptureDeviceFormat* desiredDeviceFormat = nil;
NSArray<AVCaptureDeviceFormat*>* eligibleFormats =
GetEligibleDeviceFormats(device, videoFormat.framerate());
for (AVCaptureDeviceFormat* deviceFormat in eligibleFormats) {
CMVideoDimensions dimension =
CMVideoFormatDescriptionGetDimensions(deviceFormat.formatDescription);
FourCharCode mediaSubType =
CMFormatDescriptionGetMediaSubType(deviceFormat.formatDescription);
if (videoFormat.width == dimension.width &&
videoFormat.height == dimension.height) {
if (mediaSubType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
// This is the preferred format so no need to wait for better option.
return deviceFormat;
} else {
// This is good candidate, but let's wait for something better.
desiredDeviceFormat = deviceFormat;
}
}
}
return desiredDeviceFormat;
}
namespace webrtc {
std::set<cricket::VideoFormat> GetSupportedVideoFormatsForDevice(
AVCaptureDevice* device) {
std::set<cricket::VideoFormat> supportedFormats;
NSArray<AVCaptureDeviceFormat*>* eligibleFormats =
GetEligibleDeviceFormats(device, kFramesPerSecond);
for (AVCaptureDeviceFormat* deviceFormat in eligibleFormats) {
CMVideoDimensions dimension =
CMVideoFormatDescriptionGetDimensions(deviceFormat.formatDescription);
cricket::VideoFormat format = cricket::VideoFormat(
dimension.width, dimension.height,
cricket::VideoFormat::FpsToInterval(kFramesPerSecond),
cricket::FOURCC_NV12);
supportedFormats.insert(format);
}
return supportedFormats;
}
bool SetFormatForCaptureDevice(AVCaptureDevice* device,
AVCaptureSession* session,
const cricket::VideoFormat& format) {
AVCaptureDeviceFormat* deviceFormat =
GetDeviceFormatForVideoFormat(device, format);
const int fps = cricket::VideoFormat::IntervalToFps(format.interval);
NSError* error = nil;
bool success = true;
[session beginConfiguration];
if ([device lockForConfiguration:&error]) {
@try {
device.activeFormat = deviceFormat;
device.activeVideoMinFrameDuration = CMTimeMake(1, fps);
} @catch (NSException* exception) {
RTCLogError(@"Failed to set active format!\n User info:%@",
exception.userInfo);
success = false;
}
[device unlockForConfiguration];
} else {
RTCLogError(@"Failed to lock device %@. Error: %@", device, error.userInfo);
success = false;
}
[session commitConfiguration];
return success;
}
} // namespace webrtc