iOS GPUImage研究五:短視訊拍攝(濾鏡、檔案寫入)

iOS GPUImage研究五:短視訊拍攝(濾鏡、檔案寫入)

希望這個Demo,可以給大家在視訊濾鏡製作上帶來一些靈感、避免重複造輪子。也希望大家轉載的時候帶上原文地址,算是對原創的鼓勵。

最下方有Demo地址
執行環境Xcode7.3.1

這個Demo應該是對GPUImage的總結吧,包含了視訊寫入,濾鏡資訊讀取。前面幾篇文章也分別對GPUImage中常用的功能進行了分別介紹。雖說GPUImage是開源的,但對初學者來講上手還是有一定難度的,希望可以幫助大家。

在GPUImage的探索的道路上依舊還未停止。

這裡寫圖片描述

需求:

序號描述
1.實現畫面獲取:使用GPUImageMovieCamera
2.實現畫面呈現:使用GPUImageView
3.實現美顏濾鏡:包含讀取影象顏色矩陣,以及GPUImage自帶濾鏡組合和網上流傳的美顏濾鏡
4.實現實時濾鏡視訊處理:使用GPUImageMovieWriter
5.實現儲存到相簿,並播放。
6.簡單地切換動畫,讓過程看起來更自然。

效果如圖 :

這裡寫圖片描述偏微黃濾鏡![這裡寫圖片描述]

Demo目錄:

這裡寫圖片描述

相關類說明:
Frameworks是使用GPUImage的必須依賴庫。
GPUImageCustomLookupFilter是將讀取圖片資訊並將之轉化為濾鏡 的關鍵類。也包含一些我自己的濾鏡組合,包含魚眼濾鏡、水晶球濾鏡、放行濾鏡、浮雕、反魚眼等。
FSKVideoFilterVc是主控制器,包含切換攝像頭、視訊濾鏡寫入、視訊錄製播放、儲存相簿、切換濾鏡、以及組合濾鏡的使用方法。
SkVideoFilterView是濾鏡的檢視,繼承collectionview,一共包含大概30種組合濾鏡。
FilterModel是濾鏡Model,處理濾鏡名稱定義,以及濾鏡列表的初始化。
SSGPUImageBeautyFilter就是GPUImageBeautifulFilter,只是換了個名字而已。
MyLayout是一個繼承UICollectionViewFlowLayout的佈局類,一個是擁有縮放效果的佈局方式,後來我把縮放效果調成了0,因為綜合效果的原因。
 CGFloat distance = offset - attribute.center.x;
// 越往中心移動,值越小,那麼縮放就越小,從而顯示就越大
// 同樣,超過中心後,越往左、右走,縮放就越大,顯示就越小
CGFloat scaleForDistance = distance / self.itemSize.height;
// 可調整,值越大,顯示就越大
CGFloat scaleForCell = 1   0 * (1 - fabs(scaleForDistance));
// only scale y-axis
attribute.transform3D = CATransform3DMakeScale(1, scaleForCell, 1);
attribute.zIndex = 1;

可通過調節0 * (1 – fabs(scaleForDistance));來改變效果。

StoryBoard說明:

這裡寫圖片描述

storyboard具備快速開發的優點,除了實時變化相對於程式碼差一點,其他的他已經很成熟、也是偉大的開發模式、在這裡也希望那些老傢伙們別固執與程式碼佈局,緊隨時代吧!
特別提示:注意Mylayout的位置 很重要

這裡寫圖片描述

初始化相機和GPUImageview:

func setNewCameraAndGPUViewAndStart() {
videoCamera = GPUImageVideoCamera.init(sessionPreset: AVCaptureSessionPreset640x480, cameraPosition: AVCaptureDevicePosition.Front)
videoCamera.outputImageOrientation = UIInterfaceOrientation.Portrait
videoCamera.horizontallyMirrorFrontFacingCamera = true
videoCamera.horizontallyMirrorRearFacingCamera = true
filter = SSGPUImageBeautyFilter.init()
filterView = self.view as! GPUImageView
filterView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill
videoCamera.addTarget(filter as GPUImageInput)
filter.addTarget(filterView)
videoCamera.startCameraCapture()
}

初始化寫入物件:

func getNewMovieWriter() {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
let documentsDirectory = paths[0] as String
let filePath : String? = "\(documentsDirectory)/DBLiveTest\(currentTimeStamp()).mp4"
movieURL = NSURL(fileURLWithPath: filePath!)
movieWriter = GPUImageMovieWriter.init(movieURL: movieURL, size: CGSizeMake(480.0, 640.0))
movieWriter.encodingLiveVideo = true
if groupFilter == nil {
filter.addTarget(movieWriter)
}else{
self.groupFilter?.addTarget(movieWriter)
}
videoCamera.audioEncodingTarget = movieWriter
}

