使用AVCaptureSession切换前后摄像头

16

我正在遵循SO上唯一的答案 -

使用AVCaptureSession切换相机

但是cameraWithPosition似乎不起作用。已经废弃了吗?

//Get new input
    AVCaptureDevice *newCamera = nil;
    if(((AVCaptureDeviceInput*)currentCameraInput).device.position == AVCaptureDevicePositionBack)
    {
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
    }
    else
    {
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
    }

cameraWithPosition 是一个自定义方法,请查看我的答案。 :) - 0yeoj
5个回答

12
你需要做的是重新配置你的AVCaptureSession
这里是我正在使用的内容:
// note that `AVCaptureSession * session`
//
if(session)
{
    [session beginConfiguration];

    AVCaptureInput *currentCameraInput = [session.inputs objectAtIndex:0];

    [session removeInput:currentCameraInput];

    AVCaptureDevice *newCamera = nil;

    if(((AVCaptureDeviceInput*)currentCameraInput).device.position == AVCaptureDevicePositionBack)
    {
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
    }
    else
    {
        newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
    }

    NSError *err = nil;

    AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:newCamera error:&err];

    if(!newVideoInput || err)
    {
        NSLog(@"Error creating capture device input: %@", err.localizedDescription);
    }
    else
    {
        [session addInput:newVideoInput];
    }

    [session commitConfiguration];
}

// make sure you have this method in your class
//
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position
{
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];

    for (AVCaptureDevice *device in devices)
    {
        if ([device position] == position)
            return device;
    }
    return nil;
}

这正是我所遇到的问题...我们可能从同一个链接获取了它。它一直给我这个“没有可见的@interface --- 选择器'cameraWithPosition:'”。 - durazno
@durazno,可能是你漏掉了什么,嘿嘿...看一下- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position中的(*)。你能展示一下你如何实现cameraWithPosition:吗? - 0yeoj
啊,我们实际上需要实现cameraWithPosition方法...我以为它只是随着AVFoundation一起提供的...好的,现在明白了。谢谢! - durazno
我遇到了同样的问题...当我在不同的相机之间切换时,委托方法didFinishRecordingToOutputFile会自动调用。 - Manish Ahuja
@ManishAhuja,可能是相同的方法,但问题不同。请发布您的问题以及一些必要的代码。 :) - 0yeoj
录制音频怎么办?切换相机后我会遇到音视频不对齐的问题。而且切换本身有时需要长达10秒钟的时间!!! - JCutting8

8

在Swift 3.0中

/// Swap camera and reconfigures camera session with new input
fileprivate func swapCamera() {

    // Get current input
    guard let input = cameraSession.inputs[0] as? AVCaptureDeviceInput else { return }

    // Begin new session configuration and defer commit
    cameraSession.beginConfiguration()
    defer { cameraSession.commitConfiguration() }

    // Create new capture device
    var newDevice: AVCaptureDevice?
    if input.device.position == .back {
        newDevice = captureDevice(with: .front)
    } else {
        newDevice = captureDevice(with: .back)
    }

    // Create new capture input
    var deviceInput: AVCaptureDeviceInput!
    do {
        deviceInput = try AVCaptureDeviceInput(device: newDevice)
    } catch let error {
        print(error.localizedDescription)
        return
    }

    // Swap capture device inputs
    cameraSession.removeInput(input)
    cameraSession.addInput(deviceInput)
}

/// Create new capture device with requested position
fileprivate func captureDevice(with position: AVCaptureDevicePosition) -> AVCaptureDevice? {

    let devices = AVCaptureDeviceDiscoverySession(deviceTypes: [ .builtInWideAngleCamera, .builtInMicrophone, .builtInDualCamera, .builtInTelephotoCamera ], mediaType: AVMediaTypeVideo, position: .unspecified).devices

    if let devices = devices {
        for device in devices {
            if device.position == position {
                return device
            }
        }
    }

    return nil
}

5

Swift 4.2的代码更新

/// Swap camera and reconfigures camera session with new input
fileprivate func swapCamera() {

    // Get current input
    guard let input = captureSession.inputs[0] as? AVCaptureDeviceInput else { return }

    // Begin new session configuration and defer commit
    captureSession.beginConfiguration()
    defer { captureSession.commitConfiguration() }

    // Create new capture device
    var newDevice: AVCaptureDevice?
    if input.device.position == .back {
        newDevice = captureDevice(with: .front)
    } else {
        newDevice = captureDevice(with: .back)
    }

    // Create new capture input
    var deviceInput: AVCaptureDeviceInput!
    do {
        deviceInput = try AVCaptureDeviceInput(device: newDevice!)
    } catch let error {
        print(error.localizedDescription)
        return
    }

    // Swap capture device inputs
    captureSession.removeInput(input)
    captureSession.addInput(deviceInput)
}

