GPUImage美颜视频录制

工作太忙,偷闲分享一个我刚做的一个录制视频工具类,基于GPUImage的带美颜功能的工具,简单易用!

完整录制播放代码链接:https://github.com/SunshineTraveller/LMVideoManager

测试Demo只支持真机,模拟器是无法调用摄像头的!

用法如下:

声明一个属性:

/** manager */

@property (nonatomic,strong)LMVideoManager *manager;


在一个方法里初始化它

    _manager = [[LMVideoManageralloc]init];

    _manager.delegate =self;

    [_managershowWithFrame:CGRectMake(20,120,LMWID-40,LMHEI/2-1)superView:self.view];

    _manager.maxTime =30.0;  // 最长录制时间,可忽略。若设置,则超过该时间自动停止录制


在一个点击事件中调用开始录制功能

[_manager startRecording];


想停止的时候调用停止录制功能

[_manager endRecording];


代理方法回调

// 开始录制

-(void)didStartRecordVideo {

    

    [self.viewaddSubview:[LMNoticeLabelmessage:@"开始录制..."delaySecond:2]];

}

// 压缩中回调

-(void)didCompressingVideo {

    

    [self.viewaddSubview:[LMNoticeLabelmessage:@"视频压缩中..."delaySecond:2]];

    

}

// 录制结束回调,输出压缩路径

-(void)didEndRecordVideoWithTime:(CGFloat)totalTime outputFile:(NSString *)filePath {

    

    [self.viewaddSubview:[LMNoticeLabelmessage:@"录制完毕,时长:%lu 路径:%@"delaySecond:4]];

    _filePath = filePath;

    

}





代码如下:

//

//  LMVideoManager.h

//  TopicEdit

//

//  Created by 张利民 on 2017/8/9.

//  Copyright © 2017 ghostlord. All rights reserved.

//


typedef NS_ENUM(NSUInteger, LMVideoManagerCameraType) {

    LMVideoManagerCameraTypeFront = 1,

    LMVideoManagerCameraTypeBack,

};


@protocol LMVideoManagerProtocol <NSObject>



/** 开始录制 */

-(void)didStartRecordVideo;


/** 视频压缩中 */

-(void)didCompressingVideo;


/** 结束录制 */

-(void)didEndRecordVideoWithTime:(CGFloat)totalTime outputFile:(NSString *)filePath;




@end


#import "GPUImageBeautifyFilter.h"

#import


@interface LMVideoManager : NSObject


/** 代理 */

@property (nonatomic,weak)id <LMVideoManagerProtocol> delegate;


/** 录制视频区域 */

@property (nonatomic,assign)CGRect frame;


/** 录制视频最大时长 */

@property (nonatomic,assign)CGFloat maxTime;


/**

 录制视频单例,若工程中不止一处用到录视频,尺寸有变,直接实例化即可忽略此方法


 @return self

 */

+(instancetype)manager;


/**

 加载到显示的视图上

 @param frame  父视图中的frame

 @param superView 父视图

 */

-(void)showWithFrame:(CGRect)frame superView:(UIView *)superView;


/**

 开始录制

 */

-(void)startRecording;



/**

 结束录制

 */

-(void)endRecording;



/**

 暂停录制

 */

-(void)pauseRecording;



/**

 继续录制

 */

-(void)resumeRecording;


/**

 切换前后摄像头

 @param type LMVideoManagerCameraTypeFront 前置

 */

-(void)changeCameraPosition:(LMVideoManagerCameraType)type;



/**

 打开闪光灯


 @param on YES  NO

 */

-(void)turnTorchOn:(BOOL)on;



@end



.m 文件

//

//  LMVideoManager.m

//  TopicEdit

//

//  Created by 张利民 on 2017/8/9.

//  Copyright © 2017 ghostlord. All rights reserved.

//

#define COMPRESSEDVIDEOPATH [NSHomeDirectory() stringByAppendingFormat:@"/Documents/CompressionVideoField"]



#import "LMVideoManager.h"


