/* * 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* GetEligibleDeviceFormats( const AVCaptureDevice* device, int supportedFps) { NSMutableArray* 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* 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 GetSupportedVideoFormatsForDevice( AVCaptureDevice* device) { std::set supportedFormats; NSArray* 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