我想使用AVFoundation切换相机。这是我的代码 我有NSObject的子类
@interface CaptureSessionManager : NSObject
@property (retain) AVCaptureVideoPreviewLayer *previewLayer;
@property (retain) AVCaptureSession *captureSession;
@property (retain) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic, retain) UIImage *stillImage;
在@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];
}
在我的ViewController中,我有
@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];
}
当我点击toggleCamera按钮时,它会崩溃并显示以下错误
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)
我想添加切换功能,以便用户可以使用前/后相机点击照片。
非常感谢任何形式的帮助。提前谢谢。
答案 0 :(得分: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调用。
答案 1 :(得分: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];
}
答案 2 :(得分:0)
In swift toggle camera implementation is quite small and error prone. Swift really does some very great modification to check errors. So mine toggle camera code inside CameraController.swift looks like this -:
// 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
}()
In my view controller:
@IBAction func toggleCamera(sender: UIButton) {
// toggle the camera side
println("toggling the camera side")
self.cameraController.toggleCamera()
}
Hope it helps. Thanks