iOS短视频:基于GPUIMage的短视频录制(GPUImage自定义相机)

主要涉及到的GPUIMage的类

  • GPUImageVideoCamera:录制视频,采集数据使用到的类,GPUImage中一种是GPUImageStillCamera,另一种为GPUImageVideoCamera.正如其命名,如果只是拍照使用前者.录制视频使用后者.
  • GPUImageView:用于显示视频的GPUImageView
  • GPUImageBeautifyFilter:继承自GPUImageFilterGroup,涉及使用到的类有GPUImageBilateralFilter(磨皮:本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊。),GPUImageCannyEdgeDetectionFilter(边缘检测算法,更强烈的黑白对比度),GPUImageCombinationFilter(组合滤镜),GPUImageHSBFilter(线性亮度调节)。
  • GPUImageMovieWriter:用于保存滤镜处理过的视频

视频录制整体原理

基于GPUImage录制原理

  • 首先通过GPUImageVideoCamera采集音视频数据
  • 视频部分:经过filter滤镜的视频帧分两步,一步用于在屏幕预览GPUImageView上显示,另一步用于写入GPUImageMovieWriter。
  • 音频部分:从GPUImageVideoCamera分离的音频直接写入GPUImageMovieWriter。另外,如果需要对音频进行混响、变声等处理,可以从这个节点分支处理写入。

视频录制具体实现流程

