3

I want to toggle camera using AVFoundation. Here is my code I have subclass of NSObject as

@interface CaptureSessionManager : NSObject

@property (retain) AVCaptureVideoPreviewLayer *previewLayer;
@property (retain) AVCaptureSession *captureSession;
@property (retain) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic, retain) UIImage *stillImage;

In @implementation CaptureSessionManager

    - (id)init {
    if ((self = [super init])) {
        [self setCaptureSession:[[AVCaptureSession alloc] init]];
    }
    return self;
}

- (void)addVideoPreviewLayer {
    [self setPreviewLayer:[[[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]] autorelease]];
    [[self previewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];

}

- (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");
            }
        }
    }
}

- (void)addStillImageOutput 
{
  [self setStillImageOutput:[[[AVCaptureStillImageOutput alloc] init] autorelease]];
  NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];
  [[self stillImageOutput] setOutputSettings:outputSettings];

  AVCaptureConnection *videoConnection = nil;
  for (AVCaptureConnection *connection in [[self stillImageOutput] connections]) {
    for (AVCaptureInputPort *port in [connection inputPorts]) {
      if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
        videoConnection = connection;
        break;
      }
    }
    if (videoConnection) { 
      break; 
    }
  }

  [[self captureSession] addOutput:[self stillImageOutput]];
}

- (void)captureStillImage
{  
    AVCaptureConnection *videoConnection = nil;
    for (AVCaptureConnection *connection in [[self stillImageOutput] connections]) {
        for (AVCaptureInputPort *port in [connection inputPorts]) {
            if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
                videoConnection = connection;
                break;
            }
        }
        if (videoConnection) { 
      break; 
    }
    }

    NSLog(@"about to request a capture from: %@", [self stillImageOutput]);
    [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection 
                                                       completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { 
                                                         CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
                                                         if (exifAttachments) {
                                                           NSLog(@"attachements: %@", exifAttachments);
                                                         } else { 
                                                           NSLog(@"no attachments");
                                                         }
                                                         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];    
                                                         UIImage *image = [[UIImage alloc] initWithData:imageData];
                                                         [self setStillImage:image];
                                                         [image release];
                                                         [[NSNotificationCenter defaultCenter] postNotificationName:kImageCapturedSuccessfully object:nil];
                                                       }];
}

- (void)dealloc {

    [[self captureSession] stopRunning];

    [previewLayer release], previewLayer = nil;
    [captureSession release], captureSession = nil;
  [stillImageOutput release], stillImageOutput = nil;
  [stillImage release], stillImage = nil;

    [super dealloc];
}

-(void)toggleCamera:(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;
            }
        }
    }
    [[self captureSession] beginConfiguration];
    NSError *error = nil;
    AVCaptureDeviceInput *frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
    AVCaptureDeviceInput *backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];
    if (front)
    {
        [[self captureSession] removeInput:backFacingCameraDeviceInput];
        if (!error) {
            if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) {
                [[self captureSession] addInput:frontFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add front facing video input");
            }
            [[self captureSession] addInput:frontFacingCameraDeviceInput];
        }
    } else
    {
        [[self captureSession] removeInput:frontFacingCameraDeviceInput];
        if (!error) {
            if ([[self captureSession] canAddInput:backFacingCameraDeviceInput]) {
                [[self captureSession] addInput:backFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add back facing video input");
            }
            [[self captureSession] addInput:backFacingCameraDeviceInput];
        }
    }

    [[self captureSession] commitConfiguration];
}

In my ViewController , I have

