Switch cameras with avcapturesession

后端 未结 6 1633
借酒劲吻你
借酒劲吻你 2020-11-28 08:06

Using this tutorial here: http://www.musicalgeometry.com/?p=1297 I have created a custom overlay and image capture with AVCaptureSession.

I am attemptin

相关标签:
6条回答
  • 2020-11-28 08:19

    Based on previous answers I made my own version with some validations and one specific change, the current camera input might not be the first object of the capture session's inputs, so I changed this:

    //Remove existing input
    AVCaptureInput* currentCameraInput = [self.captureSession.inputs objectAtIndex:0];
    [self.captureSession removeInput:currentCameraInput];
    

    To this (removing all video type inputs):

    for (AVCaptureDeviceInput *input in self.captureSession.inputs) {
        if ([input.device hasMediaType:AVMediaTypeVideo]) {
            [self.captureSession removeInput:input];
            break;
        }
    }
    

    Here's the entire code:

    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];
    
    0 讨论(0)
  • 2020-11-28 08:20

    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

    You can check full implementation in this gist

    0 讨论(0)
  • 2020-11-28 08:27

    You first need to remove the existing AVCameraInput from the AVCaptureSession and then add a new AVCameraInput to the AVCaptureSession. The following works for me (under 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;
    }
    
    0 讨论(0)
  • 2020-11-28 08:29

    Swift 4/5

    @IBAction func switchCameraTapped(sender: Any) {
        //Change camera source
        if let session = captureSession {
            //Remove existing input
            guard let currentCameraInput: AVCaptureInput = session.inputs.first else {
                return
            }
    
            //Indicate that some changes will be made to the session
            session.beginConfiguration()
            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 Edit (Combined with François-Julien Alcaraz answer):

    @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
    }
    

    Swift version to @NES_4Life's answer:

    @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
    }
    
    0 讨论(0)
  • 2020-11-28 08:29

    Here is an updated version of chengsam's code that includes the fix for 'Multiple audio/video AVCaptureInputs are not currently supported'.

    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
    }
    
    0 讨论(0)
  • 2020-11-28 08:33

    Swift 3 version of cameraWithPosition without deprecated warning :

        // 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
    }
    

    If you want, you can also get the new devicesTypes from iPhone 7+ (dual camera) by changing the deviceTypes array.

    Here's a good read : https://forums.developer.apple.com/thread/63347

    0 讨论(0)
提交回复
热议问题