@interface LMVideoManager ()<GPUImageVideoCameraDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>


/** 摄像头 */

@property (nonatomic,strong)GPUImageVideoCamera *videoCamera;


/** 美颜滤镜 */

@property (nonatomic,strong)GPUImageBeautifyFilter *beautifyFilter;


/** 视频输出视图 */

@property (nonatomic,strong)GPUImageView *displayView;


/** 视频写入 */

@property (nonatomic,strong)GPUImageMovieWriter *movieWriter;


/** 视频写入的地址URL */

@property (nonatomic,strong)NSURL *movieURL;


/** 视频写入路径 */

@property (nonatomic,copy)NSString *moviePath;


/** 压缩成功后的视频路径 */

@property (nonatomic,copy)NSString *resultPath;


/** 视频时长 */

@property (nonatomic,assign)int seconds;


/** 系统计时器 */

@property (nonatomic,strong)NSTimer *timer;


/** 计时器常量 */

@property (nonatomic,assign)int recordSecond;



@end


@implementation LMVideoManager


static LMVideoManager *_manager;


// 单例

+(instancetype)manager {

    

    staticdispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        _manager = [[LMVideoManageralloc]init];

    });

    return_manager;

}


+(instancetype)allocWithZone:(struct_NSZone *)zone {

    

    staticdispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        if (_manager ==nil) {

            _manager = [superallocWithZone:zone];

        }

    });

    return_manager;

}


// 开始录制

-(void)startRecording {

    

    NSString *defultPath = [selfgetVideoPathCache];

    self.moviePath = [defultPathstringByAppendingPathComponent:[selfgetVideoNameWithType:@"mp4"]];

    // 录制路径

    self.movieURL = [NSURLfileURLWithPath:self.moviePath];

    //

    unlink([self.moviePathUTF8String]);

    

    self.movieWriter = [[GPUImageMovieWriteralloc]initWithMovieURL:self.movieURLsize:CGSizeMake(480.0,640.0)];

    self.movieWriter.encodingLiveVideo =YES;

    self.movieWriter.shouldPassthroughAudio =YES;

    [self.beautifyFilteraddTarget:self.movieWriter];

    self.videoCamera.audioEncodingTarget =self.movieWriter;

    // 开始录制

    [self.movieWriterstartRecording];

    

    if (self.delegate && [self.delegaterespondsToSelector:@selector(didStartRecordVideo)]) {

        [self.delegatedidStartRecordVideo];

    }

    [self.timersetFireDate:[NSDatedistantPast]];

    [self.timerfire];

    

}


// 结束录制

-(void)endRecording {

    

    if ([self.videoCameraisRunning]) {

        

        [self.timerinvalidate];

        self.timer =nil;

        __weaktypeof(self) weakSelf =self;

        [self.movieWriterfinishRecording];

        [self.beautifyFilterremoveTarget:self.movieWriter];

        self.videoCamera.audioEncodingTarget = nil;

        if (self.recordSecond >self.maxTime) {

            

            // 清除录制的视频

            

        }else {

            

            // 压缩中...

            if ([self.delegaterespondsToSelector:@selector(didCompressingVideo)]) {

                [self.delegatedidCompressingVideo];

            }

            

            // 压缩

            [selfcompressVideoWithUrl:self.movieURLcompressionType:AVAssetExportPresetMediumQualityfilePath:^(NSString *resultPath,float memorySize,NSString *videoImagePath,int seconds) {

                

                

                NSData *data = [NSDatadataWithContentsOfFile:resultPath];

                CGFloat totalTime = (CGFloat)data.length /1024 / 1024;

                

                // 压缩完回调

                if ([weakSelf.delegaterespondsToSelector:@selector(didEndRecordVideoWithTime:outputFile:)]) {

                    [weakSelf.delegatedidEndRecordVideoWithTime:totalTimeoutputFile:resultPath];

                }

                

            }];

        }

    }

}


// 暂停

