GPUImage解析(八) —— 一個(gè)簡(jiǎn)單的實(shí)例之多濾鏡視頻采集存儲(chǔ)(三)

版本記錄

版本號(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)找到了是路徑問題,我將路徑修改為NSString *moviePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"movie.m4v"];就好了。


功能驗(yàn)證

下面我們就看一下功能效果,查看手機(jī)相冊(cè)。

可見實(shí)現(xiàn)了視頻的采集和存儲(chǔ)寫入。

參考文章

1. GPUImageMovieWriter 無(wú)法2次錄像 報(bào)錯(cuò):[AVAssetWriter startWriting] Cannot call method when status is 3

后記

未完,待續(xù)~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容