初始化相机

    //AVCaptureSessionPreset1280x720 相机分辨率
    //AVCaptureDevicePositionBack 后置摄像头
    //AVCaptureDevicePositionFront 前置摄像头
    //GPUImageVideoCamera用于录制视频,采集数据
    videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 cameraPosition:AVCaptureDevicePositionBack];
    //Alan 为了在AVCaptureDevice上设置硬件属性,比如focusMode和exposureMode,客户端必须首先获取设备上的锁。
    if ([videoCamera.inputCamera lockForConfiguration:nil]) {
        //自动对焦
        if ([videoCamera.inputCamera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
            [videoCamera.inputCamera setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
        }
        //自动曝光
        if ([videoCamera.inputCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
            [videoCamera.inputCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
        }
        //自动白平衡
        if ([videoCamera.inputCamera isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
            [videoCamera.inputCamera setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
        } 
        //Alan  解锁设备,提交配置
        [videoCamera.inputCamera unlockForConfiguration];
    }
     _position = CameraManagerDevicePositionBack;
    //竖屏方向采集数据
    videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    //录制的时候添加声音,添加输入源和输出源会暂时会使录制暂时卡住,所以在要使用声音的情况下要先调用该方法来防止录制被卡住。
    [videoCamera addAudioInputsAndOutputs];
    //前置不是镜像,后置是镜像
    videoCamera.horizontallyMirrorFrontFacingCamera = YES;
    videoCamera.horizontallyMirrorRearFacingCamera = NO;
    //先添加一个空的滤镜
    filter = [[LFGPUImageEmptyFilter alloc] init];
    //用于显示视频的GPUImageView
    filteredVideoView = [[GPUImageView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [videoCamera addTarget:filter];
    [filter addTarget:filteredVideoView];

给相机设置UI

   timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 27.0, 80, 26.0)];
    timeLabel.font = [UIFont systemFontOfSize:13.0f];
    timeLabel.text = @"录制 00:00";
    timeLabel.textAlignment = NSTextAlignmentCenter;
    timeLabel.backgroundColor = [UIColor colorWithRed:253/256.0 green:91/256.0 blue:73/256.0 alpha:1];
    timeLabel.textColor = [UIColor whiteColor];
    [filteredVideoView addSubview:timeLabel];
//    [[AppDelegate appDelegate].cmImageSize setLabelsRounded:timeLabel cornerRadiusValue:2 borderWidthValue:0 borderColorWidthValue:[UIColor clearColor]];
    timeLabel.hidden = YES;
    
    
    _btView = [[UIView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH/2 - 36.5, SCREEN_HEIGHT - 125, 73, 73)];
    [_btView makeCornerRadius:36.5 borderColor:nil borderWidth:0];
    _btView.backgroundColor = [UIColor colorWithRed:(float)0xfe/256.0 green:(float)0x65/256.0 blue:(float)0x53/256.0 alpha:1];
    [filteredVideoView addSubview:_btView];
    
    _photoCaptureButton = [[UIButton alloc] initWithFrame:CGRectMake(SCREEN_WIDTH/2 - 31.5, SCREEN_HEIGHT- 120, 63, 63)];
    _photoCaptureButton.backgroundColor = [UIColor colorWithRed:(float)0xfe/256.0 green:(float)0x65/256.0 blue:(float)0x53/256.0 alpha:1];

    [_photoCaptureButton addTarget:self action:@selector(startRecording:) forControlEvents:UIControlEventTouchUpInside];
    [_photoCaptureButton makeCornerRadius:31.5 borderColor:[UIColor blackColor] borderWidth:1.5];

    [filteredVideoView addSubview:_photoCaptureButton];
     
    _camerafilterChangeButton = [[UIButton alloc] init];
    _camerafilterChangeButton.frame = CGRectMake(SCREEN_WIDTH - 160,  25, 30.0, 30.0);
    UIImage* img = [UIImage imageNamed:@"beautyOFF"];
    [_camerafilterChangeButton setImage:img forState:UIControlStateNormal];
    [_camerafilterChangeButton setImage:[UIImage imageNamed:@"beautyON"] forState:UIControlStateSelected];
    [_camerafilterChangeButton addTarget:self action:@selector(changebeautifyFilterBtn:) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:_camerafilterChangeButton];
    
    UIButton* backBtn = [[UIButton alloc] initWithFrame:CGRectMake(SCREEN_WIDTH - 60, 25, 30, 30)];
    [backBtn setImage:[UIImage imageNamed:@"BackToHome"] forState:UIControlStateNormal];
    [backBtn addTarget:self action:@selector(clickBackToHome) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:backBtn];

    _cameraPositionChangeButton = [[UIButton alloc] initWithFrame:CGRectMake(SCREEN_WIDTH - 110, 25, 30, 30)];
    UIImage* img2 = [UIImage imageNamed:@"cammera"];
    [_cameraPositionChangeButton setImage:img2 forState:UIControlStateNormal];
    [_cameraPositionChangeButton addTarget:self action:@selector(changeCameraPositionBtn:) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:_cameraPositionChangeButton];
    
    _cameraChangeButton  = [[UIButton alloc] init];
    _cameraChangeButton.hidden = YES;
    _cameraChangeButton.frame = CGRectMake(SCREEN_WIDTH - 100 , SCREEN_HEIGHT - 105.0, 52.6, 50.0);
    UIImage* img3 = [UIImage imageNamed:@"complete"];
    [_cameraChangeButton setImage:img3 forState:UIControlStateNormal];
    [_cameraChangeButton addTarget:self action:@selector(stopRecording:) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:_cameraChangeButton];
    
    _dleButton = [[UIButton alloc] init];
    _dleButton.hidden = YES;
    _dleButton.frame = CGRectMake( 50 , SCREEN_HEIGHT - 105.0, 50, 50.0);
    UIImage* img4 = [UIImage imageNamed:@"del"];
    [_dleButton setImage:img4 forState:UIControlStateNormal];
    [_dleButton addTarget:self action:@selector(clickDleBtn:) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:_dleButton];
    
    _inputLocalVieoBtn = [[UIButton alloc] init];
//    _inputLocalVieoBtn.hidden = YES;
    _inputLocalVieoBtn.frame = CGRectMake( 50 , SCREEN_HEIGHT - 105.0, 50, 50.0);
    UIImage* img5 = [UIImage imageNamed:@"record_ico_input_1"];
    [_inputLocalVieoBtn setImage:img5 forState:UIControlStateNormal];
    [_inputLocalVieoBtn addTarget:self action:@selector(clickInputBtn:) forControlEvents:UIControlEventTouchUpInside];
    [filteredVideoView addSubview:_inputLocalVieoBtn];

    
    progressPreView = [[UIView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT -4 , 0, 4)];
    progressPreView.backgroundColor = UIColorFromRGB(0xffc738);
    [progressPreView makeCornerRadius:2 borderColor:nil borderWidth:0];
    [filteredVideoView addSubview:progressPreView];

给相机设置点击聚焦

-(void)cameraViewTapAction:(UITapGestureRecognizer *)tgr
{
    //识别为手势触摸
        if (tgr.state == UIGestureRecognizerStateRecognized && (_focusLayer == nil || _focusLayer.hidden)) {
        CGPoint location = [tgr locationInView:filteredVideoView];
        [self setfocusImage];
        [self layerAnimationWithPoint:location];
        AVCaptureDevice *device = videoCamera.inputCamera;
        CGPoint pointOfInterest = [self convertToPointOfInterestFromViewCoordinates:location];
        NSError *error;
        //需要先锁定,防止其他线程访问
        if ([device lockForConfiguration:&error]) {
            //判断是否支持控制对焦
            if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
                [device setFocusPointOfInterest:pointOfInterest];
                [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];

            }
            
            if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure])
            {
                [device setExposurePointOfInterest:pointOfInterest];
                [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
            }
            //操作完成后,记得进行unlock。
            [device unlockForConfiguration];
            
            NSLog(@"FOCUS OK");
        } else {
            NSLog(@"ERROR = %@", error);
        }  
    }
}

- (void)layerAnimationWithPoint:(CGPoint)point {
    if (_focusLayer) {
        CALayer *focusLayer = _focusLayer;
        focusLayer.hidden = NO;
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        [focusLayer setPosition:point];
        focusLayer.transform = CATransform3DMakeScale(2.0f,2.0f,1.0f);
        [CATransaction commit];
         
        CABasicAnimation *animation = [ CABasicAnimation animationWithKeyPath: @"transform" ];
        //CATransform3DMakeScale (CGFloat sx, CGFloat sy,CGFloat sz),用来表示针对X(sx)轴,Y(sy)轴,Z(sz)轴的3D比例缩放,
        animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeScale(1.0f,1.0f,1.0f)];
//        animation.delegate = self;
        animation.duration = 0.3f;
        animation.repeatCount = 1;
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeForwards;
        [focusLayer addAnimation: animation forKey:@"animation"];
        
        // 0.5秒钟延时
        [self performSelector:@selector(focusLayerNormal) withObject:self afterDelay:0.5f];
    }
}

相机切换和解除美颜

   if (!sender.selected) {
        //开启美颜
        sender.selected = YES;
        [videoCamera removeAllTargets];
        filter = [[GPUImageBeautifyFilter alloc] init];
        [videoCamera addTarget:filter];
        [filter addTarget:filteredVideoView];
    }else
    {
        //关闭美颜
        sender.selected = NO;
        [videoCamera removeAllTargets];
        filter = [[LFGPUImageEmptyFilter alloc] init];
        [videoCamera addTarget:filter];
        [filter addTarget:filteredVideoView];
    }

开启摄像头

    //开启摄像头
    [videoCamera startCameraCapture];

开始录制

     movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(720.0, 1280.0)];//GPUImageMovieWriter 保存滤镜处理过的视频
     movieWriter.isNeedBreakAudioWhiter = YES;//GPUImageMovieWriter添加的新属性
     movieWriter.encodingLiveVideo = YES;
     movieWriter.shouldPassthroughAudio = YES;//是否使用声音
     [filter addTarget:movieWriter];
     videoCamera.audioEncodingTarget = movieWriter;//加入声音
     [movieWriter startRecording];

暂停录制,也就是实现断点续拍,就是把视频先保存起来,并将视频地址保存在数组中,最后结束录制的时候取出数组中的所有视频

     [movieWriter finishRecording];
     [filter removeTarget:movieWriter];
     [urlArray addObject:[NSURL URLWithString:[NSString stringWithFormat:@"file://%@",pathToMovie]]];
      _isRecoding = NO;

在下一篇视频导出中会有将断开的视频整合到一起,实现断点续拍的讲解。
地址:https://blog.csdn.net/weixin_42433480/article/details/90109873

你可能感兴趣的:(移动开发iOS,音视频,移动开发)