/// Create new capture device with requested position
fileprivate func captureDevice(with position: AVCaptureDevice.Position) -> AVCaptureDevice? {

    let devices = AVCaptureDevice.DiscoverySession(deviceTypes: [ .builtInWideAngleCamera, .builtInMicrophone, .builtInDualCamera, .builtInTelephotoCamera ], mediaType: AVMediaType.video, position: .unspecified).devices

    //if let devices = devices {
        for device in devices {
            if device.position == position {
                return device
            }
        }
    //}

    return nil
}

2

Swift 5的代码更新

extension CameraManager {
    func switchCamera() {
        captureSession.beginConfiguration()
        defer {captureSession.commitConfiguration()}

        let nextPosition = ((currentCameraInput as? AVCaptureDeviceInput)?.device.position == .front) ? AVCaptureDevice.Position.back : .front

        if let currentCameraInput = currentCameraInput {
            captureSession.removeInput(currentCameraInput)
        }

        if let newCamera = cameraDevice(position: nextPosition),
            let newVideoInput: AVCaptureDeviceInput = try? AVCaptureDeviceInput(device: newCamera),
            captureSession.canAddInput(newVideoInput) {

            captureSession.addInput(newVideoInput)
            currentCameraInput = newVideoInput

            videoDataOutput.connection(with: .video)?.videoOrientation = .portrait
            videoDataOutput.connection(with: .video)?.automaticallyAdjustsVideoMirroring = false
            videoDataOutput.connection(with: .video)?.isVideoMirrored = nextPosition == .front
        }
    }

    private func cameraDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
        let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
        for device in discoverySession.devices where device.position == position {
            return device
        }

        return nil
    }
}

使用AVCaptureSession的完整相机代码可以在这个代码片段中找到。


1

以下是使用switch进行视频会话的示例:

.h

UIViewController<AVCaptureFileOutputRecordingDelegate>

@property(nonatomic,strong)  AVCaptureSession *CaptureSession;
@property(nonatomic,strong) AVCaptureMovieFileOutput *MovieFileOutput;
@property(nonatomic,strong) AVCaptureDeviceInput *VideoInputDevice;

- (void) CameraSetOutputProperties;
- (AVCaptureDevice *) CameraWithPosition:(AVCaptureDevicePosition) Position;

然后:

.m

- (void)viewDidLoad {

   [super viewDidLoad];

   CaptureSession = [[AVCaptureSession alloc] init];

   //etc


}

- (IBAction)CameraToggle:(id)sender
{
    if ([[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count] > 1)        //Only do if device has multiple cameras
    {
        NSError *error;
        //AVCaptureDeviceInput *videoInput = [self videoInput];
        AVCaptureDeviceInput *NewVideoInput;
        AVCaptureDevicePosition position = [[VideoInputDevice device] position];
        if (position == AVCaptureDevicePositionBack)
        {
            NewVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self CameraWithPosition:AVCaptureDevicePositionFront] error:&error];
        }
        else if (position == AVCaptureDevicePositionFront)
        {
            NewVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self CameraWithPosition:AVCaptureDevicePositionBack] error:&error];
        }

        if (NewVideoInput != nil)
        {
            [CaptureSession beginConfiguration];
            [CaptureSession removeInput:VideoInputDevice];
            if ([CaptureSession canAddInput:NewVideoInput])
            {
                [CaptureSession addInput:NewVideoInput];
                VideoInputDevice = NewVideoInput;
            }
            else
            {
                [CaptureSession addInput:VideoInputDevice];
            }

            //Set the connection properties again
            [self CameraSetOutputProperties];


            [CaptureSession commitConfiguration];
        }
    }
}

cameraWithPosition方法来自哪里???它是来自我不知道的某个库吗? - durazno
在头文件中声明了一个方法 (AVCaptureDevice *) CameraWithPosition:(AVCaptureDevicePosition) Position;,它是根据当前捕获位置返回设备的方法。没有太多花哨的东西。 - lukeswitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接