iOS-AVCapture,自定義實(shí)現(xiàn)照相功能

關(guān)于iOS調(diào)用攝像機(jī)來獲取照片,通常我們都會(huì)調(diào)用UIImagePickerController來調(diào)用系統(tǒng)提供的相機(jī)來拍照,這個(gè)控件非常好用舅逸。但是有時(shí)UIImagePickerController控件無法滿足我們的需求粹淋,例如我們需要更加復(fù)雜的OverlayerView,這時(shí)候我們就要自己構(gòu)造一個(gè)攝像機(jī)控件了桶错。

0.AVCapture <AVFoundation/AVFoundation.h>
媒體采集需要的幾個(gè)對(duì)象:
1、AVCaptureDevice: 代表抽象的硬件設(shè)備(如前置攝像頭胀蛮,后置攝像頭等)院刁。
2、AVCaptureInput: 代表輸入設(shè)備(可以是它的子類)粪狼,它配置抽象硬件設(shè)備的ports退腥。
3任岸、AVCaptureOutput: 它代表輸出數(shù)據(jù),管理著輸出到一個(gè)movie或者圖像狡刘。
4享潜、AVCaptureSession: 它是input和output的橋梁。它協(xié)調(diào)著input到output的數(shù)據(jù)傳輸颓帝。

關(guān)系:
有很多Device的input米碰,也有很多數(shù)據(jù)類型的Output,都通過一個(gè)Capture Session來控制進(jìn)行傳輸购城。也即:CaptureDevice適配AVCaptureInput吕座,通過Session來輸入到AVCaptureOutput中。這樣也就達(dá)到了從設(shè)備到文件等持久化輸入目的(如從相機(jī)設(shè)備采集圖像到UIImage中)瘪板。

那么存在一個(gè)問題了:視頻輸入(input)就對(duì)應(yīng)視頻的輸出(output)吴趴,而音頻輸入就對(duì)應(yīng)音頻的輸出,因而需要建立對(duì)應(yīng)的Connections侮攀,來各自連接它們锣枝。而這樣的連接對(duì)象,是由AVCaptureSession來持有的兰英,這個(gè)對(duì)象叫AVCaptureConnection撇叁。
在一個(gè)AVCaptureConnection中,這里維持著對(duì)應(yīng)的數(shù)據(jù)傳輸輸入到數(shù)據(jù)輸出的過程(detail過程)畦贸。這里陨闹,AVCaptureInput或其子類對(duì)象包含著各種input port,通過各種input port薄坏,我們的AVCaptureOutput可以獲取相應(yīng)的數(shù)據(jù)趋厉。

一個(gè)AVCaptureConnection可以控制input到output的數(shù)據(jù)傳輸。

1.Session及其使用模式
You use an instance to coordinate the flow of data from AV input devices to outputs. You add the capture devices and outputs you want to the session, then start data flow by sending the session a startRunning message, and stop recording by sending a stopRunning message.

AVCaptureSession *session = [AVCaptureSession alloc] init];
[session startRunning];

這里表明了胶坠,需要create一個(gè)session君账,然后發(fā)running消息給它,它會(huì)自動(dòng)跑起來沈善,把輸入設(shè)備的東西乡数,提交到輸出設(shè)備中。

若想在一個(gè)已經(jīng)使用上的session中(已經(jīng)startRunning了)做更換新的device闻牡、刪除舊的device等一系列的操作瞳脓,那么就需要使用如下方法:
AVCaptureSession *session;
[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];

當(dāng)然,如果session的時(shí)候發(fā)生了異常澈侠,那么我們可以通過notification去observe相關(guān)的事件(可以在AVCaptureSession Class Reference中的Notifications中找到相應(yīng)的情況),而session如果出現(xiàn)相應(yīng)問題的時(shí)候埋酬,它會(huì)post出來哨啃,此時(shí)我們就可以處理了烧栋。

2.AVCaptureDevice,主要用來獲取iPhone一些關(guān)于相機(jī)設(shè)備的屬性。
InputDevice即是對(duì)硬件的抽象拳球,一對(duì)一的审姓。一個(gè)AVCaptureDevice對(duì)象,對(duì)應(yīng)一個(gè)實(shí)際的硬件設(shè)備祝峻。

那么顯然魔吐,我們可以通過AVCaptureDevice的類方法devices或devicesWithMediaType去獲取全部或局部設(shè)備列表。(當(dāng)然也可以檢測(cè)相應(yīng)的設(shè)備是否可以使用莱找,這里注意有設(shè)備搶占問題酬姆,當(dāng)前是否可用)