-(void)pauseRecording {

    

    if ([_videoCameraisRunning]) {

        [self.timerinvalidate];

        self.timer =nil;

        [_videoCamerapauseCameraCapture];

    }

    

}


// 恢复

-(void)resumeRecording {

 

    [_videoCameraresumeCameraCapture];

    [self.timersetFireDate:[NSDatedistantPast]];

    [self.timerfire];

}



#pragma mark -- 自定义方法


-(void)showWithFrame:(CGRect)frame superView:(UIView *)superView {

    

    _frame = frame;

    [self.beautifyFilteraddTarget:self.displayView];

    [self.videoCameraaddTarget:self.beautifyFilter];

    [superView addSubview:self.displayView];

    [self.videoCamerastartCameraCapture];

    

}


// 切换前后摄像头

-(void)changeCameraPosition:(LMVideoManagerCameraType)type {

    

    switch (type) {

        caseLMVideoManagerCameraTypeFront:{

            [_videoCamerarotateCamera];

        }

            break;

        caseLMVideoManagerCameraTypeBack:{

            [_videoCamerarotateCamera];

        }

            break;

            

        default:

            break;

    }

    

}


#pragma mark ----摄像头输出代理方法----

- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer{

    

}


// 手电筒开关

-(void)turnTorchOn:(BOOL)on {

    

    if ([_videoCamera.inputCamerahasTorch] && [_videoCamera.inputCamerahasFlash]) {

        

        [_videoCamera.inputCameralockForConfiguration:nil];

        if (on) {

            [_videoCamera.inputCamerasetTorchMode:AVCaptureTorchModeOn];

            [_videoCamera.inputCamerasetFlashMode:AVCaptureFlashModeOn];

        }else{

            [_videoCamera.inputCamerasetTorchMode:AVCaptureTorchModeOff];

            [_videoCamera.inputCamerasetFlashMode:AVCaptureFlashModeOff];

        }

        [_videoCamera.inputCameraunlockForConfiguration];

        

    }

    

}


// 获取视频地址

-(NSString *)getVideoPathCache {

    

    NSString *videoCache = [NSTemporaryDirectory()stringByAppendingString:@"videos"];

    BOOL isDir =NO;

    NSFileManager *fileManager = [NSFileManagerdefaultManager];

    BOOL existed = [fileManagerfileExistsAtPath:videoCacheisDirectory:&isDir];

    if (!existed) {

        [fileManager createDirectoryAtPath:videoCachewithIntermediateDirectories:YESattributes:nilerror:nil];

    }

    return videoCache;

}


// 获取视频名称

-(NSString *)getVideoNameWithType:(NSString *)fileType {

    

    NSTimeInterval now = [[NSDatedate]timeIntervalSince1970];

    NSDateFormatter *formatter = [[NSDateFormatteralloc]init];

    [formatter setDateFormat:@"HHmmss"];

    NSDate *nowDate = [NSDatedateWithTimeIntervalSince1970:now];

    NSString *timeStr = [formatterstringFromDate:nowDate];

    NSString *fileName = [NSStringstringWithFormat:@"video_%@.%@",timeStr,fileType];

    return fileName;

    

}


#pragma mark -- 懒加载


// 摄像头

-(GPUImageVideoCamera *)videoCamera {

    

    if (_videoCamera ==nil) {

        _videoCamera = [[GPUImageVideoCameraalloc]initWithSessionPreset:AVCaptureSessionPreset640x480cameraPosition:AVCaptureDevicePositionBack];

        _videoCamera.outputImageOrientation =UIInterfaceOrientationPortrait;

        _videoCamera.horizontallyMirrorFrontFacingCamera =YES;

        _videoCamera.delegate =self;

        // 可防止允许声音通过的情况下,避免第一帧黑屏

        [_videoCameraaddAudioInputsAndOutputs];

    }

    return_videoCamera;

}


// 滤镜

-(GPUImageBeautifyFilter *)beautifyFilter {

    

    if (_beautifyFilter ==nil) {

        _beautifyFilter = [[GPUImageBeautifyFilteralloc]init];

    }

    return_beautifyFilter;

}


