美文网首页iOS Developer收藏ios
短视频录制-仿映客

短视频录制-仿映客

作者: wbtuxi | 来源:发表于2017-03-11 13:56 被阅读118次
    项目中集成了短视频录制,UI和映客很像,在这里贴下代码搬运流程[迷之微笑]
    录制.png 播放.png
    基于系统提供框架

    #import <AVFoundation/AVFoundation.h>

    初始化必要属性
    /**
     *  AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
     */
    @property (nonatomic, strong) AVCaptureSession* session;
    /**
     *  视频输入设备
     */
    @property (nonatomic, strong) AVCaptureDeviceInput* videoInput;
    /**
     *  声音输入
     */
    @property (nonatomic, strong) AVCaptureDeviceInput* audioInput;
    /**
     *  视频输出流
     */
    @property(nonatomic,strong)AVCaptureMovieFileOutput *movieFileOutput;
    /**
     *  预览图层
     */
    @property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer;
    
    #pragma mark - private mehtod
    - (void)initAVCaptureSession{
        
        self.session = [[AVCaptureSession alloc] init];
        
        //这里根据需要设置  可以设置4K
        self.session.sessionPreset = AVCaptureSessionPreset1280x720;
        
        NSError *error;
        
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
        [device lockForConfiguration:nil];
        //设置闪光灯为自动
        [device setFlashMode:AVCaptureFlashModeAuto];
        [device unlockForConfiguration];
        
        self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
        
        self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:nil];
        
        if (error) {
            DLog(@"error%@",error);
        }
        
        self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
        
        if ([self.session canAddInput:self.videoInput]) {
            [self.session addInput:self.videoInput];
        }
        
        if ([self.session canAddInput:self.audioInput]) {
            
            [self.session addInput:self.audioInput];
        }
        
        if ([self.session canAddOutput:self.movieFileOutput]) {
            
            [self.session addOutput:self.movieFileOutput];
        }
        
        [self basicViewSet];
    }
    
    开始录制前先要获取下权限
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
        if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
        {
            
            return;
        }
        
        //判断用户是否允许访问麦克风权限
        authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
        if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
        {
            //无权限
            [SVProgressHUD showWithStatus:@"未授予麦克风权限"];
            return;
        }
    

    到了重点了-开始录制

    - (void)startSession{
        
        if (![self.session isRunning]) {
            
            [self.session startRunning];
        }
    }
    
    AVCaptureConnection *movieConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        AVCaptureVideoOrientation avcaptureOrientation = AVCaptureVideoOrientationPortrait;
        [movieConnection setVideoOrientation:avcaptureOrientation];
        [movieConnection setVideoScaleAndCropFactor:1.0];
        //    保存按URL
        self.url = [[RAFileManager defaultManager] filePathUrlWithUrl:[self getVideoSaveFilePathString]];
        
        [self.movieFileOutput startRecordingToOutputFileURL:self.url recordingDelegate:self];
    
    按路径保存的方法是自己写的
    - (NSURL*)filePathUrlWithUrl:(NSString*)url
    {
        return [NSURL fileURLWithPath:[self filePathWithUrl:url]];
    }
    
    呵呵 其实是调用的系统方法

    到这里录制的部分已经完了,视频已经按路径保存了

    如果录制完成后就做些操作的话可以用这个
    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{}
    
    这个代理

    <AVCaptureFileOutputRecordingDelegate>
    解释说是 调用此方法时,文件输出已经写完所有数据到一个文件的记录了。。。来自某道翻译

    怪我咯.jpg

    就先这样吧

    除了视频的压缩上传、反复播放、圆环动画就没有了呼呼呼

    有机会再接着搬。。。

    老板要加短拍美颜-_-!,我就把我原生的代码都一块搬过来,做个纪念

    #import "XNLMakVideoController.h"

    #import <AVFoundation/AVFoundation.h>
    #import <AssetsLibrary/AssetsLibrary.h>
    #import "XNLVideoPlayerController.h"
    #import "RAFileManager.h"
    #define TIMER_INTERVAL 0.05 //定时器间隔
    #define VIDEO_RECORDER_MAX_TIME 60 //视频最大时长 (单位/秒)
    #define VIDEO_RECORDER_MIN_TIME 30 //最短视频时长 (单位/秒)

    @interface XNLMakVideoController ()<AVCaptureFileOutputRecordingDelegate>
    
    @property (nonatomic) dispatch_queue_t sessionQueue;
    /**
     *  AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
     */
    @property (nonatomic, strong) AVCaptureSession* session;
    /**
     *  视频输入设备
     */
    @property (nonatomic, strong) AVCaptureDeviceInput* videoInput;
    /**
     *  声音输入
     */
    @property (nonatomic, strong) AVCaptureDeviceInput* audioInput;
    /**
     *  视频输出流
     */
    @property(nonatomic,strong)AVCaptureMovieFileOutput *movieFileOutput;
    /**
     *  预览图层
     */
    @property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer;
    /**
     *  记录录制时间
     */
    @property (nonatomic, strong) NSTimer* timer;
    
    /**
     录制完成的视频路径
     */
    @property (nonatomic,strong)NSURL *url;
    
    /**
     开始View
     */
    @property (nonatomic,strong) UIView *begView;
    /**
     圆环的底环及填充环
     */
    @property (strong, nonatomic)CAShapeLayer *ovalSShapeLayer;
    @property (strong, nonatomic)CAShapeLayer *redShapeLayer;
    @property (nonatomic,strong)UIView *centerCircle;// 中心圆
    
    @end
    
    @implementation XNLMakVideoController{
        //时间长度
        CGFloat timeLength;
        
    }
    
    - (void)viewWillAppear:(BOOL)animated{
        
        [super viewWillAppear:YES];
        
        [self startSession];
        
        //开启定时器
        [self.timer setFireDate:[NSDate distantPast]];
    }
    
    //页面消失,进入后台不显示该页面,关闭定时器
    -(void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        //关闭定时器
        [self.timer setFireDate:[NSDate distantFuture]];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        [self initAVCaptureSession];
    
    }
    
    #pragma mark - private mehtod
    - (void)initAVCaptureSession{
        
        self.session = [[AVCaptureSession alloc] init];
        
        //这里根据需要设置  可以设置4K
        
        if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率
            self.session.sessionPreset = AVCaptureSessionPreset1280x720;
        }
        
        NSError *error;
        
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
        [device lockForConfiguration:nil];
        //设置闪光灯为自动
        [device setFlashMode:AVCaptureFlashModeAuto];
        [device unlockForConfiguration];
        
        self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
        
        self.audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:nil];
        
        if (error) {
            DLog(@"error%@",error);
        }
        
        self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
        
        //它的默认值就是10秒。将这个值禁用后就好了--- 坑我不轻-_-!
        self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid;
    
        
        if ([self.session canAddInput:self.videoInput]) {
            [self.session addInput:self.videoInput];
        }
        
        if ([self.session canAddInput:self.audioInput]) {
            
            [self.session addInput:self.audioInput];
        }
        
        if ([self.session canAddOutput:self.movieFileOutput]) {
            
            [self.session addOutput:self.movieFileOutput];
        }
        
        
        [self basicViewSet];
    }
    
    
    - (void)startSession{
        
        if (![self.session isRunning]) {
            
            [self.session startRunning];
        }
    }
    
    - (void)stopSession{
        
        if ([self.session isRunning]) {
            
            [self.session stopRunning];
        }
    }
    
    #pragma mark - 视频输出
    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{
        
        if (CMTimeGetSeconds(captureOutput.recordedDuration) < VIDEO_RECORDER_MIN_TIME) {
            
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"时间少于30s,不予保存"
                                                            message:nil
                                                           delegate:self
                                                  cancelButtonTitle:@"确定"
                                                  otherButtonTitles:nil, nil];
            [alert show];
            return;
        }
        WS(weakSelf);
        XNLVideoPlayerController *playerVC = [XNLVideoPlayerController new];
        playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
        playerVC.url = self.url;
        playerVC.videoURL = ^(){
    //        圆环重置
            weakSelf.redShapeLayer.strokeEnd = 0;
            weakSelf.centerCircle.backgroundColor = kRGBA(255, 255, 255, 0.8);
            // 删除视频
            [[RAFileManager defaultManager] removeFileWithUrl:weakSelf.url block:^(BOOL success) {
                DLog(@"----删除视频-----success-----%d",success);
            }];
        };
        [self presentViewController:playerVC animated:NO completion:^{}];
    }
    
    #pragma mark -- 定时器设置
    - (void)timerFired{
        
        timeLength = 0;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(timerRecord) userInfo:nil repeats:YES];
    }
    - (void)timerStop{
        
        if ([self.timer isValid]) {
            
            [self.timer invalidate];
            self.timer = nil;
        }
    }
    
    #pragma mark - response method 录制按钮点击触发
    - (void)takeVideoButtonPress:(UILongPressGestureRecognizer *)sender {
        
        AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
        if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
        {
            [SMGlobalMethod showViewCenter:kKeyWindow.center message:@"未授予摄像头权限"];
            return;
        }
        
        //判断用户是否允许访问麦克风权限
        authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
        if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied)
        {
            //无权限
            [SVProgressHUD showWithStatus:@"未授予麦克风权限"];
            return;
        }
        
        switch (sender.state) {
            case UIGestureRecognizerStateBegan:
                [self startVideoRecorder];
                break;
            case UIGestureRecognizerStateCancelled:
                [self stopVideoRecorder];
                break;
            case UIGestureRecognizerStateEnded:
                [self stopVideoRecorder];
                break;
            case UIGestureRecognizerStateFailed:
                [self stopVideoRecorder];
                break;
            default:
                break;
        }
        
    }
    
    #pragma 视频名以当前日期为名
    - (NSString*)getVideoSaveFilePathString
    {
        NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"yyyyMMddHHmmss";
        NSString* nowTimeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow:0]];
        
        return nowTimeStr;
    }
    
    #pragma mark 开始录制和结束录制
    - (void)startVideoRecorder{
       
        
        AVCaptureConnection *movieConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
        AVCaptureVideoOrientation avcaptureOrientation = AVCaptureVideoOrientationPortrait;
        [movieConnection setVideoOrientation:avcaptureOrientation];
        [movieConnection setVideoScaleAndCropFactor:1.0];
        //    保存按URL
        self.url = [[RAFileManager defaultManager] filePathUrlWithUrl:[self getVideoSaveFilePathString]];
        
        [self.movieFileOutput startRecordingToOutputFileURL:self.url recordingDelegate:self];
        
        
        // 圆环弹性动画
        [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:15 options:UIViewAnimationOptionLayoutSubviews animations:^{
            self.begView.transform = CGAffineTransformScale(self.begView.transform, 1.2, 1.2);
        } completion:^(BOOL finished) {
            
        }];
        
        self.redShapeLayer.strokeEnd = 0;
        
        [self timerFired];
        
    }
    // 录制结束后的方法
    - (void)stopVideoRecorder{
        
        [self.movieFileOutput stopRecording];
        
        [self timerStop];
        
        self.begView.transform = CGAffineTransformIdentity;
        //strokeStart为0时是从3点钟方向开始,故将其旋转270度从12点钟方向开始
        self.begView.transform = CGAffineTransformMakeRotation((M_PI * 2) / 4 * 3);
    
    }
    
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    // 定时器调用方法
    - (void)timerRecord{
        
        timeLength += (TIMER_INTERVAL/VIDEO_RECORDER_MAX_TIME);
        
        self.redShapeLayer.strokeEnd = timeLength;
        
        self.centerCircle.backgroundColor = kRGBA(255, 255*(1-timeLength), 255*(1-timeLength), 0.8);
    
        if (timeLength >= 1.0) {
            
            [self stopVideoRecorder];
            
            [self timerStop];
        }
    }
    
    
    #pragma mark -- 前后摄像头切换
    - (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition) position {
    
        NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    
        for (AVCaptureDevice *device in devices) {
    
            if ([device position] == position) {
                return device;
            }
        }
        return nil;
    }
    - (void)swapFrontAndBackCameras {
        // Assume the session is already running
        NSArray *inputs = self.session.inputs;
        for ( AVCaptureDeviceInput *input in inputs ) {
            AVCaptureDevice *device = input.device;
            if ( [device hasMediaType:AVMediaTypeVideo] ) {
                AVCaptureDevicePosition position = device.position;
                AVCaptureDevice *newCamera = nil;
                AVCaptureDeviceInput *newInput = nil;
                
                if (position == AVCaptureDevicePositionFront)
                    newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
                else
                    newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
                newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
                
                // beginConfiguration ensures that pending changes are not applied immediately
                [self.session beginConfiguration];
                
                [self.session removeInput:input];
                [self.session addInput:newInput];
                
                // Changes take effect once the outermost commitConfiguration is invoked.
                [self.session commitConfiguration];
                break;
            }
        } 
    }
    #pragma  mark -- 关闭
    - (void)shutDownClick
    {
        [self stopSession];
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    #pragma mark -- 几个控件的布局
    - (void)basicViewSet
    {
        //初始化预览图层
        self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
        //    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
        self.previewLayer.frame = CGRectMake(0, 0,kScreenWidth, kScreenHeight);
        
        [self.view.layer insertSublayer:self.previewLayer atIndex:0];
        
    //    圆环、圆的父View
        self.begView = [[UIView alloc]initWithFrame:CGRectMake(kScreenWidth/2-48*kScreenW, kScreenHeight-130*kScreenH, 96*kScreenW, 96*kScreenW)];
        [self.view addSubview:self.begView];
        
        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(takeVideoButtonPress:)];
        [self.begView addGestureRecognizer:longPress];
    //    圆环
        [self creatLayer];
    //    摄像头切换
        UIButton *fan = [UIButton buttonWithType:UIButtonTypeCustom];
        [fan setImage:kGetImage(@"shortvideo_icon_down_switch") forState:UIControlStateNormal];
        [self.view addSubview:fan];
        [fan mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.mas_equalTo(self.view).mas_offset(kScreenWidth/2 - 40*kScreenW);
            make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(-20*kScreenH);
            make.size.mas_equalTo(CGSizeMake(60*kScreenW, 60*kScreenW));
        }];
        [fan addTarget:self action:@selector(swapFrontAndBackCameras) forControlEvents:UIControlEventTouchUpInside];
        
    //    关闭
        UIButton *shutDown = [UIButton buttonWithType:UIButtonTypeCustom];
        [shutDown setImage:kGetImage(@"XNLlive_yemian5_close") forState:UIControlStateNormal];
        [self.view addSubview:shutDown];
        [shutDown mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.mas_equalTo(self.view).mas_offset(kScreenWidth/2 - 40*kScreenW);
            make.top.mas_equalTo(self.view.mas_top).mas_offset(20*kScreenW);
            make.size.mas_equalTo(CGSizeMake(50*kScreenW, 50*kScreenW));
        }];
        [shutDown addTarget:self action:@selector(shutDownClick) forControlEvents:UIControlEventTouchUpInside];
    }
    
    // 圆环的设置
    -(void)creatLayer
    {
        //添加底层白色圆环
        self.ovalSShapeLayer = [[CAShapeLayer alloc]init];
        //圆环颜色
        self.ovalSShapeLayer.strokeColor = DEFAULT_BACKGROUND_COLOR.CGColor;
        //内部填充颜色
        self.ovalSShapeLayer.fillColor = [UIColor clearColor].CGColor;
        //线宽
        self.ovalSShapeLayer.lineWidth = 6*kScreenW;
        //半径
        CGFloat ovalRadius = self.begView.frame.size.height/2;
        self.ovalSShapeLayer.path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.begView.frame.size.width/2 - ovalRadius, self.begView.frame.size.height/2 - ovalRadius, ovalRadius * 2, ovalRadius * 2)].CGPath;
        self.ovalSShapeLayer.lineCap = kCALineCapRound;
        [self.begView.layer addSublayer:self.ovalSShapeLayer];
        
        //添加填充圆环
        self.redShapeLayer = [[CAShapeLayer alloc]init];
        self.redShapeLayer.strokeColor = [UIColor redColor].CGColor;
        self.redShapeLayer.fillColor = [UIColor clearColor].CGColor;
        self.redShapeLayer.lineWidth = 6*kScreenW;
        self.redShapeLayer.cornerRadius = 3.0*kScreenW;
        self.redShapeLayer.lineCap = kCALineCapRound;  
        
        self.redShapeLayer.path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.begView.frame.size.width/2 - ovalRadius, self.begView.frame.size.height/2 - ovalRadius, ovalRadius * 2, ovalRadius * 2)].CGPath;
        self.redShapeLayer.strokeStart = 0;
        self.redShapeLayer.strokeEnd = 0;
        
        [self.begView.layer addSublayer:self.redShapeLayer];
        
        // 中心圆
        self.centerCircle = [UIView new];
        self.centerCircle.backgroundColor = kRGBA(255, 255, 255, 0.8);
        self.centerCircle.layer.cornerRadius = 40*kScreenW;
        self.centerCircle.clipsToBounds = YES;
        [self.begView addSubview:self.centerCircle];
        [self.centerCircle mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.mas_equalTo(self.begView);
            make.size.mas_equalTo(CGSizeMake(80*kScreenW, 80*kScreenW));
        }];
        
        self.begView.transform = CGAffineTransformMakeRotation((M_PI * 2) / 4 * 3);
    }
    
    其中应该有用到一些大神的代码,像RAFileManager.h,如有不对请联系我

    相关文章

      网友评论

        本文标题:短视频录制-仿映客

        本文链接:https://www.haomeiwen.com/subject/ntmowttx.html