ここでこのチュートリアルを使用: http://www.musicalgeometry.com/?p=1297AVCaptureSession
を使用してカスタムオーバーレイと画像キャプチャを作成しました。
ユーザーがフロントカメラとバックカメラを切り替えることを許可しようとしています。カメラを切り替えるCaptureSessionManager
のコードは次のとおりです。
- (void)addVideoInputFrontCamera:(BOOL)front {
NSArray *devices = [AVCaptureDevice devices];
AVCaptureDevice *frontCamera;
AVCaptureDevice *backCamera;
for (AVCaptureDevice *device in devices) {
//NSLog(@"Device name: %@", [device localizedName]);
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
//NSLog(@"Device position : back");
backCamera = device;
}
else {
//NSLog(@"Device position : front");
frontCamera = device;
}
}
}
NSError *error = nil;
if (front) {
AVCaptureDeviceInput *frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
if (!error) {
if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) {
[[self captureSession] addInput:frontFacingCameraDeviceInput];
} else {
NSLog(@"Couldn't add front facing video input");
}
}
} else {
AVCaptureDeviceInput *backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];
if (!error) {
if ([[self captureSession] canAddInput:backFacingCameraDeviceInput]) {
[[self captureSession] addInput:backFacingCameraDeviceInput];
} else {
NSLog(@"Couldn't add back facing video input");
}
}
}
}
カスタムオーバーレイコントローラーで、viewDidLoad
のようにすべてを初期化します。
[self setCaptureManager:[[CaptureSessionManager alloc] init]];
[[self captureManager] addVideoInputFrontCamera:NO]; // set to YES for Front Camera, No for Back camera
[[self captureManager] addStillImageOutput];
[[self captureManager] addVideoPreviewLayer];
CGRect layerRect = [[[self view] layer] bounds];
[[[self captureManager] previewLayer] setBounds:layerRect];
[[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))];
[[[self view] layer] addSublayer:[[self captureManager] previewLayer]];
[[_captureManager captureSession] startRunning];
カメラ切り替えボタンは、switchCamera
というメソッドに接続されています。私はこれを試しました:
- (void)switchCameraView:(id)sender {
[[self captureManager] addVideoInputFrontCamera:YES]; // set to YES for Front Camera, No for Back camera
}
これを呼び出すと、NSLog
からCaptureSessionManager
のエラーが発生し、その理由がわかりません。 viewDidLoad
で、fontCamera
をYES
に設定すると、前面カメラは表示されますが、背面カメラに切り替えることはできません。
適切に切り替える方法についてのアイデアはありますか?
最初に既存のAVCameraInputをAVCaptureSessionから削除してから、新しいAVCameraInputをAVCaptureSessionに追加する必要があります。私にとっては次のように動作します(ARCの下):
-(IBAction)switchCameraTapped:(id)sender
{
//Change camera source
if(_captureSession)
{
//Indicate that some changes will be made to the session
[_captureSession beginConfiguration];
//Remove existing input
AVCaptureInput* currentCameraInput = [_captureSession.inputs objectAtIndex:0];
[_captureSession removeInput:currentCameraInput];
//Get new input
AVCaptureDevice *newCamera = nil;
if(((AVCaptureDeviceInput*)currentCameraInput).device.position == AVCaptureDevicePositionBack)
{
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
}
else
{
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
}
//Add input to session
NSError *err = nil;
AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:newCamera error:&err];
if(!newVideoInput || err)
{
NSLog(@"Error creating capture device input: %@", err.localizedDescription);
}
else
{
[_captureSession addInput:newVideoInput];
}
//Commit all the configuration changes at once
[_captureSession commitConfiguration];
}
}
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
- (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition) position
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices)
{
if ([device position] == position) return device;
}
return nil;
}
Swift 4
@IBAction func switchCameraTapped(sender: Any) {
//Change camera source
if let session = captureSession {
//Indicate that some changes will be made to the session
session.beginConfiguration()
//Remove existing input
guard let currentCameraInput: AVCaptureInput = session.inputs.first else {
return
}
session.removeInput(currentCameraInput)
//Get new input
var newCamera: AVCaptureDevice! = nil
if let input = currentCameraInput as? AVCaptureDeviceInput {
if (input.device.position == .back) {
newCamera = cameraWithPosition(position: .front)
} else {
newCamera = cameraWithPosition(position: .back)
}
}
//Add input to session
var err: NSError?
var newVideoInput: AVCaptureDeviceInput!
do {
newVideoInput = try AVCaptureDeviceInput(device: newCamera)
} catch let err1 as NSError {
err = err1
newVideoInput = nil
}
if newVideoInput == nil || err != nil {
print("Error creating capture device input: \(err?.localizedDescription)")
} else {
session.addInput(newVideoInput)
}
//Commit all the configuration changes at once
session.commitConfiguration()
}
}
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
for device in discoverySession.devices {
if device.position == position {
return device
}
}
return nil
}
Swift 3編集(François-JulienAlcaraz回答と組み合わせて):
@IBAction func switchCameraTapped(sender: Any) {
//Change camera source
if let session = captureSession {
//Indicate that some changes will be made to the session
session.beginConfiguration()
//Remove existing input
guard let currentCameraInput: AVCaptureInput = session.inputs.first as? AVCaptureInput else {
return
}
session.removeInput(currentCameraInput)
//Get new input
var newCamera: AVCaptureDevice! = nil
if let input = currentCameraInput as? AVCaptureDeviceInput {
if (input.device.position == .back) {
newCamera = cameraWithPosition(position: .front)
} else {
newCamera = cameraWithPosition(position: .back)
}
}
//Add input to session
var err: NSError?
var newVideoInput: AVCaptureDeviceInput!
do {
newVideoInput = try AVCaptureDeviceInput(device: newCamera)
} catch let err1 as NSError {
err = err1
newVideoInput = nil
}
if newVideoInput == nil || err != nil {
print("Error creating capture device input: \(err?.localizedDescription)")
} else {
session.addInput(newVideoInput)
}
//Commit all the configuration changes at once
session.commitConfiguration()
}
}
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
if let discoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified) {
for device in discoverySession.devices {
if device.position == position {
return device
}
}
}
return nil
}
@ NES_4Lifeの答えの迅速なバージョン:
@IBAction func switchCameraTapped(sender: AnyObject) {
//Change camera source
if let session = captureSession {
//Indicate that some changes will be made to the session
session.beginConfiguration()
//Remove existing input
let currentCameraInput:AVCaptureInput = session.inputs.first as! AVCaptureInput
session.removeInput(currentCameraInput)
//Get new input
var newCamera:AVCaptureDevice! = nil
if let input = currentCameraInput as? AVCaptureDeviceInput {
if (input.device.position == .Back)
{
newCamera = cameraWithPosition(.Front)
}
else
{
newCamera = cameraWithPosition(.Back)
}
}
//Add input to session
var err: NSError?
var newVideoInput: AVCaptureDeviceInput!
do {
newVideoInput = try AVCaptureDeviceInput(device: newCamera)
} catch let err1 as NSError {
err = err1
newVideoInput = nil
}
if(newVideoInput == nil || err != nil)
{
print("Error creating capture device input: \(err!.localizedDescription)")
}
else
{
session.addInput(newVideoInput)
}
//Commit all the configuration changes at once
session.commitConfiguration()
}
}
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice?
{
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
for device in devices {
let device = device as! AVCaptureDevice
if device.position == position {
return device
}
}
return nil
}
以前の回答に基づいて、いくつかの検証と特定の変更を加えて独自のバージョンを作成しましたが、現在のカメラ入力はキャプチャセッションの入力の最初のオブジェクトではない可能性があるため、これを変更しました:
//Remove existing input
AVCaptureInput* currentCameraInput = [self.captureSession.inputs objectAtIndex:0];
[self.captureSession removeInput:currentCameraInput];
これに(すべてのビデオタイプ入力を削除する):
for (AVCaptureDeviceInput *input in self.captureSession.inputs) {
if ([input.device hasMediaType:AVMediaTypeVideo]) {
[self.captureSession removeInput:input];
break;
}
}
コード全体は次のとおりです。
if (!self.captureSession) return;
[self.captureSession beginConfiguration];
AVCaptureDeviceInput *currentCameraInput;
// Remove current (video) input
for (AVCaptureDeviceInput *input in self.captureSession.inputs) {
if ([input.device hasMediaType:AVMediaTypeVideo]) {
[self.captureSession removeInput:input];
currentCameraInput = input;
break;
}
}
if (!currentCameraInput) return;
// Switch device position
AVCaptureDevicePosition captureDevicePosition = AVCaptureDevicePositionUnspecified;
if (currentCameraInput.device.position == AVCaptureDevicePositionBack) {
captureDevicePosition = AVCaptureDevicePositionFront;
} else {
captureDevicePosition = AVCaptureDevicePositionBack;
}
// Select new camera
AVCaptureDevice *newCamera;
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *captureDevice in devices) {
if (captureDevice.position == captureDevicePosition) {
newCamera = captureDevice;
}
}
if (!newCamera) return;
// Add new camera input
NSError *error;
AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:newCamera error:&error];
if (!error && [self.captureSession canAddInput:newVideoInput]) {
[self.captureSession addInput:newVideoInput];
}
[self.captureSession commitConfiguration];
Swift 3
func switchCamera() {
session?.beginConfiguration()
let currentInput = session?.inputs.first as? AVCaptureDeviceInput
session?.removeInput(currentInput)
let newCameraDevice = currentInput?.device.position == .back ? getCamera(with: .front) : getCamera(with: .back)
let newVideoInput = try? AVCaptureDeviceInput(device: newCameraDevice)
session?.addInput(newVideoInput)
session?.commitConfiguration()
}
// MARK: - Private
extension CameraService {
func getCamera(with position: AVCaptureDevicePosition) -> AVCaptureDevice? {
guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else {
return nil
}
return devices.filter {
$0.position == position
}.first
}
}
Swift 4
これで完全な実装を確認できます Gist
非推奨の警告なしのcameraWithPositionのSwift 3バージョン:
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(_ position: AVCaptureDevicePosition) -> AVCaptureDevice?
{
if let deviceDescoverySession = AVCaptureDeviceDiscoverySession.init(deviceTypes: [AVCaptureDeviceType.builtInWideAngleCamera],
mediaType: AVMediaTypeVideo,
position: AVCaptureDevicePosition.unspecified) {
for device in deviceDescoverySession.devices {
if device.position == position {
return device
}
}
}
return nil
}
必要に応じて、deviceTypes配列を変更して、iPhone 7+(デュアルカメラ)から新しいdevicesTypesを取得することもできます。
ここに良い読み物があります: https://forums.developer.Apple.com/thread/63347
以下は、「複数のオーディオ/ビデオAVCaptureInputsは現在サポートされていません」の修正を含む、チェンサムのコードの更新バージョンです。
func switchCameraTapped() {
//Change camera source
//Indicate that some changes will be made to the session
session.beginConfiguration()
//Remove existing input
guard let currentCameraInput: AVCaptureInput = session.inputs.first else {
return
}
//Get new input
var newCamera: AVCaptureDevice! = nil
if let input = currentCameraInput as? AVCaptureDeviceInput {
if (input.device.position == .back) {
newCamera = cameraWithPosition(position: .front)
} else {
newCamera = cameraWithPosition(position: .back)
}
}
//Add input to session
var err: NSError?
var newVideoInput: AVCaptureDeviceInput!
do {
newVideoInput = try AVCaptureDeviceInput(device: newCamera)
} catch let err1 as NSError {
err = err1
newVideoInput = nil
}
if let inputs = session.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
session.removeInput(input)
}
}
if newVideoInput == nil || err != nil {
print("Error creating capture device input: \(err?.localizedDescription)")
} else {
session.addInput(newVideoInput)
}
//Commit all the configuration changes at once
session.commitConfiguration()
}
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
for device in discoverySession.devices {
if device.position == position {
return device
}
}
return nil
}