iOS視頻錄制

#import "MovieViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);

@interface MovieViewController ``()<AVCaptureFileOutputRecordingDelegate>

@property (strong,nonatomic) AVCaptureSession *captureSession;//負(fù)責(zé)輸入和輸出設(shè)備之間的數(shù)據(jù)傳遞
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負(fù)責(zé)從AVCaptureDevice獲得輸入數(shù)據(jù)
@property (strong,nonatomic) AVCaptureMovieFileOutput *captureMovieFileOutput;//視頻輸出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機(jī)拍攝預(yù)覽圖層
@property (assign,nonatomic) BOOL enableRotation;//是否允許旋轉(zhuǎn)(注意在視頻錄制過(guò)程中禁止屏幕旋轉(zhuǎn))
@property (assign,nonatomic) CGRect *lastBounds;//旋轉(zhuǎn)的前大小
@property (assign,nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;//后臺(tái)任務(wù)標(biāo)識(shí)
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標(biāo)

@end

@implementation MovieViewController

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

-(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //初始化會(huì)話 _captureSession=[[AVCaptureSession alloc]init]; if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設(shè)置分辨率 _captureSession.sessionPreset=AVCaptureSessionPreset1280x720; }
//獲得輸入設(shè)備
AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭 if (!captureDevice) { NSLog(@"取得后置攝像頭時(shí)出現(xiàn)問(wèn)題."); return; }
//添加一個(gè)音頻輸入設(shè)備
AVCaptureDevice *audioCaptureDevice=[[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];

NSError *error=nil;
//根據(jù)輸入設(shè)備初始化設(shè)備輸入對(duì)象起趾,用于獲得輸入數(shù)據(jù)
_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
    NSLog(@"取得設(shè)備輸入對(duì)象時(shí)出錯(cuò),錯(cuò)誤原因:%@",error.localizedDescription);
    return;
}
AVCaptureDeviceInput *audioCaptureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:audioCaptureDevice error:&error];
if (error) {
    NSLog(@"取得設(shè)備輸入對(duì)象時(shí)出錯(cuò),錯(cuò)誤原因:%@",error.localizedDescription);
    return;
}
//初始化設(shè)備輸出對(duì)象,用于獲得輸出數(shù)據(jù)
_captureMovieFileOutput=[[AVCaptureMovieFileOutput alloc]init];

//將設(shè)備輸入添加到會(huì)話中
if ([_captureSession canAddInput:_captureDeviceInput]) {
    [_captureSession addInput:_captureDeviceInput];
    [_captureSession addInput:audioCaptureDeviceInput];
    AVCaptureConnection *captureConnection=[_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
    if ([captureConnection isVideoStabilizationSupported ]) {
        captureConnection.preferredVideoStabilizationMode=AVCaptureVideoStabilizationModeAuto;
    }
}

//將設(shè)備輸出添加到會(huì)話中
if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
    [_captureSession addOutput:_captureMovieFileOutput];
}

//創(chuàng)建視頻預(yù)覽層,用于實(shí)時(shí)展示攝像頭狀態(tài)
_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];

CALayer *layer=self.viewContainer.layer;
layer.masksToBounds=YES;

_captureVideoPreviewLayer.frame=layer.bounds;
_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
//將視頻預(yù)覽層添加到界面中
//[layer addSublayer:_captureVideoPreviewLayer];
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];

_enableRotation=YES;
[self addNotificationToCaptureDevice:captureDevice];
[self addGenstureRecognizer];

}

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

-(void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [self.captureSession stopRunning]; }
-(BOOL)shouldAutorotate{ return self.enableRotation; }

////屏幕旋轉(zhuǎn)時(shí)調(diào)整視頻預(yù)覽圖層的方向
//-(void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
// [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
//// NSLog(@"%i,%i",newCollection.verticalSizeClass,newCollection.horizontalSizeClass);
// UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
// NSLog(@"%i",orientation);
// AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection];
// captureConnection.videoOrientation=orientation;
//
//}
//屏幕旋轉(zhuǎn)時(shí)調(diào)整視頻預(yù)覽圖層的方向
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection];
captureConnection.videoOrientation=(AVCaptureVideoOrientation)toInterfaceOrientation;
}
//旋轉(zhuǎn)后重新設(shè)置大小
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ _captureVideoPreviewLayer.frame=self.viewContainer.bounds; }

