From d690eab54fa49026156b3525982d022d5166a38c Mon Sep 17 00:00:00 2001 From: "sjlee@webrtc.org" Date: Wed, 14 Aug 2013 22:07:04 +0000 Subject: [PATCH] The video capture module for iOS. This CL is from https://webrtc-codereview.appspot.com/1339004. Patch this CL, then run the trunk/webrtc/build/vie-webrtc.sh. BUG=2105 R=fischman@webrtc.org, mallinath@webrtc.org, niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1641004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4546 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/build/common.gypi | 3 - webrtc/build/vie-webrtc.sh | 69 +++++ .../video_capture/ios/device_info_ios.h | 63 ++++ .../video_capture/ios/device_info_ios.mm | 108 +++++++ .../video_capture/ios/device_info_ios_objc.h | 25 ++ .../video_capture/ios/device_info_ios_objc.mm | 49 +++ .../video_capture/ios/video_capture_ios.h | 45 +++ .../video_capture/ios/video_capture_ios.mm | 99 ++++++ .../ios/video_capture_ios_objc.h | 40 +++ .../ios/video_capture_ios_objc.mm | 287 ++++++++++++++++++ .../modules/video_capture/video_capture.gypi | 12 + .../video_capture/video_capture_factory.cc | 13 - 12 files changed, 797 insertions(+), 16 deletions(-) create mode 100755 webrtc/build/vie-webrtc.sh create mode 100644 webrtc/modules/video_capture/ios/device_info_ios.h create mode 100644 webrtc/modules/video_capture/ios/device_info_ios.mm create mode 100644 webrtc/modules/video_capture/ios/device_info_ios_objc.h create mode 100644 webrtc/modules/video_capture/ios/device_info_ios_objc.mm create mode 100644 webrtc/modules/video_capture/ios/video_capture_ios.h create mode 100644 webrtc/modules/video_capture/ios/video_capture_ios.mm create mode 100644 webrtc/modules/video_capture/ios/video_capture_ios_objc.h create mode 100644 webrtc/modules/video_capture/ios/video_capture_ios_objc.mm diff --git a/webrtc/build/common.gypi b/webrtc/build/common.gypi index efdf737c4..17516f49d 100644 --- a/webrtc/build/common.gypi +++ b/webrtc/build/common.gypi @@ -133,10 +133,7 @@ 'enable_android_opensl%': 0, }], ['OS=="ios"', { - 'enable_video%': 0, 'enable_protobuf%': 0, - 'build_libjpeg%': 0, - 'build_libyuv%': 0, 'include_tests%': 0, }], ['target_arch=="arm"', { diff --git a/webrtc/build/vie-webrtc.sh b/webrtc/build/vie-webrtc.sh new file mode 100755 index 000000000..a3201462c --- /dev/null +++ b/webrtc/build/vie-webrtc.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +# Copyright (c) 2013 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. +set -e + +# TODO(sjlee): remove this whole script file. +# (https://code.google.com/p/webrtc/issues/detail?id=2028) +function build_project() { + # make the target string + local target_string="" + if [[ -n "$2" ]]; then + target_string="-target $2" + fi + + xcodebuild -project "$1" -sdk iphoneos -arch armv7 \ + -configuration ${CONFIGURATION} \ + -CONFIGURATION_BUILD_DIR=${CONFIGURATION_BUILD_DIR} $target_string +} + +# change the working directory to trunk +cd "$( dirname "$0" )/../.." + +# build setting +CONFIGURATION_BUILD_DIR=./xcodebuild +CONFIGURATION=Debug +export GYP_DEFINES="OS=ios target_arch=arm armv7=1 arm_neon=1" +# TODO(sjlee): remove this script. +# (https://webrtc-codereview.appspot.com/1874005) + +# update gyp settings +echo '[Updating gyp settings...]' +gclient runhooks +./build/gyp_chromium --depth=. \ +webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp +./build/gyp_chromium --depth=. \ +webrtc/modules/video_coding/utility/video_coding_utility.gyp +./build/gyp_chromium --depth=. third_party/opus/opus.gyp +./build/gyp_chromium --depth=. third_party/libyuv/libyuv.gyp +./build/gyp_chromium --depth=. third_party/libjpeg/libjpeg.gyp + +# build the xcode projects +echo '[Building xcode projects...]' + +build_project "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.xcodeproj" +build_project "webrtc/modules/video_coding/utility/video_coding_utility.xcodeproj" +build_project "third_party/opus/opus.xcodeproj" "opus" +build_project "third_party/libjpeg/libjpeg.xcodeproj" +build_project "third_party/libyuv/libyuv.xcodeproj" + +# build the libvpx +cd third_party/libvpx/source/libvpx + +./configure --target=armv7-darwin-gcc --disable-vp9 \ + --libc=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk + +make + +cd - + +cp third_party/libvpx/source/libvpx/libvpx.a \ + ${CONFIGURATION_BUILD_DIR}/${CONFIGURATION}-iphoneos + +echo "[Building xcode projects is success...]\n" diff --git a/webrtc/modules/video_capture/ios/device_info_ios.h b/webrtc/modules/video_capture/ios/device_info_ios.h new file mode 100644 index 000000000..bb2ec8c60 --- /dev/null +++ b/webrtc/modules/video_capture/ios/device_info_ios.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_H_ + +#include "webrtc/modules/video_capture/device_info_impl.h" + +namespace webrtc { +namespace videocapturemodule { +class DeviceInfoIos : public DeviceInfoImpl { + public: + explicit DeviceInfoIos(const int32_t device_id); + virtual ~DeviceInfoIos(); + + // Implementation of DeviceInfoImpl. + virtual int32_t Init() OVERRIDE; + virtual uint32_t NumberOfDevices() OVERRIDE; + virtual int32_t GetDeviceName( + uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8 = 0, + uint32_t productUniqueIdUTF8Length = 0) OVERRIDE; + + virtual int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) OVERRIDE; + + virtual int32_t GetCapability(const char* deviceUniqueIdUTF8, + const uint32_t deviceCapabilityNumber, + VideoCaptureCapability& capability) OVERRIDE; + + virtual int32_t GetBestMatchedCapability( + const char* deviceUniqueIdUTF8, + const VideoCaptureCapability& requested, + VideoCaptureCapability& resulting) OVERRIDE; + + virtual int32_t DisplayCaptureSettingsDialogBox( + const char* deviceUniqueIdUTF8, + const char* dialogTitleUTF8, + void* parentWindow, + uint32_t positionX, + uint32_t positionY) OVERRIDE; + + virtual int32_t GetOrientation(const char* deviceUniqueIdUTF8, + VideoCaptureRotation& orientation) OVERRIDE; + + virtual int32_t CreateCapabilityMap( + const char* device_unique_id_utf8) OVERRIDE; +}; + +} // namespace videocapturemodule +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_H_ diff --git a/webrtc/modules/video_capture/ios/device_info_ios.mm b/webrtc/modules/video_capture/ios/device_info_ios.mm new file mode 100644 index 000000000..c51a53a33 --- /dev/null +++ b/webrtc/modules/video_capture/ios/device_info_ios.mm @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 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 "webrtc/modules/video_capture/ios/device_info_ios.h" +#include "webrtc/modules/video_capture/ios/device_info_ios_objc.h" +#include "webrtc/modules/video_capture/video_capture_impl.h" +#include "webrtc/system_wrappers/interface/trace.h" + +using namespace webrtc; +using namespace videocapturemodule; + +#define IOS_UNSUPPORTED() \ + WEBRTC_TRACE(kTraceError, \ + kTraceVideoCapture, \ + _id, \ + "%s is not supported on the iOS platform.", \ + __FUNCTION__); \ + return -1; + +VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo( + const int32_t device_id) { + return new DeviceInfoIos(device_id); +} + +DeviceInfoIos::DeviceInfoIos(const int32_t device_id) + : DeviceInfoImpl(device_id) {} + +DeviceInfoIos::~DeviceInfoIos() {} + +int32_t DeviceInfoIos::Init() { return 0; } + +uint32_t DeviceInfoIos::NumberOfDevices() { + return [DeviceInfoIosObjC captureDeviceCount]; +} + +int32_t DeviceInfoIos::GetDeviceName(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameUTF8Length, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length) { + NSString* deviceName = [DeviceInfoIosObjC deviceNameForIndex:deviceNumber]; + + NSString* deviceUniqueId = + [DeviceInfoIosObjC deviceUniqueIdForIndex:deviceNumber]; + + strncpy(deviceNameUTF8, [deviceName UTF8String], deviceNameUTF8Length); + deviceNameUTF8[deviceNameUTF8Length - 1] = '\0'; + + strncpy(deviceUniqueIdUTF8, + [deviceUniqueId UTF8String], + deviceUniqueIdUTF8Length); + deviceUniqueIdUTF8[deviceUniqueIdUTF8Length - 1] = '\0'; + + if (productUniqueIdUTF8) { + productUniqueIdUTF8[0] = '\0'; + } + + return 0; +} + +int32_t DeviceInfoIos::NumberOfCapabilities(const char* deviceUniqueIdUTF8) { + IOS_UNSUPPORTED(); +} + +int32_t DeviceInfoIos::GetCapability(const char* deviceUniqueIdUTF8, + const uint32_t deviceCapabilityNumber, + VideoCaptureCapability& capability) { + IOS_UNSUPPORTED(); +} + +int32_t DeviceInfoIos::GetBestMatchedCapability( + const char* deviceUniqueIdUTF8, + const VideoCaptureCapability& requested, + VideoCaptureCapability& resulting) { + IOS_UNSUPPORTED(); +} + +int32_t DeviceInfoIos::DisplayCaptureSettingsDialogBox( + const char* deviceUniqueIdUTF8, + const char* dialogTitleUTF8, + void* parentWindow, + uint32_t positionX, + uint32_t positionY) { + IOS_UNSUPPORTED(); +} + +int32_t DeviceInfoIos::GetOrientation(const char* deviceUniqueIdUTF8, + VideoCaptureRotation& orientation) { + if (strcmp(deviceUniqueIdUTF8, "Front Camera") == 0) { + orientation = kCameraRotate0; + } else { + orientation = kCameraRotate90; + } + return orientation; +} + +int32_t DeviceInfoIos::CreateCapabilityMap(const char* deviceUniqueIdUTF8) { + IOS_UNSUPPORTED(); +} diff --git a/webrtc/modules/video_capture/ios/device_info_ios_objc.h b/webrtc/modules/video_capture/ios/device_info_ios_objc.h new file mode 100644 index 000000000..b4ab0cb22 --- /dev/null +++ b/webrtc/modules/video_capture/ios/device_info_ios_objc.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_OBJC_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_OBJC_H_ + +#import + +@interface DeviceInfoIosObjC : NSObject ++ (int)captureDeviceCount; ++ (AVCaptureDevice*)captureDeviceForIndex:(int)index; ++ (AVCaptureDevice*)captureDeviceForUniqueId:(NSString*)uniqueId; ++ (NSString*)deviceNameForIndex:(int)index; ++ (NSString*)deviceUniqueIdForIndex:(int)index; ++ (NSString*)deviceNameForUniqueId:(NSString*)uniqueId; +@end + +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_DEVICE_INFO_IOS_OBJC_H_ diff --git a/webrtc/modules/video_capture/ios/device_info_ios_objc.mm b/webrtc/modules/video_capture/ios/device_info_ios_objc.mm new file mode 100644 index 000000000..2d11a2043 --- /dev/null +++ b/webrtc/modules/video_capture/ios/device_info_ios_objc.mm @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 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. + */ + +#import + +#import "webrtc/modules/video_capture/ios/device_info_ios_objc.h" + +@implementation DeviceInfoIosObjC + ++ (int)captureDeviceCount { + return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count]; +} + ++ (AVCaptureDevice*)captureDeviceForIndex:(int)index { + return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] + objectAtIndex:index]; +} + ++ (AVCaptureDevice*)captureDeviceForUniqueId:(NSString*)uniqueId { + for (AVCaptureDevice* device in + [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if ([uniqueId isEqual:device.uniqueID]) { + return device; + } + } + + return nil; +} + ++ (NSString*)deviceNameForIndex:(int)index { + return [DeviceInfoIosObjC captureDeviceForIndex:index].localizedName; +} + ++ (NSString*)deviceUniqueIdForIndex:(int)index { + return [DeviceInfoIosObjC captureDeviceForIndex:index].uniqueID; +} + ++ (NSString*)deviceNameForUniqueId:(NSString*)uniqueId { + return [[AVCaptureDevice deviceWithUniqueID:uniqueId] localizedName]; +} + +@end diff --git a/webrtc/modules/video_capture/ios/video_capture_ios.h b/webrtc/modules/video_capture/ios/video_capture_ios.h new file mode 100644 index 000000000..5d7e4b357 --- /dev/null +++ b/webrtc/modules/video_capture/ios/video_capture_ios.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_H_ + +#include "webrtc/modules/video_capture/video_capture_impl.h" + +@class VideoCaptureIosObjC; + +namespace webrtc { +namespace videocapturemodule { +class VideoCaptureIos : public VideoCaptureImpl { + public: + explicit VideoCaptureIos(const int32_t capture_id); + virtual ~VideoCaptureIos(); + + static VideoCaptureModule* Create(const int32_t capture_id, + const char* device_unique_id_utf8); + + // Implementation of VideoCaptureImpl. + virtual int32_t StartCapture( + const VideoCaptureCapability& capability) OVERRIDE; + virtual int32_t StopCapture() OVERRIDE; + virtual bool CaptureStarted() OVERRIDE; + virtual int32_t CaptureSettings(VideoCaptureCapability& settings) OVERRIDE; + + private: + VideoCaptureIosObjC* capture_device_; + bool is_capturing_; + int32_t id_; + VideoCaptureCapability capability_; +}; + +} // namespace videocapturemodule +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_H_ diff --git a/webrtc/modules/video_capture/ios/video_capture_ios.mm b/webrtc/modules/video_capture/ios/video_capture_ios.mm new file mode 100644 index 000000000..bb576c3fc --- /dev/null +++ b/webrtc/modules/video_capture/ios/video_capture_ios.mm @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013 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 "webrtc/modules/video_capture/ios/device_info_ios_objc.h" +#include "webrtc/modules/video_capture/ios/video_capture_ios_objc.h" +#include "webrtc/system_wrappers/interface/ref_count.h" +#include "webrtc/system_wrappers/interface/scoped_refptr.h" +#include "webrtc/system_wrappers/interface/trace.h" + +using namespace webrtc; +using namespace videocapturemodule; + +VideoCaptureModule* VideoCaptureImpl::Create(const int32_t capture_id, + const char* deviceUniqueIdUTF8) { + return VideoCaptureIos::Create(capture_id, deviceUniqueIdUTF8); +} + +VideoCaptureIos::VideoCaptureIos(const int32_t capture_id) + : VideoCaptureImpl(capture_id), is_capturing_(false), id_(capture_id) { + capability_.width = kDefaultWidth; + capability_.height = kDefaultHeight; + capability_.maxFPS = kDefaultFrameRate; +} + +VideoCaptureIos::~VideoCaptureIos() { + if (capture_device_) { + [capture_device_ stopCapture]; + } +} + +VideoCaptureModule* VideoCaptureIos::Create(const int32_t capture_id, + const char* deviceUniqueIdUTF8) { + if (!deviceUniqueIdUTF8[0]) { + return NULL; + } + + RefCountImpl* capture_module = + new RefCountImpl(capture_id); + + const int32_t name_length = strlen(deviceUniqueIdUTF8); + if (name_length > kVideoCaptureUniqueNameLength) + return NULL; + + capture_module->_deviceUniqueId = new char[name_length + 1]; + strncpy(capture_module->_deviceUniqueId, deviceUniqueIdUTF8, name_length + 1); + capture_module->_deviceUniqueId[name_length] = '\0'; + + capture_module->capture_device_ = + [[VideoCaptureIosObjC alloc] initWithOwner:capture_module + captureId:capture_module->id_]; + if (!capture_module->capture_device_) { + return NULL; + } + + if (![capture_module->capture_device_ setCaptureDeviceByUniqueId:[ + [NSString alloc] initWithCString:deviceUniqueIdUTF8 + encoding:NSUTF8StringEncoding]]) { + return NULL; + } + return capture_module; +} + +int32_t VideoCaptureIos::StartCapture( + const VideoCaptureCapability& capability) { + capability_ = capability; + + if (![capture_device_ startCaptureWithCapability:capability]) { + return -1; + } + + is_capturing_ = true; + + return 0; +} + +int32_t VideoCaptureIos::StopCapture() { + if (![capture_device_ stopCapture]) { + return -1; + } + + is_capturing_ = false; + + return 0; +} + +bool VideoCaptureIos::CaptureStarted() { return is_capturing_; } + +int32_t VideoCaptureIos::CaptureSettings(VideoCaptureCapability& settings) { + settings = capability_; + settings.rawType = kVideoNV12; + return 0; +} diff --git a/webrtc/modules/video_capture/ios/video_capture_ios_objc.h b/webrtc/modules/video_capture/ios/video_capture_ios_objc.h new file mode 100644 index 000000000..8e50facba --- /dev/null +++ b/webrtc/modules/video_capture/ios/video_capture_ios_objc.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ +#define WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ + +#import + +#include "webrtc/modules/video_capture/ios/video_capture_ios.h" + +@interface VideoCaptureIosObjC + : UIViewController { + @private + webrtc::videocapturemodule::VideoCaptureIos* _owner; + webrtc::VideoCaptureCapability _capability; + AVCaptureSession* _captureSession; + int _captureId; +} + +@property webrtc::VideoCaptureRotation frameRotation; + +// custom initializer. Instance of VideoCaptureIos is needed +// for callback purposes. +// default init methods have been overridden to return nil. +- (id)initWithOwner:(webrtc::videocapturemodule::VideoCaptureIos*)owner + captureId:(int)captureId; +- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniequeId; +- (BOOL)startCaptureWithCapability: + (const webrtc::VideoCaptureCapability&)capability; +- (BOOL)stopCapture; + +@end +#endif // WEBRTC_MODULES_VIDEO_CAPTURE_IOS_VIDEO_CAPTURE_IOS_OBJC_H_ diff --git a/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm b/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm new file mode 100644 index 000000000..67cc5d1df --- /dev/null +++ b/webrtc/modules/video_capture/ios/video_capture_ios_objc.mm @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2013 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. + */ + +#import "webrtc/modules/video_capture/ios/device_info_ios_objc.h" +#import "webrtc/modules/video_capture/ios/video_capture_ios_objc.h" + +#include "webrtc/system_wrappers/interface/trace.h" + +using namespace webrtc; +using namespace webrtc::videocapturemodule; + +@interface VideoCaptureIosObjC (hidden) +- (int)changeCaptureInputWithName:(NSString*)captureDeviceName; + +@end + +@implementation VideoCaptureIosObjC + +@synthesize frameRotation = _framRotation; + +- (id)initWithOwner:(VideoCaptureIos*)owner captureId:(int)captureId { + if (self == [super init]) { + _owner = owner; + _captureId = captureId; + _captureSession = [[AVCaptureSession alloc] init]; + + if (!_captureSession) { + return nil; + } + + // create and configure a new output (using callbacks) + AVCaptureVideoDataOutput* captureOutput = + [[AVCaptureVideoDataOutput alloc] init]; + [captureOutput setSampleBufferDelegate:self + queue:dispatch_get_current_queue()]; + NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; + + NSNumber* val = [NSNumber + numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]; + NSDictionary* videoSettings = + [NSDictionary dictionaryWithObject:val forKey:key]; + captureOutput.videoSettings = videoSettings; + + // add new output + if ([_captureSession canAddOutput:captureOutput]) { + [_captureSession addOutput:captureOutput]; + } else { + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d Could not add output to AVCaptureSession ", + __FILE__, + __FUNCTION__, + __LINE__); + } + + NSNotificationCenter* notify = [NSNotificationCenter defaultCenter]; + [notify addObserver:self + selector:@selector(onVideoError:) + name:AVCaptureSessionRuntimeErrorNotification + object:_captureSession]; + } + + return self; +} + +- (BOOL)setCaptureDeviceByUniqueId:(NSString*)uniqueId { + // check to see if the camera is already set + if (_captureSession) { + NSArray* currentInputs = [NSArray arrayWithArray:[_captureSession inputs]]; + if ([currentInputs count] > 0) { + AVCaptureDeviceInput* currentInput = [currentInputs objectAtIndex:0]; + if ([uniqueId isEqualToString:[currentInput.device localizedName]]) { + return YES; + } + } + } + + return [self changeCaptureInputByUniqueId:uniqueId]; +} + +- (BOOL)startCaptureWithCapability:(const VideoCaptureCapability&)capability { + if (!_captureSession) { + return NO; + } + + // check limits of the resolution + if (capability.maxFPS < 0 || capability.maxFPS > 60) { + return NO; + } + + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { + if (capability.width > 1920 || capability.height > 1080) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset1280x720]) { + if (capability.width > 1280 || capability.height > 720) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset640x480]) { + if (capability.width > 640 || capability.height > 480) { + return NO; + } + } else if ([_captureSession + canSetSessionPreset:AVCaptureSessionPreset352x288]) { + if (capability.width > 352 || capability.height > 288) { + return NO; + } + } else if (capability.width < 0 || capability.height < 0) { + return NO; + } + + _capability = capability; + + NSArray* currentOutputs = [_captureSession outputs]; + if ([currentOutputs count] == 0) { + return NO; + } + + NSString* captureQuality = + [NSString stringWithString:AVCaptureSessionPresetLow]; + if (_capability.width >= 1920 || _capability.height >= 1080) { + captureQuality = + [NSString stringWithString:AVCaptureSessionPreset1920x1080]; + } else if (_capability.width >= 1280 || _capability.height >= 720) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset1280x720]; + } else if (_capability.width >= 640 || _capability.height >= 480) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset640x480]; + } else if (_capability.width >= 352 || _capability.height >= 288) { + captureQuality = [NSString stringWithString:AVCaptureSessionPreset352x288]; + } + + AVCaptureVideoDataOutput* currentOutput = + (AVCaptureVideoDataOutput*)[currentOutputs objectAtIndex:0]; + + // begin configuration for the AVCaptureSession + [_captureSession beginConfiguration]; + + // picture resolution + [_captureSession setSessionPreset:captureQuality]; + + // take care of capture framerate now + AVCaptureConnection* connection = + [currentOutput connectionWithMediaType:AVMediaTypeVideo]; + + CMTime cm_time = {1, _capability.maxFPS, kCMTimeFlags_Valid, 0}; + + [connection setVideoMinFrameDuration:cm_time]; + [connection setVideoMaxFrameDuration:cm_time]; + + // finished configuring, commit settings to AVCaptureSession. + [_captureSession commitConfiguration]; + + [_captureSession startRunning]; + + [captureQuality release]; + + return YES; +} + +- (void)onVideoError { + // TODO(sjlee): make the specific error handling with this notification. + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d [AVCaptureSession startRunning] error.", + __FILE__, + __FUNCTION__, + __LINE__); +} + +- (BOOL)stopCapture { + if (!_captureSession) { + return NO; + } + + [_captureSession stopRunning]; + + return YES; +} + +- (BOOL)changeCaptureInputByUniqueId:(NSString*)uniqueId { + NSArray* currentInputs = [_captureSession inputs]; + // remove current input + if ([currentInputs count] > 0) { + AVCaptureInput* currentInput = + (AVCaptureInput*)[currentInputs objectAtIndex:0]; + + [_captureSession removeInput:currentInput]; + } + + // Look for input device with the name requested (as our input param) + // get list of available capture devices + int captureDeviceCount = [DeviceInfoIosObjC captureDeviceCount]; + if (captureDeviceCount <= 0) { + return NO; + } + + AVCaptureDevice* captureDevice = + [DeviceInfoIosObjC captureDeviceForUniqueId:uniqueId]; + + if (!captureDevice) { + return NO; + } + + // now create capture session input out of AVCaptureDevice + NSError* deviceError = nil; + AVCaptureDeviceInput* newCaptureInput = + [AVCaptureDeviceInput deviceInputWithDevice:captureDevice + error:&deviceError]; + + if (!newCaptureInput) { + const char* errorMessage = [[deviceError localizedDescription] UTF8String]; + + WEBRTC_TRACE(kTraceError, + kTraceVideoCapture, + _captureId, + "%s:%s:%d deviceInputWithDevice error:%s", + __FILE__, + __FUNCTION__, + __LINE__, + errorMessage); + + return NO; + } + + // try to add our new capture device to the capture session + [_captureSession beginConfiguration]; + + BOOL addedCaptureInput = NO; + if ([_captureSession canAddInput:newCaptureInput]) { + [_captureSession addInput:newCaptureInput]; + addedCaptureInput = YES; + } else { + addedCaptureInput = NO; + } + + [_captureSession commitConfiguration]; + + return addedCaptureInput; +} + +- (void)captureOutput:(AVCaptureOutput*)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection*)connection { + const int kFlags = 0; + CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer); + + if (CVPixelBufferLockBaseAddress(videoFrame, kFlags) != kCVReturnSuccess) { + return; + } + + const int kYPlaneIndex = 0; + const int kUVPlaneIndex = 1; + + uint8_t* baseAddress = + (uint8_t*)CVPixelBufferGetBaseAddressOfPlane(videoFrame, kYPlaneIndex); + int yPlaneBytesPerRow = + CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kYPlaneIndex); + int yPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kYPlaneIndex); + int uvPlaneBytesPerRow = + CVPixelBufferGetBytesPerRowOfPlane(videoFrame, kUVPlaneIndex); + int uvPlaneHeight = CVPixelBufferGetHeightOfPlane(videoFrame, kUVPlaneIndex); + int frameSize = + yPlaneBytesPerRow * yPlaneHeight + uvPlaneBytesPerRow * uvPlaneHeight; + + VideoCaptureCapability tempCaptureCapability; + tempCaptureCapability.width = CVPixelBufferGetWidth(videoFrame); + tempCaptureCapability.height = CVPixelBufferGetHeight(videoFrame); + tempCaptureCapability.maxFPS = _capability.maxFPS; + tempCaptureCapability.rawType = kVideoNV12; + + _owner->IncomingFrame(baseAddress, frameSize, tempCaptureCapability, 0); + + CVPixelBufferUnlockBaseAddress(videoFrame, kFlags); +} + +@end diff --git a/webrtc/modules/video_capture/video_capture.gypi b/webrtc/modules/video_capture/video_capture.gypi index 6e06a170c..05f078b09 100644 --- a/webrtc/modules/video_capture/video_capture.gypi +++ b/webrtc/modules/video_capture/video_capture.gypi @@ -115,6 +115,18 @@ 'android/video_capture_android.h', ], }], # android + ['OS=="ios"', { + 'sources': [ + 'ios/device_info_ios.h', + 'ios/device_info_ios.mm', + 'ios/device_info_ios_objc.h', + 'ios/device_info_ios_objc.mm', + 'ios/video_capture_ios.h', + 'ios/video_capture_ios.mm', + 'ios/video_capture_ios_objc.h', + 'ios/video_capture_ios_objc.mm', + ], + }], # ios ], # conditions }], # include_internal_video_capture ], # conditions diff --git a/webrtc/modules/video_capture/video_capture_factory.cc b/webrtc/modules/video_capture/video_capture_factory.cc index 28337dfa5..5b44a6c70 100644 --- a/webrtc/modules/video_capture/video_capture_factory.cc +++ b/webrtc/modules/video_capture/video_capture_factory.cc @@ -30,17 +30,4 @@ VideoCaptureModule::DeviceInfo* VideoCaptureFactory::CreateDeviceInfo( return videocapturemodule::VideoCaptureImpl::CreateDeviceInfo(id); } -// TODO(sjlee): land https://webrtc-codereview.appspot.com/1641004/ -#ifdef WEBRTC_IOS -namespace videocapturemodule { -VideoCaptureModule* VideoCaptureImpl::Create(int32_t, const char*) { - return NULL; -} - -VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo(int32_t) { - return NULL; -} -} // namespace videocaptureimpl -#endif - } // namespace webrtc