Index: webrtc/sdk/objc/Framework/Classes/avfoundationformatmapper.mm |
diff --git a/webrtc/sdk/objc/Framework/Classes/avfoundationformatmapper.mm b/webrtc/sdk/objc/Framework/Classes/avfoundationformatmapper.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..19afa337683bacbeb7b1a14e2cd539cf821b6f09 |
--- /dev/null |
+++ b/webrtc/sdk/objc/Framework/Classes/avfoundationformatmapper.mm |
@@ -0,0 +1,135 @@ |
+/* |
+ * 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 |