-(void)dealloc{ [self removeNotification]; }
#pragma mark - UI方法
#pragma mark 視頻錄制
- (IBAction)takeButtonClick:(UIButton *)sender { //根據(jù)設(shè)備輸出獲得連接 AVCaptureConnection *captureConnection=[self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo]; //根據(jù)連接取得設(shè)備輸出的數(shù)據(jù) if (![self.captureMovieFileOutput isRecording]) { self.enableRotation=NO; //如果支持多任務(wù)則則開始多任務(wù) if ([[UIDevice currentDevice] isMultitaskingSupported]) { self.backgroundTaskIdentifier=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; } //預(yù)覽圖層和視頻方向保持一致 captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation; NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"]; NSLog(@"save path is :%@",outputFielPath); NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath]; [self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self]; } else{ [self.captureMovieFileOutput stopRecording];//停止錄制 } }
#pragma mark 切換前后攝像頭
- (IBAction)toggleButtonClick:(UIButton *)sender { AVCaptureDevice *currentDevice=[self.captureDeviceInput device]; AVCaptureDevicePosition currentPosition=[currentDevice position]; [self removeNotificationFromCaptureDevice:currentDevice]; AVCaptureDevice *toChangeDevice; AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront; if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) { toChangePosition=AVCaptureDevicePositionBack; } toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition]; [self addNotificationToCaptureDevice:toChangeDevice]; //獲得要調(diào)整的設(shè)備輸入對(duì)象 AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];

//改變會(huì)話的配置前一定要先開啟配置匿情,配置完成后提交配置改變 [self.captureSession beginConfiguration]; //移除原有輸入對(duì)象 [self.captureSession removeInput:self.captureDeviceInput]; //添加新的輸入對(duì)象 if ([self.captureSession canAddInput:toChangeDeviceInput]) { [self.captureSession addInput:toChangeDeviceInput]; self.captureDeviceInput=toChangeDeviceInput; }
//提交會(huì)話配置 [self.captureSession commitConfiguration];

}

#pragma mark - 視頻輸出代理
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections{ NSLog(@"開始錄制..."); }
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{ NSLog(@"視頻錄制完成."); //視頻錄入完成之后在后臺(tái)將視頻存儲(chǔ)到相簿 self.enableRotation=YES; UIBackgroundTaskIdentifier lastBackgroundTaskIdentifier=self.backgroundTaskIdentifier; self.backgroundTaskIdentifier=UIBackgroundTaskInvalid; ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init]; [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) { if (error) { NSLog(@"保存視頻到相簿過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription); } if (lastBackgroundTaskIdentifier!=UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:lastBackgroundTaskIdentifier]; } NSLog(@"成功保存視頻到相簿."); }];

}

#pragma mark - 通知
/**
* 給輸入設(shè)備添加通知 */
-(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{ //注意添加區(qū)域改變捕獲通知必須首先設(shè)置設(shè)備允許捕獲 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { captureDevice.subjectAreaChangeMonitoringEnabled=YES; }];
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; //捕獲區(qū)域發(fā)生改變 [notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; } -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{ NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; }
/**
* 移除所有通知 */
-(void)removeNotification{ NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self]; }

-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{ NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//會(huì)話出錯(cuò) [notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession]; }

/**
* 設(shè)備連接成功
*
* @param notification 通知對(duì)象
*/
-(void)deviceConnected:(NSNotification *)notification{ NSLog(@"設(shè)備已連接..."); }
/**

  • 設(shè)備連接斷開
  • @param notification 通知對(duì)象
    /
    -(void)deviceDisconnected:(NSNotification )notification{
    NSLog(@"設(shè)備已斷開.");
    }
    /
  • 捕獲區(qū)域改變
  • @param notification 通知對(duì)象
    */
    -(void)areaChange:(NSNotification *)notification{
    NSLog(@"捕獲區(qū)域改變...");
    }

/**

  • 會(huì)話出錯(cuò)
  • @param notification 通知對(duì)象
    */
    -(void)sessionRuntimeError:(NSNotification *)notification{
    NSLog(@"會(huì)話發(fā)生錯(cuò)誤.");
    }

pragma mark - 私有方法

/**

  • 取得指定位置的攝像頭
  • @param position 攝像頭位置
  • @return 攝像頭設(shè)備
    */
    -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
    NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
    if ([camera position]==position) {
    return camera;
    }
    }
    return nil;
    }