相機(jī)設(shè)備可以用下面的方法判斷設(shè)備是否支持相關(guān)屬性(property),比如對(duì)焦方式或者對(duì)焦?fàn)顟B(tài)Focus modes奥溺。
[currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus];

  1. 前置和后置攝像頭
    enum {
    AVCaptureDevicePositionBack = 1,
    AVCaptureDevicePositionFront = 2
    };
    typedef NSInteger AVCaptureDevicePosition;

  2. 閃光燈開關(guān)
    enum {
    AVCaptureFlashModeOff = 0,
    AVCaptureFlashModeOn = 1,
    AVCaptureFlashModeAuto = 2
    };
    typedef NSInteger AVCaptureFlashMode;

  3. 手電筒開關(guān)
    enum {
    AVCaptureTorchModelOff = 0,
    AVCaptureTorchModelOn = 1,
    AVCaptureTorchModeAuto = 2
    };
    typedef NSInteger AVCaptureTorchMode;

  4. 焦距調(diào)整
    enum {
    AVCaptureFocusModelLocked = 0,
    AVCaptureFocusModeAutoFocus = 1,
    AVCaptureFocusModeContinousAutoFocus = 2
    };
    typedef NSInteger AVCaptureFocusMode;

  5. 曝光量調(diào)節(jié)
    enum {
    AVCaptureExposureModeLocked = 0,
    AVCaptureExposureModeAutoExpose = 1,
    AVCaptureExposureModeContinuousAutoExposure = 2
    };
    typedef NSInteger AVCaptureExposureMode;

  6. 白平衡
    enum {
    AVCaptureWhiteBalanceModeLocked = 0,
    AVCaptureWhiteBalanceModeAutoWhiteBalance = 1,
    AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance = 2
    };
    typedef NSInteger AVCaptureWhiteBalanceMode;

  7. CaptureInput的構(gòu)建和添加到Session中的方法
    創(chuàng)建并配置輸入設(shè)備
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

// 添加input到session的模式是(檢查可否添加到session辞色,然后根據(jù)情況添加或者不添加)
AVCaptureSession *captureSession = <#Get a capture session#>;
if ([captureSession canAddInput:input]) {
[captureSession addInput:input];
}

4.output的分類和使用
ios中,分為MovieFile浮定、VideoData相满、AudioData和StillImage幾種output,使用方式類似桦卒,只是范圍不同立美。另外,它們都繼承于AVCaptureOutput方灾。

第一個(gè)是輸出成movie文件建蹄,第二個(gè)適用于逐個(gè)Frame的處理,第三個(gè)適用于聲音采集迎吵,第四個(gè)是still image(靜態(tài)圖像<拍照>)相關(guān)躲撰。

他們的添加方式都是使用session的addOutput方法。

5.AVCaptureStillImageOutput 照片輸出流對(duì)象

  1. AVCaptureVideoPreviewLayer 預(yù)覽圖層击费,來顯示照相機(jī)拍攝到的畫面
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()
{
    
}

// AVCaptureSession對(duì)象來執(zhí)行輸入設(shè)備和輸出設(shè)備之間的數(shù)據(jù)傳遞
@property (nonatomic, strong)AVCaptureSession *session;

// AVCaptureDeviceInput對(duì)象是輸入流
@property (nonatomic, strong)AVCaptureDeviceInput *videoInput;

// 照片輸出流對(duì)象
@property (nonatomic, strong)AVCaptureStillImageOutput *stillImageOutput;

// 預(yù)覽圖層拢蛋,來顯示照相機(jī)拍攝到的畫面
@property (nonatomic, strong)AVCaptureVideoPreviewLayer *previewLayer;

// 切換前后鏡頭的按鈕
@property (nonatomic, strong)UIButton *toggleButton;

// 拍照按鈕
@property (nonatomic, strong)UIButton *shutterButton;

// 放置預(yù)覽圖層的View
@property (nonatomic, strong)UIView *cameraShowView;

// 用來展示拍照獲取的照片
@property (nonatomic, strong)UIImageView *imageShowView;

@end

@implementation ViewController

- (id)init {
    self = [super init];
    if (self) {
        [self initialSession];
        [self initCameraShowView];
        [self initImageShowView];
        [self initButton];
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self setUpCameraLayer];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    if (self.session) {
        [self.session startRunning];
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    if (self.session) {
        [self.session stopRunning];
    }
}

- (void)initialSession {
    self.session = [[AVCaptureSession alloc] init];
    
    self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backCamera] error:nil];
    
    self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    // 這是輸出流的設(shè)置參數(shù)AVVideoCodecJPEG參數(shù)表示以JPEG的圖片格式輸出圖片
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
    [self.stillImageOutput setOutputSettings:outputSettings];
    
    if ([self.session canAddInput:self.videoInput]) {
        [self.session addInput:self.videoInput];
    }
    
    if ([self.session canAddOutput:self.stillImageOutput]) {
        [self.session addOutput:self.stillImageOutput];
    }
}

- (void)initCameraShowView {
    self.cameraShowView = [[UIView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:self.cameraShowView];
}

- (void)initImageShowView {
    self.imageShowView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height -200, 200, 200)];
    self.imageShowView.contentMode = UIViewContentModeScaleToFill;
    self.imageShowView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.imageShowView];
}

