您的位置:首页 > 产品设计 > UI/UE

GPUImage美颜视频录制

2017-08-10 20:48 435 查看
工作太忙,偷闲分享一个我刚做的一个录制视频工具类,基于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 <Foundation/Foundation.h>

@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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: