demo下載地址:https://github.com/SXDgit/ZB_GPUImageVideoCameragit
先看效果圖:github
直接上代碼,後面解釋:chrome
- (void)createVideoCamera {
// 建立畫面捕獲器
self.videoCamera = [[GPUImageVideoCamera alloc]initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
// 輸出方向爲豎屏
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
self.videoCamera.horizontallyMirrorRearFacingCamera = NO;
self.videoCamera.horizontallyMirrorFrontFacingCamera = NO;
self.videoCamera.runBenchmark = YES;
// 構建組合濾鏡
[self addGPUImageFilter:self.sepiaFilter];
[self addGPUImageFilter:self.monochromeFilter];
// 建立畫面呈現控件
self.filterView = [[GPUImageView alloc]initWithFrame:self.view.frame];
self.filterView.fillMode = kGPUImageFillModePreserveAspectRatio;
self.view = self.filterView;
[self.videoCamera addTarget:self.filterView];
// 相機運行
[self.videoCamera startCameraCapture];
[self configMovie];
}
- (void)configMovie {
// 設置寫入地址
self.pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/ZBMovied%u.mp4", arc4random() % 1000]];
// movieUrl指視頻寫入的地址
self.moviewURL = [NSURL fileURLWithPath:_pathToMovie];
self.movieWriter = [[GPUImageMovieWriter alloc]initWithMovieURL:_moviewURL size:CGSizeMake(480.0, 640.0)];
_movieWriter.encodingLiveVideo = YES;
// 設置聲音
_videoCamera.audioEncodingTarget = _movieWriter;
}
- (void)addGPUImageFilter:(GPUImageFilter *)filter {
[self.filterGroup addFilter:filter];
GPUImageOutput<GPUImageInput> *newTerminalFilter = filter;
NSInteger count = self.filterGroup.filterCount;
if (count == 1) {
self.filterGroup.initialFilters = @[newTerminalFilter];
self.filterGroup.terminalFilter = newTerminalFilter;
}else {
GPUImageOutput<GPUImageInput> *terminalFilter = self.filterGroup.terminalFilter;
NSArray *initialFilters = self.filterGroup.initialFilters;
[terminalFilter addTarget:newTerminalFilter];
self.filterGroup.initialFilters = @[initialFilters[0]];
self.filterGroup.terminalFilter = newTerminalFilter;
}
}
- (void)switchButtonAction {
// 切換攝像頭先後翻轉
[self.videoCamera rotateCamera];
self.switchButton.selected = !self.switchButton.selected;
}
複製代碼
GPUImageFilter
是用來接收源圖像,經過自定義的頂點、片元着色器來渲染新的圖像,並在繪製完成後通知響應鏈的下一個對象。
GPUImageVideoCamera
提供來自攝像頭的圖像數據做爲源數據,是GPUImageOutput
的子類,通常是響應鏈的源頭。
GPUImageView
通常用於顯示GPUImage
的圖像,是響應鏈的終點。
GPUImageFilterGroup
是多個filter的集合,terminalFilter
爲最終的filter,initialFilter
是filter數組。自己不繪製圖像,對它的添加刪除Target操做,都會轉爲terminalFilter
的操做。 數組
GPUImageMovieWriter
是和
GPUImageView
處於同一地位的,都是視頻輸出類,只不過一個是輸出到文件,一個輸出到屏幕。
GPUImageBeautifyFilter
是
基於GPUImage的實時美顏濾鏡中的美顏濾鏡,來自琨君。它是繼承於
GPUImageFilterGroup
。包括了
GPUImageBilateralFilter
、
GPUImageCannyEdgeDetectionFilter
、
GPUImageCombinationFilter
、
GPUImageHSBFilter
。
GPUImageVideoCamera
捕獲攝像頭圖像調用newFrameReadyAtTime: atIndex:
通知GPUImageBeautifyFilter
;GPUImageBeautifyFilter
調用newFrameReadyAtTime: atIndex:
通知GPUImageBilateralFliter
輸入紋理已經準備好;GPUImageBilateralFliter
繪製圖像後在informTargetsAboutNewFrameAtTime()
,調用setInputFramebufferForTarget: atIndex
:把繪製的圖像設置爲GPUImageCombinationFilter
輸入紋理,並通知GPUImageCombinationFilter
紋理已經繪製完畢;GPUImageBeautifyFilter
調用newFrameReadyAtTime: atIndex:
通知 GPUImageCannyEdgeDetectionFilter
輸入紋理已經準備好;GPUImageCannyEdgeDetectionFilter
繪製圖像後,把圖像設置爲GPUImageCombinationFilter
輸入紋理;GPUImageBeautifyFilter
調用newFrameReadyAtTime: atIndex:
通知 GPUImageCombinationFilter
輸入紋理已經準備好;GPUImageCombinationFilter
判斷是否有三個紋理,三個紋理都已經準備好後調用GPUImageThreeInputFilter
的繪製函數renderToTextureWithVertices: textureCoordinates:
,圖像繪製完後,把圖像設置爲GPUImageHSBFilter
的輸入紋理,通知GPUImageHSBFilter
紋理已經繪製完畢;GPUImageHSBFilter
調用renderToTextureWithVertices: textureCoordinates:
繪製圖像,完成後把圖像設置爲GPUImageView
的輸入紋理,並通知GPUImageView
輸入紋理已經繪製完畢;GPUImageView
把輸入紋理繪製到本身的幀緩存,而後經過[self.context presentRenderbuffer:GL_RENDERBUFFER]
顯示到UIView
上。經過GPUImageVideoCamera
採集音視頻的信息,音頻信息直接發送給GPUImageMovieWriter
,視頻信息傳入響應鏈做爲源頭,渲染後的視頻信息再寫入GPUImageMovieWriter
,同時GPUImageView
顯示再屏幕上。 bash
經過源碼能夠知道GPUImage
是使用AVFoundation
框架來獲取視頻的。
AVCaptureSession
類從AV輸入設備的採集數據到制定的輸出。
爲了實現實時的圖像捕獲,要實現AVCaptureSession
類,添加合適的輸入(AVCaptureDeviceInput
)和輸出(好比AVCaptureMovieFileOutput
)調用startRunning
開始輸入到輸出的數據流,調用stopRunning
中止數據流。須要注意的是startingRunning
函數會花費必定的時間,因此不能在主線程調用,防止卡頓。 app
_inputCamera
、麥克風
_microphone
,建立攝像頭輸入
videoInput
和麥克風輸入
audioInput
。
videoInput
和
audioInput
爲
_captureSession
的輸入,同時設置
videoOutput
和
audioOutput
爲
_captureSession
的輸出,而且設置
videoOutput
和
audioOutput
的輸出
delegate
。
_captureSession
調用
startRunning
,開始捕獲信號。
audioEncodingTarget
,並經過調用
assetWriterAudioInput
的
appendSampleBuffer
方法寫入音頻數據。
assetWriterPixelBufferInput
的
appendSampleBuffer
方法寫入視頻數據。
一、錄製後保存在相冊裏的視頻是白屏?
在初始化movieWriter
的過程當中,使用addTarget:
增長了濾鏡致使。框架
二、錄製完視頻後,再次點擊錄製,會crash?
報錯的緣由是[AVAssetWriter startWriting] Cannot call method when status is 3
,報錯是在[self.movieWriter startRecording];
這行代碼,追溯源碼,能夠看到GPUImageMovieWriter
是對AssetWriter
進行了一次封裝,其核心的寫文件仍是由AssetWriter
完成。 經過源碼能夠發現[self.movieWriter finishRecording];
之後並無新建一個AssetWriter
實例。因此能夠保存視頻到相冊成功後,加入下面幾行代碼:dom
- (void)videoCameraReset {
[_videoCamera removeTarget:_movieWriter];
[[NSFileManager defaultManager] removeItemAtURL:_moviewURL error:nil];
[self initMovieWriter];
[_videoCamera addTarget:_movieWriter];
}
- (void)initMovieWriter {
_movieWriter = [[GPUImageMovieWriter alloc]initWithMovieURL:_moviewURL size:CGSizeMake(480.0, 640.0)];
_movieWriter.encodingLiveVideo = YES;
}
複製代碼
一、攝像頭實例取消對GPUImageMovieWriter
的綁定,由於從新實例化新的GPUImageMovieWriter
之後原來的實例就沒用了。
二、刪除原來已經寫好的影片文件,若是新的實例直接寫入已存在的文件會報錯AVAssetWriterStatusFailed
。
三、從新實例化一個GPUImageMovieWriter
。
四、把新的GPUImageMovieWriter
綁定到攝像頭實例。ide
這樣之後就能夠不一樣的錄製保存了。參考[紹棠] GPUImageMovieWriter 沒法2次錄像 報錯:[AVAssetWriter startWriting] Cannot call method when status is 3
參考資料:落影大佬的GPUImage文集