1. 程式人生 > Android開發 >使用GPUImage實現視訊濾鏡

使用GPUImage實現視訊濾鏡

關於GPUImage

這裡直接引用官方描述:

The GPUImage framework is a BSD-licensed iOS library that lets you apply GPU-accelerated filters and other effects to images,live camera video,and movies. In comparison to Core Image (part of iOS 5.0),GPUImage allows you to write your own custom filters,supports deployment to iOS 4.0,and has a simpler interface. However,it currently lacks some of the more advanced features of Core Image,such as facial detection.

專案地址:https://github.com/BradLarson/GPUImage

濾鏡基本原理

濾鏡處理的原理就是把靜態圖片或者視訊的每一幀進行圖形變換再顯示出來。它的本質就是畫素點的座標和顏色變化,這點從GPUImage專案中濾鏡的原始碼就可以瞭解到。 例如下面就是一個名為DarkenBlend的濾鏡程式碼:

NSString *const kGPUImageDarkenBlendFragmentShaderString = SHADER_STRING( varying highp vec2 textureCoordinate; varying highp vec2 textureCoordinate2;
複製程式碼

uniform sampler2D inputImageTexture; uniform sampler2D inputImageTexture2;

void main() {

lowp vec4 base = texture2D(inputImageTexture,textureCoordinate);
lowp vec4 overlayer = texture2D(inputImageTexture2,textureCoordinate2);

gl_FragColor = vec4(min(overlayer.rgb * base.a,base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a),1.0);
複製程式碼

} );

GPUImage的最新版本已經內建了125個濾鏡。也支援編寫自定義濾鏡(custom filter)。

影象處理流程

資料來源需要實現GPUImageOutput介面,而後續各個環節(包括最後處理輸出結果)的物件都要實現GPUImageInput介面。每個處理環節都是從上一個環節獲取影象資料,進行處理後再將結果傳遞給下一個。下游的處理物件稱為上一步的target。使用addTarget:方法為處理鏈路新增每個環節的物件。一個常見的鏈條如下: 資料來源(例如GPUImageVideoCamera或者GPUImageMovie)->各類filter->GPUImageView 如果需要將結果輸出到檔案,只要把上述流程中最末的GPUImageView改為一個GPUImageMovieWriter即可。所以濾鏡的新增和替換,以及輸出結果都十分簡單方便。

附一張GPUImage的結構圖

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交 流群:519832104 不管你是小白還 是大牛歡迎入駐,分享經驗,討論技術,大家一起交流學習成長!

另附上一份各好友收集的大廠面試題,需要iOS開發學習資料、面試真題,可以新增 iOS開發進階交流群,進群可自行下載!

接入app

將濾鏡接入app非常簡單,只要建立一個GPUImageMovie,一個具體的GPUImageFilter和一個用來顯示的GPUImageView,然後按處理鏈條的上下游關係把它們用addTarget串起來。

下面附上我的app裡面的呼叫程式碼(預覽視訊):

_movie = [[GPUImageMovie alloc] initWithURL:url];
_movie.shouldRepeat = YES;
_movie.playAtActualSpeed = YES;
_movie.playSound = YES;
_movie.delegate = self;

GPUImageMissEtikateFilter *filter = [[GPUImageMissEtikateFilter alloc] init];//膠片效果
[_movie addTarget:filter];
[filter addTarget:self.gpuImageView];

[_movie startProcessing];
複製程式碼

然後就是一些重要的引數設定: playAtActualSpeed 控制GPUImageView預覽視訊時的速度是否要保持真實的速度。如果設為NO,則會將視訊的所有幀無間隔渲染,導致速度非常快。設為YES,則會根據視訊本身時長計算出每幀的時間間隔,然後每渲染一幀,就sleep一個時間間隔,從而達到正常的播放速度。

shouldRepeat 控制視訊是否迴圈播放。

當你不想預覽,而是想將處理過的結果輸出到檔案時,步驟也類似,只是不再需要建立GPUImageView,而是需要一個GPUImageMovieWriter:

_writer = [[GPUImageMovieWriter alloc] initWithMovieURL:filtedMovieURL size:CGSizeMake(480,640) fileType:(NSString *)kUTTypeMPEG4 outputSettings:outputSettings];
_writer.encodingLiveVideo = NO;
_writer.shouldPassthroughAudio = NO;
_movie.audioEncodingTarget = _writer;
_movie.playAtActualSpeed = NO;
[_movie addTarget:_curFilter];
[_curFilter addTarget:_writer];
[_movie enableSynchronizedEncodingUsingMovieWriter:_writer];
[_writer startRecording];
[_movie startProcessing];
複製程式碼

一些侷限和踩過的坑

1.預覽時不支援播放聲音 視訊結果輸出到GPUImageView預覽時不支援播放聲音,所以要自行新增聲音播放: 自行建立了一個播放器物件

(void) setupSound { if (theAudioPlayer != nil) {

[theAudioPlayer pause];
[theAudioPlayer seekToTime:kCMTimeZero];
theAudioPlayer = nil;
複製程式碼

} theAudioPlayer = [[AVPlayer alloc] initWithURL:self.url]; }

跟GPUImageView的影象同步播放

if (self.playSound && hasAudioTrack)
{
    [theAudioPlayer seekToTime:kCMTimeZero];
    [theAudioPlayer play];
}
複製程式碼

由於GPUImageMovie裡面沒有在這幾個重要位置提供回撥,所以只能在原始碼中直接新增,這也導致了對原始碼的侵入,不利於以後版本升級。

2.GPUImageView預覽視訊並迴圈播放時,當播放重新開始時,有大約50%的概率會有紅屏閃一下,目前還未定位到原因

3.GPUImageView預覽視訊時,app切到後臺會crash,所以要特別處理下,在willDisappear和handleApplicationWillEnterBackgroundNotify這兩個時機都要暫停濾鏡處理:

if (_movie) {
    [_movie endProcessing];
    [_movie removeAllTargets];
    _movie = nil;
}
複製程式碼

對應的,在willAppear和handleApplicationWillEnterForegroundNotify這兩個時機要重啟處理:

if (!_movie) {
    _movie = [[GPUImageMovie alloc] initWithURL:url];
}

_movie.shouldRepeat = YES;
_movie.playAtActualSpeed = YES;
_movie.playSound = YES;
_movie.delegate = self;

[_movie addTarget:_curFilter];
[_curFilter addTarget:self.gpuImageView];

[_movie startProcessing];
複製程式碼

點選此處,立即與iOS大牛交流學習