- (void)initButton {
    self.shutterButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.shutterButton.frame = CGRectMake(10, 30, 60, 30);
    self.shutterButton.backgroundColor = [UIColor cyanColor];
    [self.shutterButton setTitle:@"拍照" forState:UIControlStateNormal];
    [self.shutterButton addTarget:self action:@selector(shutterCamera)forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.shutterButton];
    
    self.toggleButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.toggleButton.frame = CGRectMake(80, 30, 60, 30);
    self.toggleButton.backgroundColor = [UIColor cyanColor];
    [self.toggleButton setTitle:@"切換攝像頭" forState:UIControlStateNormal];
    [self.toggleButton addTarget:self action:@selector(toggleCamera)forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.toggleButton];
}

// 這是獲取前后攝像頭對(duì)象的方法
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *device in devices) {
        if (device.position == position) {
            return device;
        }
    }
    return nil;
}

- (AVCaptureDevice *)frontCamera {
    return [self cameraWithPosition:AVCaptureDevicePositionFront];
}

- (AVCaptureDevice *)backCamera {
    return [self cameraWithPosition:AVCaptureDevicePositionBack];
}

- (void)setUpCameraLayer {
    if (self.previewLayer == nil) {
        self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
        UIView * view = self.cameraShowView;
        CALayer * viewLayer = [view layer];
        // UIView的clipsToBounds屬性和CALayer的setMasksToBounds屬性表達(dá)的意思是一致的,決定子視圖的顯示范圍。當(dāng)取值為YES的時(shí)候蔫巩,剪裁超出父視圖范圍的子視圖部分谆棱,當(dāng)取值為NO時(shí),不剪裁子視圖圆仔。
        [viewLayer setMasksToBounds:YES];
        
        CGRect bounds = [view bounds];
        [self.previewLayer setFrame:bounds];
        [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
        
        [viewLayer addSublayer:self.previewLayer];
    }
}

// 這是拍照按鈕的方法
- (void)shutterCamera {
    AVCaptureConnection *videoConnection = [self.stillImageOutputconnectionWithMediaType:AVMediaTypeVideo];
    if (!videoConnection) {
        return;
    }
    
    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnectioncompletionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        if (imageDataSampleBuffer == NULL) {
            return;
        }
        NSData *imageData = [AVCaptureStillImageOutputjpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        UIImage *image = [UIImage imageWithData:imageData];
        NSLog(@"image size = %@", NSStringFromCGSize(image.size));
        self.imageShowView.image = image;
    }];
}

// 這是切換鏡頭的按鈕方法
- (void)toggleCamera {
    NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
    if (cameraCount > 1) {
        NSError *error;
        AVCaptureDeviceInput *newVideoInput;
        AVCaptureDevicePosition position = [[_videoInput device] position];
        
        if (position == AVCaptureDevicePositionBack) {
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self frontCamera] error:&error];
        } else if (position == AVCaptureDevicePositionFront) {
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backCamera] error:&error];
        } else {
            return;
        }
        
        if (newVideoInput != nil) {
            [self.session beginConfiguration];
            [self.session removeInput:self.videoInput];
            if ([self.session canAddInput:newVideoInput]) {
                [self.session addInput:newVideoInput];
                self.videoInput = newVideoInput;
            } else {
                [self.session addInput:self.videoInput];
            }
            [self.session commitConfiguration];
        } else if (error) {
            NSLog(@"toggle carema failed, error = %@", error);
        }
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垃瞧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坪郭,更是在濱河造成了極大的恐慌个从,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嗦锐,居然都是意外死亡嫌松,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門奕污,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萎羔,“玉大人,你說我怎么就攤上這事碳默〖窒荩” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嘱根,是天一觀的道長髓废。 經(jīng)常有香客問我,道長儿子,這世上最難降的妖魔是什么瓦哎? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮柔逼,結(jié)果婚禮上蒋譬,老公的妹妹穿的比我還像新娘。我一直安慰自己愉适,他們只是感情好犯助,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著维咸,像睡著了一般剂买。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上癌蓖,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天瞬哼,我揣著相機(jī)與錄音,去河邊找鬼租副。 笑死坐慰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的用僧。 我是一名探鬼主播结胀,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼责循!你這毒婦竟也來了糟港?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤院仿,失蹤者是張志新(化名)和其女友劉穎桐经,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枣购,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年健芭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秀姐。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖若贮,靈堂內(nèi)的尸體忽然破棺而出省有,到底是詐尸還是另有隱情,我是刑警寧澤谴麦,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布蠢沿,位于F島的核電站,受9級(jí)特大地震影響匾效,放射性物質(zhì)發(fā)生泄漏舷蟀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一面哼、第九天 我趴在偏房一處隱蔽的房頂上張望野宜。 院中可真熱鬧,春花似錦魔策、人聲如沸匈子。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虎敦。三九已至,卻和暖如春政敢,著一層夾襖步出監(jiān)牢的瞬間其徙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工喷户, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唾那,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓摩骨,卻偏偏與公主長得像通贞,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恼五,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容