@property (retain) CaptureSessionManager *captureManager;
@property (nonatomic,readwrite) BOOL isFrontCameraSelected;

    - (void)viewDidLoad
{
    self.isFrontCameraSelected = NO;

    [self setCaptureManager:[[[CaptureSessionManager alloc] init] autorelease]];

    [[self captureManager] addVideoInputFrontCamera:self.isFrontCameraSelected]; // set to YES for Front Camera, No for Back camera

    [[self captureManager] addStillImageOutput];

    [[self captureManager] addVideoPreviewLayer];
    CGRect layerRect = [[[self innerview] layer] bounds];
    [[[self captureManager] previewLayer] setBounds:layerRect];
    [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))];
    [[[self innerview] layer] addSublayer:[[self captureManager] previewLayer]];

    UIImageView *overlayImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"overlaygraphic.png"]];
    [overlayImageView setFrame:CGRectMake(30, 60, 260, 260)];
    [[self innerview] addSubview:overlayImageView];
    [overlayImageView release];

    UIButton *overlayButton3 = [UIButton buttonWithType:UIButtonTypeCustom];
    [overlayButton3 setImage:[UIImage imageNamed:@"camera.png"] forState:UIControlStateNormal];
    [overlayButton3 setFrame:CGRectMake(130, 330, 60, 30)];
    [overlayButton3 addTarget:self action:@selector(innerscanButtonPressed) forControlEvents:UIControlEventTouchUpInside];
    [[self innerview] addSubview:overlayButton3];

    UIButton *cameraSelection = [UIButton buttonWithType:UIButtonTypeCustom];
    [cameraSelection setImage:[UIImage imageNamed:@"camera.png"] forState:UIControlStateNormal];
    [cameraSelection setFrame:CGRectMake(260, 30, 60, 30)];
    [cameraSelection addTarget:self action:@selector(cameraSelectionTapped:) forControlEvents:UIControlEventTouchUpInside];
    [[self innerview] addSubview:cameraSelection];

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveImageToPhotoAlbum) name:kImageCapturedSuccessfully object:nil];

    [[captureManager captureSession] startRunning];
}

- (void)innerscanButtonPressed {

    [[self scanningLabel] setHidden:NO];
    [[self captureManager] captureStillImage];

}

-(void)cameraSelectionTapped:(id)sender
{
    [[self captureManager] toggleCamera:self.isFrontCameraSelected];
}

When I click on toggleCamera button , it crashes with below error

Couldn't add back facing video input
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** Multiple audio/video AVCaptureInputs are not currently supported.'
*** First throw call stack:
(0x38f2f2a3 0x3725997f 0x39aff977 0x39aff091 0xe943d 0xe7875 0x3a4de0a5 0x3a4de057 0x3a4de035 0x3a4dd8eb 0x3a4ddde1 0x3a4065f1 0x3a3f3801 0x3a3f311b 0x37cad5a3 0x37cad1d3 0x38f04173 0x38f04117 0x38f02f99 0x38e75ebd 0x38e75d49 0x37cac2eb 0x3a4472f9 0xdf1ab 0x36324b20)

I want to add toggle feature so that user can use front / back camera to click photo.

Any kind of help is highly appreciated. Thanks in advance.

4

3 回答 3

5

您尝试在此代码中添加两次相同的设备输入:

if (!error) {
        if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) {
            [[self captureSession] addInput:frontFacingCameraDeviceInput];
        } else {
            NSLog(@"Couldn't add front facing video input");
        }
        [[self captureSession] addInput:frontFacingCameraDeviceInput];
    }

在前后逻辑上取消对 addInput 的第二次调用。

于 2013-05-16T16:43:25.673 回答
3

我编辑了您上传的代码并使切换工作正常。这是我的代码。

CaptureSessionManager.h - 我为设备输入添加了变量

@interface CaptureSessionManager : NSObject {
    AVCaptureDeviceInput *frontFacingCameraDeviceInput;
    AVCaptureDeviceInput *backFacingCameraDeviceInput;

}

@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
@property (retain) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic, retain) UIImage *stillImage;


-(void)addVideoPreviewLayer;
-(void)addVideoInput;;
- (void)addVideoInputFrontCamera:(BOOL)front;
- (void)addStillImageOutput;
- (void)captureStillImage;
-(void)toggleCamera:(BOOL)front;

@end

CaptureSessionManager.m - 我编辑了 addVideoInputFrontCamera 和 toggleCamera 函数

- (void)addVideoInputFrontCamera:(BOOL)front {
    [self.captureSession beginConfiguration];
    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;
    frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
    backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];
    if (front) {

        if (!error) {
            if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) {
                [[self captureSession] addInput:frontFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add front facing video input");
            }
        }
    } else {

        if (!error) {
            if ([[self captureSession] canAddInput:backFacingCameraDeviceInput]) {
                [[self captureSession] addInput:backFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add back facing video input");
            }
        }
    }

    AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    if (audioDevice) {
        NSError *error;
        AVCaptureDeviceInput *audioIn = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
        if (!error) {
            if ([[self captureSession] canAddInput:audioIn]){
                [[self captureSession] addInput:audioIn];
                NSLog(@"adding audioIn");
            }
            else
                NSLog(@"Couldn't add audio input");
        }
        else
            NSLog(@"Couldn't create audio input");
    }
    else
        NSLog(@"Couldn't create audio capture device");
    [self.captureSession commitConfiguration];
}