// 展示视图

-(GPUImageView *)displayView {

    

    if (_displayView ==nil) {

        _displayView = [[GPUImageViewalloc]initWithFrame:self.frame];

        _displayView.fillMode =kGPUImageFillModePreserveAspectRatioAndFill;

    }

    return_displayView;

}


// 计时器

-(NSTimer *)timer {

    if (!_timer) {

        _timer = [NSTimerscheduledTimerWithTimeInterval:1target:selfselector:@selector(updateWithTime)userInfo:nilrepeats:YES];

    }

    return_timer;

}


// 超过最大录制时长结束录制

-(void)updateWithTime {

    

    self.recordSecond++;

    if (self.recordSecond >=self.maxTime) {

        [selfendRecording];

    }

    

}


// 压缩视频

-(void)compressVideoWithUrl:(NSURL *)url compressionType:(NSString *)type filePath:(void(^)(NSString *resultPath,float memorySize,NSString * videoImagePath,int seconds))resultBlock {

    

    NSString *resultPath;

    

    

     // 视频压缩前大小

    NSData *data = [NSDatadataWithContentsOfURL:url];

    CGFloat totalSize = (float)data.length /1024 / 1024;

    NSLog(@"压缩前大小:%.2fM",totalSize);

    AVURLAsset *avAsset = [AVURLAssetURLAssetWithURL:urloptions:nil];

    

    CMTime time = [avAssetduration];

    

    // 视频时长

    int seconds =ceil(time.value / time.timescale);

    

    NSArray *compatiblePresets = [AVAssetExportSessionexportPresetsCompatibleWithAsset:avAsset];

    if ([compatiblePresetscontainsObject:type]) {

        

        // 中等质量

        AVAssetExportSession *session = [[AVAssetExportSessionalloc]initWithAsset:avAssetpresetName:AVAssetExportPresetMediumQuality];

        

        // 用时间给文件命名防止存储被覆盖

        NSDateFormatter *formatter = [[NSDateFormatteralloc]init];

        [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];

        

        // 若压缩路径不存在重新创建

        NSFileManager *manager = [NSFileManagerdefaultManager];

        BOOL isExist = [managerfileExistsAtPath:COMPRESSEDVIDEOPATH];

        if (!isExist) {

            [manager createDirectoryAtPath:COMPRESSEDVIDEOPATHwithIntermediateDirectories:YESattributes:nilerror:nil];

        }

        resultPath = [COMPRESSEDVIDEOPATHstringByAppendingPathComponent:[NSStringstringWithFormat:@"user%outputVideo-%@.mp4",arc4random_uniform(10000),[formatterstringFromDate:[NSDatedate]]]];

        

        session.outputURL = [NSURLfileURLWithPath:resultPath];

        session.outputFileType =AVFileTypeMPEG4;

        session.shouldOptimizeForNetworkUse =YES;

        [session exportAsynchronouslyWithCompletionHandler:^{

            

            switch (session.status) {

                caseAVAssetExportSessionStatusUnknown:

                    break;

                caseAVAssetExportSessionStatusWaiting:

                    break;

                caseAVAssetExportSessionStatusExporting:

                    break;

                caseAVAssetExportSessionStatusCancelled:

                    break;

                caseAVAssetExportSessionStatusFailed:

                    break;

                caseAVAssetExportSessionStatusCompleted:{

                    

                    NSData *data = [NSDatadataWithContentsOfFile:resultPath];

                    // 压缩过后的大小

                    float compressedSize = (float)data.length /1024 / 1024;

                    resultBlock(resultPath,compressedSize,@"",seconds);

                    NSLog(@"压缩后大小:%.2f",compressedSize);

                }

                default:

                    break;

            }

        }];

    }

}


- (void)dealloc {

    

    [self.timerinvalidate];

    NSLog(@"销毁了啊");

    

}



@end










你可能感兴趣的:(iOS,封装)