切換濾鏡

//換濾鏡
func switchFilter(index: Int) {
self.filter?.removeAllTargets()
self.groupFilter?.removeAllTargets()
self.currentFilterIndex = index
let lookupImageName = self.filterModel.filterList[index].lookupImageName
self.lookupFilter = GPUImageCustomLookupFilter.init(lookupImageName: lookupImageName)
self.setUpGroupFilters(self.lookupFilter!)
self.groupFilter?.addTarget(filterView)
self.groupFilter?.addTarget(movieWriter)
}
func setUpGroupFilters(lookupFilter: GPUImageCustomLookupFilter) {
self.groupFilter = GPUImageFilterGroup.init()
self.groupFilter?.addTarget(filter)
self.filter?.addTarget(lookupFilter)
self.groupFilter?.initialFilters = [self.filter!]
self.groupFilter?.terminalFilter = lookupFilter
}

播放視訊

func playVideoAndAddPreView() {
dispatch_async(dispatch_get_main_queue()) {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)
let documentsDirectory = paths[0] as String
let filePath1 : String? = "\(documentsDirectory)/DBLiveTest\(currentTimeStamp()).jpg";
UIImagePNGRepresentation(getFirstImageFromVideo(self.movieURL))?.writeToFile(filePath1!, atomically: true);
self.thumbURL = NSURL(fileURLWithPath: filePath1!)
animationFlipFromRight(self.filterView)
self.videoCamera.stopCameraCapture()
self.playerItem = AVPlayerItem(URL: self.movieURL)
// 建立 AVPlayer 播放器
self.player = AVPlayer(playerItem: self.playerItem)
// 將 AVPlayer 新增到 AVPlayerLayer 上
self.playerLayer = AVPlayerLayer(player: self.player)
// 設定播放頁面大小
self.playerLayer.frame = self.view.frame
// 設定畫面縮放模式
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
// 在檢視上新增播放器
self.view.layer.addSublayer(self.playerLayer)
// 開始播放
self.player.play()
self.addPreViewToSufureView()
}
}

儲存到相簿:

func doYouWantSaveItToAlbum(bool:Bool) {
if bool == true {
let library = ALAssetsLibrary()
if library.videoAtPathIsCompatibleWithSavedPhotosAlbum(movieURL) {
library.writeVideoAtPathToSavedPhotosAlbum(movieURL, completionBlock: { (assetURL, error) in
dispatch_async(dispatch_get_main_queue(), {
if error == nil{
let alert = UIAlertView.init(title: "提示訊息", message: "儲存成功", delegate: nil, cancelButtonTitle: "確定")
alert.show()
}else{
let alert = UIAlertView.init(title: "提示訊息", message: "儲存失敗", delegate: nil, cancelButtonTitle: "確定")
alert.show()
}
})
})
}
}
}

用到的全域性方法:

//翻轉動畫
func animationFlipFromLeft(view:UIView) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
UIView.setAnimationDuration(0.5)
UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: view, cache: false)
UIView.commitAnimations()
}
//翻轉動畫
func animationFlipFromRight(view:UIView) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
UIView.setAnimationDuration(0.5)
UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromRight, forView: view, cache: false)
UIView.commitAnimations()
}
//時間戳
func currentTimeStamp()->String {
let now = NSDate()
let dformatter = NSDateFormatter()
dformatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
print("當前日期時間:\(dformatter.stringFromDate(now))")
//當前時間的時間戳
let timeInterval:NSTimeInterval = now.timeIntervalSince1970
let timeStamp = Int(timeInterval)
return String(timeStamp)
}
//獲取視訊第一幀
func getFirstImageFromVideo(videoUrl:NSURL)->(UIImage){
let asset = AVURLAsset(URL:videoUrl)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform=true
let time = CMTimeMakeWithSeconds(0.0,600)
var actualTime:CMTime = CMTimeMake(0,0)
var image:CGImageRef!
do{
image = try generator.copyCGImageAtTime(time, actualTime: &actualTime)
}catch let error as NSError{
print(error)
}
return UIImage(CGImage: image)
}

模糊效果:

//新增模糊效果
func addVisualEffectView() {
self.effectView = UIVisualEffectView.init(effect: UIBlurEffect.init(style: UIBlurEffectStyle.Light));
self.view.addSubview(self.effectView)
self.effectView.frame = self.view.bounds
}

Demo 地址:http://download.csdn.net/my