-(void)toggleCamera:(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;
            }
        }
    }
    [[self captureSession] beginConfiguration];
    NSError *error = nil;
   // AVCaptureDeviceInput *frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
   // AVCaptureDeviceInput *backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];
    if (front)
    {
        [[self captureSession] removeInput:backFacingCameraDeviceInput];
        if (!error) {

          //  frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
            if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) {
                [[self captureSession] addInput:frontFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add front facing video input");
            }

        }
    } else
    {
        [[self captureSession] removeInput:frontFacingCameraDeviceInput];
        if (!error) {
           // backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];

            if ([[self captureSession] canAddInput:backFacingCameraDeviceInput]) {
                [[self captureSession] addInput:backFacingCameraDeviceInput];
            } else {
                NSLog(@"Couldn't add back facing video input");
            }

        }
    }

    [[self captureSession] commitConfiguration];
}

然后在我的视图控制器上:

- (IBAction)cameraSelectionClicked:(id)sender {
    [self.captureManager.captureSession stopRunning];

    if (self.isFrontCameraSelected) {
        self.isFrontCameraSelected = NO;
    } else {
        self.isFrontCameraSelected = YES;

    }

    [[self captureManager] toggleCamera:self.isFrontCameraSelected];
    [self.captureManager.captureSession startRunning];
}
于 2013-06-25T09:05:53.860 回答
0

在快速切换相机中,实现非常小且容易出错。Swift 确实做了一些非常棒的修改来检查错误。所以我在CameraController.swift 中的切换相机代码看起来像这样:

// toggleCamera basic purpose is to toggle the camera from front to back or vice versa as per user selection

func toggleCamera(){
        println("toggleCamera -----")
        var error:NSError?
        var backCameraDevice:AVCaptureDevice?
        var audio:AVCaptureDevice?
        var frontCameraDevice:AVCaptureDevice?
        var rearCamera: AVCaptureInput?
        var frontCamera: AVCaptureInput?
        for device in availableCameraDevices as! [AVCaptureDevice] {
           if device.position == .Back {
            self.backCameraDevice = device
           }
           else if device.position == .Front {
            self.frontCameraDevice = device
           }
        }

        if let validVideoFrontDevice = self.frontCameraDevice {
                self.frontCamera = AVCaptureDeviceInput.deviceInputWithDevice(validVideoFrontDevice, error: &error) as! AVCaptureDeviceInput
        }

        if let validVideoBackDevice = self.backCameraDevice {
                self.rearCamera = AVCaptureDeviceInput.deviceInputWithDevice(validVideoBackDevice, error: &error) as! AVCaptureDeviceInput
        }

        session.beginConfiguration()
        let inputs = session.inputs as! [AVCaptureInput]
        let newCamera: AVCaptureDevice?
        if(currentCameraDevice!.position == AVCaptureDevicePosition.Back){
            println("Setting new camera with Front")
            newCamera = self.frontCameraDevice
            if self.hasFrontCamera {
                if let validBackDevice = self.rearCamera {
                    if contains(inputs, validBackDevice) {
                        session.removeInput(validBackDevice)
                    }
                }
                if let validFrontDevice = self.frontCamera {
                    if !contains(inputs, validFrontDevice) {
                        session.addInput(validFrontDevice)
                    }
                }
            }
        } else {
            println("Setting new camera with Back")
            newCamera = self.backCameraDevice
            if let validFrontDevice = self.frontCamera {
                if contains(inputs, validFrontDevice) {
                    session.removeInput(validFrontDevice)
                }
            }
            if let validBackDevice = self.rearCamera {
                if !contains(inputs, validBackDevice) {
                    session.addInput(validBackDevice)
                }
            }
        }
        session.commitConfiguration()
        if let validError = error {
            println("Device setup error occured \(validError.localizedDescription)")
        }
        currentCameraDevice! = newCamera!
}

    /// The Bool property to determine if current device has front camera.

public var hasFrontCamera: Bool = {
        let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
        for  device in devices  {
            let captureDevice = device as! AVCaptureDevice
            if (captureDevice.position == .Front) {
                return true
            }
        }
        return false
        }()

在我的视图控制器中:

@IBAction func toggleCamera(sender: UIButton) {
    //  toggle the camera side
    println("toggling the camera side")
    self.cameraController.toggleCamera()
}

希望能帮助到你。谢谢

于 2015-06-26T09:30:20.060 回答