版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.09.04 |
前言
GPUImage
是直接利用顯卡實(shí)現(xiàn)視頻或者圖像處理的技術(shù)。感興趣可以看上面幾篇文章忘巧。
1. GPUImage解析(一) —— 基本概覽(一)
2. GPUImage解析(二) —— 基本概覽(二)
3. GPUImage解析(三) —— 基本概覽(三)
4. GPUImage解析(四) —— 安裝方法及框架介紹
5. GPUImage解析(五) —— 框架中的幾個(gè)基類
6. GPUImage解析(六) —— 一個(gè)簡(jiǎn)單的實(shí)例(一)
7. GPUImage解析(七) —— 一個(gè)簡(jiǎn)單的實(shí)例結(jié)合GPUImageVideoCamera(二)
功能要求
利用GPUImageVideoCamera
采集視頻,并且利用GPUImageMovieWriter
寫入到臨時(shí)文件中睦刃,同時(shí)采用的多重濾鏡的效果砚嘴。
功能實(shí)現(xiàn)
下面我們就看一下代碼實(shí)現(xiàn)。
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
/*******************兩個(gè)類的分隔線***********************/
/**
* 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;
//配置存儲(chǔ)路徑和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];
//存儲(chǔ)視頻
[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
下面運(yùn)行涩拙,發(fā)生了崩潰际长,提示如下所示:
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
后來(lái)找到了是路徑問(wèn)題,我將路徑修改為NSString *moviePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"movie.m4v"];
就好了兴泥。
功能驗(yàn)證
下面我們就看一下功能效果工育,查看手機(jī)相冊(cè)。
可見(jiàn)實(shí)現(xiàn)了視頻的采集和存儲(chǔ)寫入搓彻。
參考文章
后記
未完如绸,待續(xù)~~