使用系統相機錄像,使用的AVFoundation框架。首先了解一下框架的使用。
一、錄製的相關類有:
1、AVCaptureSession
媒體(音、視頻)捕獲會話,負責把捕獲的音視頻數據輸出到輸出設備中。一個AVCaptureSession可以有多個輸入輸出。
2、AVCaptureDevice
輸入設備,包括麥克風、攝像頭,通過該對象可以設置物理設備的一些屬性(例如相機聚焦、白平衡等)。
3、AVCaptureDeviceInput
設備輸入數據管理對象,可以根據AVCaptureDevice創建對應AVCaptureDeviceInput對象,該對象將會被添加到AVCaptureSession中管理。
4、AVCaptureOutput
輸出數據管理對象,用於接收各類輸出數據,通常使用對應的子類AVCaptureAudioDataOutput、AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,該對象將會被添加到AVCaptureSession中管理。注意:前面幾個對象的輸出數據都是NSData類型,而AVCaptureFileOutput代表數據以文件形式輸出,類似的,AVCcaptureFileOutput也不會直接創建使用,通常會使用其子類:AVCaptureAudioFileOutput、AVCaptureMovieFileOutput。當把一個輸入或者輸出添加到AVCaptureSession之後AVCaptureSession就會在所有相符的輸入、輸出設備之間建立連接(AVCaptionConnection)。
5、AVCaptureVideoPreviewLayer
相機拍攝預覽圖層,是CALayer的子類,使用該對象可以實時查看拍照或視頻錄製效果,創建該對象需要指定對應的AVCaptureSession對象。
二、使用AVFoundation拍照和錄製視頻的一般步驟如下:
1、創建AVCaptureSession對象。
2、使用AVCaptureDevice的靜態方法獲得需要使用的設備,例如拍照和錄像就需要獲得攝像頭設備,錄音就要獲得麥克風設備。
3、利用輸入設備AVCaptureDevice初始化AVCaptureDeviceInput對象。
4、初始化輸出數據管理對象,如果要拍照就初始化AVCaptureStillImageOutput對象;如果拍攝視頻就初始化AVCaptureMovieFileOutput對象。
5、將數據輸入對象AVCaptureDeviceInput、數據輸出對象AVCaptureOutput添加到媒體會話管理對象AVCaptureSession中。
6、創建視頻預覽圖層AVCaptureVideoPreviewLayer並指定媒體會話,添加圖層到顯示容器中,調用AVCaptureSession的startRuning方法開始捕獲。
7、將捕獲的音頻或視頻數據輸出到指定文件。
介紹完錄製視頻,要介紹一下播放器的使用。使用的也是AVFoundation框架中的類是AVPlayer。
AVPlayer本身並不能顯示視頻,而且它也不像MPMoviePlayerController有一個view屬性。如果AVPlayer要顯示必須創建一個播放器層AVPlayerLayer用於展示,播放器層繼承於CALayer,有了AVPlayerLayer之添加到控制器視圖的layer中即可。要使用AVPlayer首先了解一下幾個常用的類:
1、AVAsset:主要用於獲取多媒體信息,是一個抽象類,不能直接使用。
2、AVURLAsset:AVAsset的子類,可以根據一個URL路徑創建一個包含媒體信息的AVURLAsset對象。
3、AVPlayerItem:一個媒體資源管理對象,管理者視頻的一些基本信息和狀態,一個AVPlayerItem對應着一個視頻資源。
簡單介紹AVFoundation框架之後,放上一個自己寫的小demo,類似於微信的視頻錄製。能保存到相冊。效果圖如下:
具體代碼如下:
ViewController.m中:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "UserDefine.h"
typedef NS_ENUM(NSInteger,VideoStatus){
VideoStatusEnded = 0,
VideoStatusStarted
};
@interface ViewController ()<AVCaptureFileOutputRecordingDelegate>
{ //拍攝視頻相關
AVCaptureSession * _captureSession;/**< 是一個會話對象,是設備音頻/視頻整個錄製期間的管理者. */
AVCaptureDevice *_videoDevice;/**< 視頻設備 */
AVCaptureDevice *_audioDevice;/**< 音頻設備 */
AVCaptureDeviceInput *_videoInput;/**< 視頻輸入 */
AVCaptureDeviceInput *_audioInput;/**< 音頻輸入 */
AVCaptureMovieFileOutput *_movieOutput;/**< 視頻輸出 */
AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer;/**< 預覽拍攝過程中的圖像 */
//播放相關
AVPlayer *_player;/**< 播放器對象 */
AVPlayerItem *_playItem;/**< 一個媒體資源管理對象,管理者視頻的一些基本信息和狀態,一個AVPlayerItem對應着一個視頻資源 */
AVPlayerLayer *_playerLayer;
BOOL _isPlaying;
}
@property (strong, nonatomic) UIView *recordingView;/**< */
@property (strong, nonatomic) UIButton *recordingButton;/**< 錄製按鈕 */
@property (strong, nonatomic) UILabel *timeLabel;/**< 倒計時時間 */
@property (strong, nonatomic) UIButton *playButton;/**< 播放按鈕 */
@property (nonatomic,assign) VideoStatus status;
@property (nonatomic,assign) BOOL canSave;
@property (nonatomic,strong) CADisplayLink *link;
@property (strong, nonatomic) NSURL *videoUrl;/**< 視頻URL */
@end
@implementation ViewController
static float CountdownTime = 15 * 60;//倒計時時間,時間爲15s,* 60 因爲CADisplayLink 1/60秒刷新一次
- (void)viewDidLoad {
[super viewDidLoad];
[self setUILayout];
[self getAuthorization];
//對視頻播放完進行監聽
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:)name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
//移除通知
[[NSNotificationCenter defaultCenter]removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
#pragma mark - **************** 界面佈局
-(void)setUILayout{
self.view.backgroundColor = RGBColor(245, 245, 245);
CGFloat jianGe = 20;
CGFloat btnH = 30;
CGFloat btnW = 120;
CGFloat lblH = 40;
CGFloat lblW = 120;
CGFloat boFangH = 160;
_recordingView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H/2)];
_recordingView.backgroundColor = [UIColor blackColor];
[self.view addSubview:_recordingView];
_timeLabel = [[UILabel alloc]initWithFrame:CGRectMake(self.view.center.x - lblW/2, _recordingView.frame.size.height + _recordingView.frame.origin.y + lblH, lblW, lblH)];
_timeLabel.hidden = NO;
_timeLabel.text = [NSString stringWithFormat:@"倒計時:%d秒",(int)(CountdownTime/60)];
_timeLabel.textAlignment = NSTextAlignmentCenter;
_timeLabel.font = [UIFont systemFontOfSize:18.0];
_timeLabel.textColor = RGBColor(217, 28, 26);
[self.view addSubview:_timeLabel];
_recordingButton = [UIButton buttonWithType:UIButtonTypeCustom];
_recordingButton.frame = CGRectMake(self.view.center.x - btnW/2, _timeLabel.frame.size.height + _timeLabel.frame.origin.y + jianGe, btnW, btnH);
[_recordingButton setTitle:@"開始錄製" forState:UIControlStateNormal];
[_recordingButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_recordingButton.backgroundColor = RGBColor(217, 28, 26);
_recordingButton.titleLabel.font = [UIFont fontWithName:@"STHeitiSC-Light" size:14.0];
_recordingButton.layer.cornerRadius = 3;
[_recordingButton addTarget:self action:@selector(recordButton:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_recordingButton];
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
_playButton.frame = CGRectMake(0, 0, boFangH, boFangH);
_playButton.center = _recordingView.center;
[_playButton setImage:[UIImage imageNamed:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
_playButton.hidden = YES;
[_playButton addTarget:self action:@selector(playButton:) forControlEvents:UIControlEventTouchUpInside];
[_recordingView addSubview:_playButton];
}
#pragma mark - **************** Button 方法
/**
* 點擊錄製
*
* @param sender
*/
-(void)recordButton:(UIButton *)sender{
if ([sender.titleLabel.text isEqualToString:@"開始錄製"]) {
[self startAnimation];
}else{
[self saveVideo:_videoUrl];
}
}
/**
* 點擊播放
*
* @param sender
*/
-(void)playButton:(UIButton *)sender{
[_player play];
_playButton.hidden = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - **************** 視頻錄製相關
#pragma mark -- 獲取授權
-(void)getAuthorization{
//此處獲取攝像頭授權
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
case AVAuthorizationStatusAuthorized://已授權可以使用
{
NSLog(@"授權成功!");
[self setupAVCaptureInfo];
}
break;
case AVAuthorizationStatusNotDetermined://未授權
{
//則再次請求授權
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {//授權成功
[self setupAVCaptureInfo];
return;
}else{
//授權失敗
return;
}
}];
}
break;
default: //用戶拒絕授權/未授權
break;
}
}
#pragma mark - 設置相關信息
-(void)setupAVCaptureInfo{
[self addSession];
//開始配置視頻的會話對象
[_captureSession beginConfiguration];
[self addVideo];
[self addAudio];
[self addPreviewLayer];
//提交配置
[_captureSession commitConfiguration];
//開啓會話----> 不等於開始錄製
[_captureSession startRunning];
}
/**
* 設置視頻的會話對象
*/
-(void)addSession{
_captureSession = [[AVCaptureSession alloc]init];
//設置視頻分辨率
//注意,這個地方設置的模式/分辨率大小將影響後面的拍攝質量
if ([_captureSession canSetSessionPreset:AVAssetExportPreset640x480]) {
[_captureSession setSessionPreset:AVAssetExportPreset640x480];
}
}
/**
* 設置視頻設備
*/
-(void)addVideo{
_videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];//AVCaptureDevicePositionBack -- 後攝像頭
[self addVideoInput];
[self addMovieOutput];
}
/**
* 設置視頻輸入對象
*/
-(void)addVideoInput{
NSError *videoError;
//視頻輸入對象
//根據輸入設備初始化輸入對象,用戶獲取輸入數據
_videoInput = [[AVCaptureDeviceInput alloc]initWithDevice:_videoDevice error:&videoError];
if (videoError) {
NSLog(@"-------取得攝像頭設備時出錯---%@",[videoError localizedDescription]);
return;
}
//將視頻輸入對象添加到會話(AVCaptureSession)中
if ([_captureSession canAddInput:_videoInput]) {
[_captureSession addInput:_videoInput];
}
}
/**
* 設置視頻輸出對象
*/
-(void)addMovieOutput{
//拍攝視頻輸出對象
//初始化輸出設備對象,用戶獲取輸出數據
_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([_captureSession canAddOutput:_movieOutput]) {
[_captureSession addOutput:_movieOutput];
//設置連接管理對象
AVCaptureConnection *captureConnection = [_movieOutput connectionWithMediaType:AVMediaTypeVideo];
//視頻穩定設置
if ([captureConnection isVideoStabilizationSupported]) {
captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
//視頻旋轉方向的設置
captureConnection.videoScaleAndCropFactor = captureConnection.videoMaxScaleAndCropFactor;
}
}
/**
* 設置音頻設備
*/
-(void)addAudio{
NSError *audioError;
//添加一個音頻設備
_audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
// 音頻輸入對象
_audioInput = [[AVCaptureDeviceInput alloc]initWithDevice:_audioDevice error:&audioError];
if (audioError) {
NSLog(@"取得錄音設備時出錯 ------ %@",audioError);
return;
}
//將音頻輸入對象添加到會話 (AVCaptureSession) 中
if ([_captureSession canAddInput:_audioInput]) {
[_captureSession addInput:_audioInput];
}
}
/**
* 設置預覽層
*/
-(void)addPreviewLayer{
[self.view layoutIfNeeded];
//通過會話(AVCaptureSession)創建預覽圖層
_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:_captureSession];
_captureVideoPreviewLayer.frame = self.view.layer.bounds;
//如果預覽圖層和視頻方向不一致,可以修改這個
_captureVideoPreviewLayer.connection.videoOrientation = [_movieOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation;
//設置captureVideoPreviewLayer在父視圖中的位置
_captureVideoPreviewLayer.position = CGPointMake(self.view.frame.size.width*0.5,self.recordingView.frame.size.height*0.5);
//顯示在視圖表面的圖層
CALayer *layer = self.recordingView.layer;
layer.masksToBounds = YES;
[self.view layoutIfNeeded];
[layer addSublayer:_captureVideoPreviewLayer];
}
-(void)startAnimation{
if (self.status == VideoStatusEnded) {
self.status = VideoStatusStarted;
[UIView animateWithDuration:0.5 animations:^{
} completion:^(BOOL finished) {
[self stopLink];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}];
}
}
-(void)stopAnimation{
if (self.status == VideoStatusStarted) {
self.status = VideoStatusEnded;
[self stopLink];
[self stopRecord];
_playButton.hidden = NO;
[UIView animateWithDuration:0.5 animations:^{
[_recordingButton setTitle:@"保存到相冊" forState:UIControlStateNormal];
} completion:^(BOOL finished) {
}];
}
}
-(CADisplayLink *)link{
if (!_link) {
_link = [CADisplayLink displayLinkWithTarget:self selector:@selector(refresh:)];
[self startRecord];
}
return _link;
}
-(void)stopLink{
_link.paused = YES;
[_link invalidate];
_link = nil;
}
-(void)refresh:(CADisplayLink *)link{
if (CountdownTime <= 0) {
CountdownTime = 15 * 60;
[self recordComplete];
[self stopAnimation];
_timeLabel.hidden = YES;
return;
}
CountdownTime -= 1;
NSLog(@"%f",CountdownTime);
_timeLabel.text = [NSString stringWithFormat:@"倒計時:%d秒",(int)(CountdownTime/60)];
}
/**
* 獲取錄製視頻的地址
*
* @return outPutFileURL
*/
- (NSURL *)outPutFileURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"outPut.mov"]];
}
- (void)startRecord
{
[_movieOutput startRecordingToOutputFileURL:[self outPutFileURL] recordingDelegate:self];
}
- (void)stopRecord
{
// 取消視頻拍攝
[_movieOutput stopRecording];
}
- (void)recordComplete
{
self.canSave = YES;
}
//這個在完全退出小視頻時調用
- (void)quit
{
[_captureSession stopRunning];
}
- (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(@"---- 錄製結束 ---%@-%@ ",outputFileURL,captureOutput.outputFileURL);
if (outputFileURL.absoluteString.length == 0 && captureOutput.outputFileURL.absoluteString.length == 0 ) {
return;
}
if (self.canSave) {
_videoUrl = outputFileURL;
self.canSave = NO;
[self creatPlayView];
}
}
#pragma mark - 獲取攝像頭-->前/後
-(AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
AVCaptureDevice *captureDevice = devices.firstObject;
for (AVCaptureDevice *device in devices) {
if (device.position == position) {
captureDevice = device;
break;
}
}
return captureDevice;
}
#pragma mark - **************** 播放相關
-(void)creatPlayView{
NSLog(@"%@",_videoUrl);
[_captureVideoPreviewLayer removeFromSuperlayer];
[self.view layoutIfNeeded];
_playItem = [AVPlayerItem playerItemWithURL:self.videoUrl];
_player = [AVPlayer playerWithPlayerItem:_playItem];
_playerLayer =[AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.frame = _recordingView.frame;
_playerLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//視頻填充模式
_playerLayer.position = CGPointMake(self.view.frame.size.width*0.5,self.recordingView.frame.size.height*0.5);
CALayer *layer = self.recordingView.layer;
layer.masksToBounds = true;
[self.view layoutIfNeeded];
[layer addSublayer:_playerLayer];
[self.recordingView bringSubviewToFront:_playButton];
}
#pragma mark - 視頻播放通知回調
-(void)playbackFinished:(NSNotification *)notification
{
[_player seekToTime:CMTimeMake(0, 1)];
_playButton.hidden = NO;
}
#pragma mark - **************** 壓縮保存
- (NSURL *)compressedURL
{
return [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"compressed.mp4"]]];
}
- (CGFloat)fileSize:(NSURL *)path
{
return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00;
}
// 壓縮視頻
-(void)saveVideoWithUrl:(NSURL *)url
{
NSLog(@"開始壓縮,壓縮前大小 %f MB",[self fileSize:url]);
AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPreset640x480];
exportSession.outputURL = [self compressedURL];
//優化網絡
exportSession.shouldOptimizeForNetworkUse = true;
//轉換後的格式
exportSession.outputFileType = AVFileTypeMPEG4;
//異步導出
[exportSession exportAsynchronouslyWithCompletionHandler:^{
// 如果導出的狀態爲完成
if ([exportSession status] == AVAssetExportSessionStatusCompleted) {
NSLog(@"壓縮完畢,壓縮後大小 %f MB",[self fileSize:[self compressedURL]]);
[self saveVideo:[self compressedURL]];
}else{
NSLog(@"當前壓縮進度:%f",exportSession.progress);
}
}];
}
}
- (void)saveVideo:(NSURL *)outputFileURL
{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"保存視頻失敗:%@",error);
} else {
NSLog(@"保存視頻到相冊成功");
}
}];
}
@end
附上demo下載地址:http://download.csdn.net/detail/qq_34195670/9589592
github下載地址:https://github.com/goingmyway1/RecordingVideoDemo
以上如有錯誤,請留言指正,謝謝!