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


功能驗(yàn)證

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

可見(jiàn)實(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市好唯,隨后出現(xiàn)的幾起案子竭沫,更是在濱河造成了極大的恐慌,老刑警劉巖骑篙,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕提,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡靶端,警方通過(guò)查閱死者的電腦和手機(jī)谎势,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杨名,“玉大人脏榆,你說(shuō)我怎么就攤上這事√ǖ” “怎么了须喂?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我坞生,道長(zhǎng)仔役,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任是己,我火速辦了婚禮又兵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卒废。我一直安慰自己沛厨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布摔认。 她就那樣靜靜地躺著逆皮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪级野。 梳的紋絲不亂的頭發(fā)上页屠,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音蓖柔,去河邊找鬼。 笑死风纠,一個(gè)胖子當(dāng)著我的面吹牛况鸣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竹观,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼镐捧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了臭增?” 一聲冷哼從身側(cè)響起懂酱,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎誊抛,沒(méi)想到半個(gè)月后列牺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拗窃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年瞎领,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片随夸。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡九默,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宾毒,到底是詐尸還是另有隱情驼修,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站乙各,受9級(jí)特大地震影響勉躺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜觅丰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一饵溅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妇萄,春花似錦蜕企、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至懦底,卻和暖如春唇牧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聚唐。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工丐重, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杆查。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓扮惦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亲桦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崖蜜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容