版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.09.04 |
前言
GPUImage
是直接利用显卡实现视频或者图像处理的技术。感兴趣可以看上面几篇文章。
1. GPUImage解析(一) —— 基本概览(一)
2. GPUImage解析(二) —— 基本概览(二)
3. GPUImage解析(三) —— 基本概览(三)
4. GPUImage解析(四) —— 安装方法及框架介绍
5. GPUImage解析(五) —— 框架中的几个基类
6. GPUImage解析(六) —— 一个简单的实例(一)
7. GPUImage解析(七) —— 一个简单的实例结合GPUImageVideoCamera(二)
功能要求
利用GPUImageVideoCamera
采集视频,并且利用GPUImageMovieWriter
写入到临时文件中,同时采用的多重滤镜的效果。
功能实现
下面我们就看一下代码实现。
1. JJGPUCustomFilter.h
#import "GPUImage.h"
@class JJGPUImageCombinationFilter;
@interface JJGPUCustomFilter : GPUImageFilterGroup
@property (nonatomic, strong) GPUImageBilateralFilter *bilateralFilter;
@property (nonatomic, strong) GPUImageCannyEdgeDetectionFilter *cannyEdgeFilter;
@property (nonatomic, strong) JJGPUImageCombinationFilter *combinationFilter;
@property (nonatomic, strong) GPUImageHSBFilter *hsbFilter;
@end
2.JJGPUCustomFilter.m
#import "JJGPUCustomFilter.h"
/**
* GPUImageCombinationFilter
*/
@interface JJGPUImageCombinationFilter : GPUImageThreeInputFilter
{
GLint smoothDegreeUniform;
}
@property (nonatomic, assign) CGFloat intensity;
@end
NSString *const kGPUImageBeautifyFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
varying highp vec2 textureCoordinate3;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
uniform sampler2D inputImageTexture3;
uniform mediump float smoothDegree;
void main()
{
highp vec4 bilateral = texture2D(inputImageTexture, textureCoordinate);
highp vec4 canny = texture2D(inputImageTexture2, textureCoordinate2);
highp vec4 origin = texture2D(inputImageTexture3,textureCoordinate3);
highp vec4 smooth;
lowp float r = origin.r;
lowp float g = origin.g;
lowp float b = origin.b;
if (canny.r < 0.2 && r > 0.3725 && g > 0.1568 && b > 0.0784 && r > b && (max(max(r, g), b) - min(min(r, g), b)) > 0.0588 && abs(r-g) > 0.0588) {
smooth = (1.0 - smoothDegree) * (origin - bilateral) + bilateral;
}
else {
smooth = origin;
}
smooth.r = log(1.0 + 0.2 * smooth.r)/log(1.2);
smooth.g = log(1.0 + 0.2 * smooth.g)/log(1.2);
smooth.b = log(1.0 + 0.2 * smooth.b)/log(1.2);
gl_FragColor = smooth;
}
);
@implementation JJGPUImageCombinationFilter
- (id)init
{
if (self = [super initWithFragmentShaderFromString:kGPUImageBeautifyFragmentShaderString]) {
smoothDegreeUniform = [filterProgram uniformIndex:@"smoothDegree"];
}
self.intensity = 0.5;
return self;
}
- (void)setIntensity:(CGFloat)intensity
{
_intensity = intensity;
[self setFloat:intensity forUniform:smoothDegreeUniform program:filterProgram];
}
@end
/*******************两个类的分隔线***********************/
/**
* JJGPUCustomFilter
*/
@implementation JJGPUCustomFilter
#pragma mark - Override Base Function
- (id)init;
{
if (!(self = [super init]))
{
return nil;
}
// First pass: face smoothing filter
self.bilateralFilter = [[GPUImageBilateralFilter alloc] init];
self.bilateralFilter.distanceNormalizationFactor = 4.0;
[self addFilter:self.bilateralFilter];
// Second pass: edge detection
self.cannyEdgeFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
[self addFilter:self.cannyEdgeFilter];
// Third pass: combination bilateral, edge detection and origin
self.combinationFilter = [[JJGPUImageCombinationFilter alloc] init];
[self addFilter:self.combinationFilter];
// Adjust HSB
self.hsbFilter = [[GPUImageHSBFilter alloc] init];
[self.hsbFilter adjustBrightness:1.1];
[self.hsbFilter adjustSaturation:1.1];
[self.bilateralFilter addTarget:self.combinationFilter];
[self.cannyEdgeFilter addTarget:self.combinationFilter];
[self.combinationFilter addTarget:self.hsbFilter];
self.initialFilters = [NSArray arrayWithObjects:self.bilateralFilter, self.cannyEdgeFilter, self.combinationFilter,nil];
self.terminalFilter = self.hsbFilter;
return self;
}
#pragma mark - GPUImageInput
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
{
if (currentFilter != self.inputFilterToIgnoreForUpdates)
{
if (currentFilter == self.combinationFilter) {
textureIndex = 2;
}
[currentFilter newFrameReadyAtTime:frameTime atIndex:textureIndex];
}
}
}
- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
{
for (GPUImageOutput<GPUImageInput> *currentFilter in self.initialFilters)
{
if (currentFilter == self.combinationFilter) {
textureIndex = 2;
}
[currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex];
}
}
@end
3. JJGPUImageFilterCameraVC.h
#import <UIKit/UIKit.h>
@interface JJGPUImageFilterCameraVC : UIViewController
@end
4. JJGPUImageFilterCameraVC.m
#import "JJGPUImageFilterCameraVC.h"
#import "GPUImage.h"
#import "JJGPUCustomFilter.h"
#import <AssetsLibrary/ALAssetsLibrary.h>
@interface JJGPUImageFilterCameraVC ()
@property (nonatomic, strong) GPUImageVideoCamera *videoCamera;
@property (nonatomic, strong) GPUImageMovieWriter *movieWriter;
@property (nonatomic, strong) GPUImageView *imageView;
@property (nonatomic, strong) JJGPUCustomFilter *customFilter;
@property (nonatomic, copy) NSString *moviePath;
@property (nonatomic, strong) NSURL *movieURL;
@end
@implementation JJGPUImageFilterCameraVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupUI];
[self beginConfiguration];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden = NO;
}
#pragma mark - Object Private Function
- (void)setupUI
{
//配置GPUImageView
self.imageView = [[GPUImageView alloc] init];
self.imageView.frame = self.view.frame;
[self.view addSubview:self.imageView];
}
- (void)beginConfiguration
{
//配置GPUImageVideoCamera
self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 cameraPosition:AVCaptureDevicePositionFront];
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
self.videoCamera.horizontallyMirrorFrontFacingCamera = YES;
//配置存储路径和URL
NSString *moviePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"movie.m4v"];
unlink([moviePath UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
self.moviePath = moviePath;
self.movieURL = movieURL;
//配置GPUImageMovieWriter
self.movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(720.0, 1280.0)];
self.videoCamera.audioEncodingTarget = self.movieWriter;
self.movieWriter.encodingLiveVideo = YES;
[self.videoCamera startCameraCapture];
//配置自定义滤镜 JJGPUCustomFilter
self.customFilter = [[JJGPUCustomFilter alloc] init];
[self.videoCamera addTarget:self.customFilter];
[self.customFilter addTarget:self.imageView];
[self.customFilter addTarget:self.movieWriter];
[self.movieWriter startRecording];
//存储视频
[self storeVideo];
}
- (void)storeVideo
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.customFilter removeTarget:self.movieWriter];
[self.videoCamera stopCameraCapture];
[self.movieWriter finishRecording];
ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(self.moviePath)) {
[assetLibrary writeVideoAtPathToSavedPhotosAlbum:self.movieURL completionBlock:^(NSURL *assetURL, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(@"保存视频失败");
}
else {
NSLog(@"保存视频成功");
}
});
}];
}
else {
NSLog(@"路径不兼容");
}
});
}
@end
下面运行,发生了崩溃,提示如下所示:
2017-09-04 18:27:15.901990+0800 JJOC[1913:1269773] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[AVAssetWriter startWriting] Cannot call method when status is 3'
*** First throw call stack:
(0x18f942fe0 0x18e3a4538 0x197326d34 0x1973212d8 0x10023e3f8 0x100509a10 0x1005165bc 0x100242b7c 0x10023e000 0x100268354 0x100268920 0x197359b2c 0x197359994 0x192269f38 0x192288e9c 0x100509a10 0x100515a84 0x1005241f8 0x10050ba60 0x100517128 0x10050d634 0x100518358 0x10052057c 0x18ea02fbc 0x18ea02cac)
libc++abi.dylib: terminating with uncaught exception of type NSException
后来找到了是路径问题,我将路径修改为NSString *moviePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"movie.m4v"];
就好了。
功能验证
下面我们就看一下功能效果,查看手机相册。
可见实现了视频的采集和存储写入。
参考文章
1. GPUImageMovieWriter 无法2次录像 报错:[AVAssetWriter startWriting] Cannot call method when status is 3
后记
未完,待续~~