/**

  • 改變?cè)O(shè)備屬性的統(tǒng)一操作方法
  • @param propertyChange 屬性改變操作
    */
    -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
    AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
    NSError *error;
    //注意改變?cè)O(shè)備屬性前一定要首先調(diào)用lockForConfiguration:調(diào)用完之后使用unlockForConfiguration方法解鎖
    if ([captureDevice lockForConfiguration:&error]) {
    propertyChange(captureDevice);
    [captureDevice unlockForConfiguration];
    }else{
    NSLog(@"設(shè)置設(shè)備屬性過(guò)程發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
    }
    }

/**

  • 設(shè)置閃光燈模式
  • @param flashMode 閃光燈模式
    /
    -(void)setFlashMode:(AVCaptureFlashMode )flashMode{
    [self changeDeviceProperty:^(AVCaptureDevice captureDevice) {
    if ([captureDevice isFlashModeSupported:flashMode]) {
    [captureDevice setFlashMode:flashMode];
    }
    }];
    }
    /
  • 設(shè)置聚焦模式
  • @param focusMode 聚焦模式
    /
    -(void)setFocusMode:(AVCaptureFocusMode )focusMode{
    [self changeDeviceProperty:^(AVCaptureDevice captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
    [captureDevice setFocusMode:focusMode];
    }
    }];
    }
    /
  • 設(shè)置曝光模式
  • @param exposureMode 曝光模式
    /
    -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
    [self changeDeviceProperty:^(AVCaptureDevice captureDevice) {
    if ([captureDevice isExposureModeSupported:exposureMode]) {
    [captureDevice setExposureMode:exposureMode];
    }
    }];
    }
    /
  • 設(shè)置聚焦點(diǎn)
  • @param point 聚焦點(diǎn)
    */
    -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
    [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
    [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
    }
    if ([captureDevice isFocusPointOfInterestSupported]) {
    [captureDevice setFocusPointOfInterest:point];
    }
    if ([captureDevice isExposureModeSupported:exposureMode]) {
    [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
    }
    if ([captureDevice isExposurePointOfInterestSupported]) {
    [captureDevice setExposurePointOfInterest:point];
    }
    }];
    }

/**

  • 添加點(diǎn)按手勢(shì)渔呵,點(diǎn)按時(shí)聚焦
    */
    -(void)addGenstureRecognizer{
    UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
    [self.viewContainer addGestureRecognizer:tapGesture];
    }
    -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
    CGPoint point= [tapGesture locationInView:self.viewContainer];
    //將UI坐標(biāo)轉(zhuǎn)化為攝像頭坐標(biāo)
    CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
    [self setFocusCursorWithPoint:point];
    [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
    }

/**

  • 設(shè)置聚焦光標(biāo)位置

  • @param point 光標(biāo)位置
    */
    -(void)setFocusCursorWithPoint:(CGPoint)point{
    self.focusCursor.center=point;
    self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
    self.focusCursor.alpha=1.0;
    [UIView animateWithDuration:1.0 animations:^{
    self.focusCursor.transform=CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
    self.focusCursor.alpha=0;

    }];
    }

@end
`

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砍鸠,隨后出現(xiàn)的幾起案子扩氢,更是在濱河造成了極大的恐慌,老刑警劉巖爷辱,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件录豺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡饭弓,警方通過(guò)查閱死者的電腦和手機(jī)双饥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)弟断,“玉大人咏花,你說(shuō)我怎么就攤上這事》蛏ぃ” “怎么了迟螺?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舍咖。 經(jīng)常有香客問(wèn)我矩父,道長(zhǎng),這世上最難降的妖魔是什么排霉? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任窍株,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘球订。我一直安慰自己后裸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布冒滩。 她就那樣靜靜地躺著微驶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪开睡。 梳的紋絲不亂的頭發(fā)上因苹,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音篇恒,去河邊找鬼扶檐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胁艰,可吹牛的內(nèi)容都是我干的款筑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼腾么,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奈梳!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哮翘,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颈嚼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后饭寺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阻课,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年艰匙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了限煞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡员凝,死狀恐怖署驻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情健霹,我是刑警寧澤旺上,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站糖埋,受9級(jí)特大地震影響宣吱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞳别,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一征候、第九天 我趴在偏房一處隱蔽的房頂上張望杭攻。 院中可真熱鬧,春花似錦疤坝、人聲如沸兆解。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锅睛。三九已至,卻和暖如春历谍,著一層夾襖步出監(jiān)牢的瞬間衣撬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工扮饶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乍构。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓甜无,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哥遮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岂丘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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