调用系统相机录像,压缩保存到相册(附仿微信视频录制demo)

使用系统相机录像,使用的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

以上如有错误,请留言指正,谢谢!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值