版本记录
| 版本号 | 时间 |
|---|---|
| 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
后记
未完,待续~~

