GIF + 視頻采集 + GPUImage + 視頻編碼
播放 Gif 圖
需要使用
ImageIO
框架 思路:
1.Gif 圖 ->CGImageSource
->[UIImage]
2.取出每張圖片的時間計算總時間
3.設置imageView.animationImages
屬性
4.imageView.startAnimating()
// 1.加載Gif圖片,并轉化為Data類型
guard let path = Bundle.main.path(forResource: "demo.gif", ofType: nil) else {
return
}
guard let data = NSData(contentsOfFile: path) else {
return
}
// 2.從data中讀取數據:將data轉場CGImageSource對象
guard let imageSource = CGImageSourceCreateWithData(data, nil) else {
return
}
let imageCount = CGImageSourceGetCount(imageSource)
// 3.遍歷所有照片
var images = [UIImage]()
var totalDuration: TimeInterval = 0
for i in 0..<imageCount {
// 3.1.取出圖片
guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, nil) else {
// 這一幀圖片有問題直接執行下一幀
continue
}
let image = UIImage(cgImage: cgImage)
if i == 0 {
imageView.image = image
}
images.append(image)
// 3.2.取出持續時間
guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) as? NSDictionary else {
continue
}
guard let gifDict = properties[kCGImagePropertyGIFDictionary] as? NSDictionary else {
continue
}
guard let frameDuration = gifDict[kCGImagePropertyGIFDelayTime] as? NSNumber else {
continue
}
totalDuration += frameDuration.doubleValue
}
// 4.設置imageView的屬性
imageView.animationImages = images
imageView.animationDuration = totalDuration
imageView.animationRepeatCount = 1
// 5.播放
imageView.startAnimating()
視頻采集
用到
AVFoundation
的AVCaptureSession
- 首先定義一個
seesion
屬性
fileprivate lazy var session: AVCaptureSession = AVCaptureSession()
- 初始化視頻的輸入&輸出
// 1.添加視頻輸入
let devices = AVCaptureDevice.devices()
// 選擇前置攝像頭
guard let device = devices.filter({ $0.position == .front }).first else {
return
}
guard let input = try? AVCaptureDeviceInput(device: device) else {
return
}
// 2.創建視頻輸出
let output = AVCaptureVideoDataOutput()
let queue = DispatchQueue.global()
// 設置代理
output.setSampleBufferDelegate(self, queue: queue)
// 3.添加輸入&輸出
session.beginConfiguration()
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
- 初始化音頻的輸入&輸出
// 1.創建音頻輸入
guard let devices = AVCaptureDevice.default(for: .audio) else {
return
}
guard let input = try? AVCaptureDeviceInput(device: devices) else {
return
}
// 2.創建音頻輸出
let output = AVCaptureAudioDataOutput()
let queue = DispatchQueue.global()
output.setSampleBufferDelegate(self, queue: queue)
// 3.添加輸入&輸出
session.beginConfiguration()
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
- 遵守輸入輸出代理協議
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
// 監聽每一幀畫面
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
print("采集視頻")
}
}
- 初始化一個預覽圖層
// 1.創建預覽圖層
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
// 2.設置previewLayer屬性
previewLayer.frame = view.bounds
self.previewLayer = previewLayer
// 3.將圖層添加到控制器的View的layer中
view.layer.insertSublayer(previewLayer, at: 0)
- 轉換攝像頭
// 1.取出之前鏡頭的方向
guard let videoInput = videoInput else {
return
}
let postion: AVCaptureDevice.Position = videoInput.device.position == .front ? .back : .front
// 2.重新設置視頻輸入
let devices = AVCaptureDevice.devices()
guard let device = devices.filter({ $0.position == postion }).first else {
return
}
guard let newInput = try? AVCaptureDeviceInput(device: device) else {
return
}
// 3.移除之前的input, 添加新的input
session.beginConfiguration()
session.removeInput(videoInput)
if session.canAddInput(newInput) {
session.addInput(newInput)
}
session.commitConfiguration()
// 4.保存最新的input
self.videoInput = newInput
- 錄制視頻存入本地沙盒(替換視頻輸出)
fileprivate func setupMovieFileOutput() {
guard let output = videoOutput else {
return
}
session.removeOutput(output)
// 1.創建寫入文件的輸出
let fileOutout = AVCaptureMovieFileOutput()
movieOutput = fileOutout
let connection = fileOutout.connection(with: .video)
connection?.automaticallyAdjustsVideoMirroring = true
if session.canAddOutput(fileOutout) {
session.addOutput(fileOutout)
}
// 2.直接開始寫入文件
let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/abc.mp4"
videoPath = filePath
let fileURL = URL(fileURLWithPath: filePath)
// 設置代理
fileOutout.startRecording(to: fileURL, recordingDelegate: self)
}
// MARK: - 通過代理監聽開始寫入文件,以及結束寫入文件
extension ViewController: AVCaptureFileOutputRecordingDelegate {
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
print("開始寫入文件")
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print("完成寫入文件")
}
}
美顏濾鏡效果
GPUImage
- GPUImage 是一個開源的基于GPU的圖片或視頻的處理框架,其本身內置了多達120多種常見的濾鏡效果
- GPUImage是利用GPU,使在圖片和視頻上應用不同的效果和濾鏡變得非常的容易,同時它還擁有出色的性能,并且它的性能要比蘋果內置的相關APIs出色
GPUImage使用 - 高斯模糊效果
1.UIToolBar本身有毛玻璃效果
2.iOS8之后UIVisualEffectView直接創建毛玻璃View
3.系統CoreImage框架中直接修改圖片
4.GPUImage框架給圖片添加一層濾鏡總結:前兩種在照片上面加一層View,后兩種方法直接修改圖片
-
實現思路
- 獲取要修改成毛玻璃的圖片
- 給圖片添加濾鏡
- 生成新的圖片
實現代碼
// 1.獲取修改的圖片
let sourceImage = UIImage(named: "test")!
// 2.使用GPUImage高斯模糊效果
// 2.1.如果是對圖像進行處理GPUImagePicture
let picProcess = GPUImagePicture(image: sourceImage)
// 2.2.添加需要處理的濾鏡
let blurFilter = GPUImageGaussianBlurFilter()
// 紋理
blurFilter.texelSpacingMultiplier = 5
blurFilter.blurRadiusInPixels = 5
picProcess?.addTarget(blurFilter)
// 2.3.處理照片
blurFilter.useNextFrameForImageCapture()
picProcess?.processImage()
// 2.4.取出最新的照片
let newImage = blurFilter.imageFromCurrentFramebuffer()
// 3.顯示最新的照片
imageView.image = newImage
其它濾鏡效果
- 多個target依賴相同的庫(Ruby語法)
platform :ios, '9.0'
use_frameworks!
targetsArray = ['01-GPUImage毛玻璃', '02-GPUImage其他濾鏡']
targetsArray.each do |t|
target t do
pod 'GPUImage'
end
end
- 常見濾鏡
#pragma mark - 調整顏色 Handle Color
#import "GPUImageBrightnessFilter.h" //亮度
#import "GPUImageExposureFilter.h" //曝光
#import "GPUImageContrastFilter.h" //對比度
#import "GPUImageSaturationFilter.h" //飽和度
#import "GPUImageGammaFilter.h" //伽馬線
#import "GPUImageColorInvertFilter.h" //反色
#import "GPUImageSepiaFilter.h" //褐色(懷舊)
#import "GPUImageLevelsFilter.h" //色階
#import "GPUImageGrayscaleFilter.h" //灰度
#import "GPUImageHistogramFilter.h" //色彩直方圖,顯示在圖片上
#import "GPUImageHistogramGenerator.h" //色彩直方圖
#import "GPUImageRGBFilter.h" //RGB
#import "GPUImageToneCurveFilter.h" //色調曲線
#import "GPUImageMonochromeFilter.h" //單色
#import "GPUImageOpacityFilter.h" //不透明度
#import "GPUImageHighlightShadowFilter.h" //提亮陰影
#import "GPUImageFalseColorFilter.h" //色彩替換(替換亮部和暗部色彩)
#import "GPUImageHueFilter.h" //色度
#import "GPUImageChromaKeyFilter.h" //色度鍵
#import "GPUImageWhiteBalanceFilter.h" //白平橫
#import "GPUImageAverageColor.h" //像素平均色值
#import "GPUImageSolidColorGenerator.h" //純色
#import "GPUImageLuminosity.h" //亮度平均
#import "GPUImageAverageLuminanceThresholdFilter.h" //像素色值亮度平均,圖像黑白(有類似漫畫效果)
#import "GPUImageLookupFilter.h" //lookup 色彩調整
#import "GPUImageAmatorkaFilter.h" //Amatorka lookup
#import "GPUImageMissEtikateFilter.h" //MissEtikate lookup
#import "GPUImageSoftEleganceFilter.h" //SoftElegance lookup
#pragma mark - 圖像處理 Handle Image
#import "GPUImageCrosshairGenerator.h" //十字
#import "GPUImageLineGenerator.h" //線條
#import "GPUImageTransformFilter.h" //形狀變化
#import "GPUImageCropFilter.h" //剪裁
#import "GPUImageSharpenFilter.h" //銳化
#import "GPUImageUnsharpMaskFilter.h" //反遮罩銳化
#import "GPUImageFastBlurFilter.h" //模糊
#import "GPUImageGaussianBlurFilter.h" //高斯模糊
#import "GPUImageGaussianSelectiveBlurFilter.h" //高斯模糊,選擇部分清晰
#import "GPUImageBoxBlurFilter.h" //盒狀模糊
#import "GPUImageTiltShiftFilter.h" //條紋模糊,中間清晰,上下兩端模糊
#import "GPUImageMedianFilter.h" //中間值,有種稍微模糊邊緣的效果
#import "GPUImageBilateralFilter.h" //雙邊模糊
#import "GPUImageErosionFilter.h" //侵蝕邊緣模糊,變黑白
#import "GPUImageRGBErosionFilter.h" //RGB侵蝕邊緣模糊,有色彩
#import "GPUImageDilationFilter.h" //擴展邊緣模糊,變黑白
#import "GPUImageRGBDilationFilter.h" //RGB擴展邊緣模糊,有色彩
#import "GPUImageOpeningFilter.h" //黑白色調模糊
#import "GPUImageRGBOpeningFilter.h" //彩色模糊
#import "GPUImageClosingFilter.h" //黑白色調模糊,暗色會被提亮
#import "GPUImageRGBClosingFilter.h" //彩色模糊,暗色會被提亮
#import "GPUImageLanczosResamplingFilter.h" //Lanczos重取樣,模糊效果
#import "GPUImageNonMaximumSuppressionFilter.h" //非最大抑制,只顯示亮度最高的像素,其他為黑
#import "GPUImageThresholdedNonMaximumSuppressionFilter.h" //與上相比,像素丟失更多
#import "GPUImageSobelEdgeDetectionFilter.h" //Sobel邊緣檢測算法(白邊,黑內容,有點漫畫的反色效果)
#import "GPUImageCannyEdgeDetectionFilter.h" //Canny邊緣檢測算法(比上更強烈的黑白對比度)
#import "GPUImageThresholdEdgeDetectionFilter.h" //閾值邊緣檢測(效果與上差別不大)
#import "GPUImagePrewittEdgeDetectionFilter.h" //普瑞維特(Prewitt)邊緣檢測(效果與Sobel差不多,貌似更平滑)
#import "GPUImageXYDerivativeFilter.h" //XYDerivative邊緣檢測,畫面以藍色為主,綠色為邊緣,帶彩色
#import "GPUImageHarrisCornerDetectionFilter.h" //Harris角點檢測,會有綠色小十字顯示在圖片角點處
#import "GPUImageNobleCornerDetectionFilter.h" //Noble角點檢測,檢測點更多
#import "GPUImageShiTomasiFeatureDetectionFilter.h" //ShiTomasi角點檢測,與上差別不大
#import "GPUImageMotionDetector.h" //動作檢測
#import "GPUImageHoughTransformLineDetector.h" //線條檢測
#import "GPUImageParallelCoordinateLineTransformFilter.h" //平行線檢測
#import "GPUImageLocalBinaryPatternFilter.h" //圖像黑白化,并有大量噪點
#import "GPUImageLowPassFilter.h" //用于圖像加亮
#import "GPUImageHighPassFilter.h" //圖像低于某值時顯示為黑
#pragma mark - 視覺效果 Visual Effect
#import "GPUImageSketchFilter.h" //素描
#import "GPUImageThresholdSketchFilter.h" //閥值素描,形成有噪點的素描
#import "GPUImageToonFilter.h" //卡通效果(黑色粗線描邊)
#import "GPUImageSmoothToonFilter.h" //相比上面的效果更細膩,上面是粗曠的畫風
#import "GPUImageKuwaharaFilter.h" //桑原(Kuwahara)濾波,水粉畫的模糊效果;處理時間比較長,慎用
#import "GPUImageMosaicFilter.h" //黑白馬賽克
#import "GPUImagePixellateFilter.h" //像素化
#import "GPUImagePolarPixellateFilter.h" //同心圓像素化
#import "GPUImageCrosshatchFilter.h" //交叉線陰影,形成黑白網狀畫面
#import "GPUImageColorPackingFilter.h" //色彩丟失,模糊(類似監控攝像效果)
#import "GPUImageVignetteFilter.h" //暈影,形成黑色圓形邊緣,突出中間圖像的效果
#import "GPUImageSwirlFilter.h" //漩渦,中間形成卷曲的畫面
#import "GPUImageBulgeDistortionFilter.h" //凸起失真,魚眼效果
#import "GPUImagePinchDistortionFilter.h" //收縮失真,凹面鏡
#import "GPUImageStretchDistortionFilter.h" //伸展失真,哈哈鏡
#import "GPUImageGlassSphereFilter.h" //水晶球效果
#import "GPUImageSphereRefractionFilter.h" //球形折射,圖形倒立
#import "GPUImagePosterizeFilter.h" //色調分離,形成噪點效果
#import "GPUImageCGAColorspaceFilter.h" //CGA色彩濾鏡,形成黑、淺藍、紫色塊的畫面
#import "GPUImagePerlinNoiseFilter.h" //柏林噪點,花邊噪點
#import "GPUImage3x3ConvolutionFilter.h" //3x3卷積,高亮大色塊變黑,加亮邊緣、線條等
#import "GPUImageEmbossFilter.h" //浮雕效果,帶有點3d的感覺
#import "GPUImagePolkaDotFilter.h" //像素圓點花樣
#import "GPUImageHalftoneFilter.h" //點染,圖像黑白化,由黑點構成原圖的大致圖形
#pragma mark - 混合模式 Blend
#import "GPUImageMultiplyBlendFilter.h" //通常用于創建陰影和深度效果
#import "GPUImageNormalBlendFilter.h" //正常
#import "GPUImageAlphaBlendFilter.h" //透明混合,通常用于在背景上應用前景的透明度
#import "GPUImageDissolveBlendFilter.h" //溶解
#import "GPUImageOverlayBlendFilter.h" //疊加,通常用于創建陰影效果
#import "GPUImageDarkenBlendFilter.h" //加深混合,通常用于重疊類型
#import "GPUImageLightenBlendFilter.h" //減淡混合,通常用于重疊類型
#import "GPUImageSourceOverBlendFilter.h" //源混合
#import "GPUImageColorBurnBlendFilter.h" //色彩加深混合
#import "GPUImageColorDodgeBlendFilter.h" //色彩減淡混合
#import "GPUImageScreenBlendFilter.h" //屏幕包裹,通常用于創建亮點和鏡頭眩光
#import "GPUImageExclusionBlendFilter.h" //排除混合
#import "GPUImageDifferenceBlendFilter.h" //差異混合,通常用于創建更多變動的顏色
#import "GPUImageSubtractBlendFilter.h" //差值混合,通常用于創建兩個圖像之間的動畫變暗模糊效果
#import "GPUImageHardLightBlendFilter.h" //強光混合,通常用于創建陰影效果
#import "GPUImageSoftLightBlendFilter.h" //柔光混合
#import "GPUImageChromaKeyBlendFilter.h" //色度鍵混合
#import "GPUImageMaskFilter.h" //遮罩混合
#import "GPUImageHazeFilter.h" //朦朧加暗
#import "GPUImageLuminanceThresholdFilter.h" //亮度閾
#import "GPUImageAdaptiveThresholdFilter.h" //自適應閾值
#import "GPUImageAddBlendFilter.h" //通常用于創建兩個圖像之間的動畫變亮模糊效果
#import "GPUImageDivideBlendFilter.h" //通常用于創建兩個圖像之間的動畫變暗模糊效果
美顏相機
-
實現思路
- 創建相機
GPUImageStillCamera
- 創建濾鏡
GPUImageFilter
- 創建
GPUImageView
顯示實時畫面 - 捕捉畫面
- 拍照保存到相冊
- 結束捕捉畫面
- 創建相機
實現代碼
// 1.創建GPUImageStillCamera
var camera:GPUImageStillCamera = GPUImageStillCamera(sessionPreset: AVCaptureSession.Preset.high, cameraPosition: .front)
// 設置相機的方向
camera.outputImageOrientation = .portrait
// 2.創建濾鏡(美白)
var filter: GPUImageFilter = GPUImageBrightnessFilter()
filter.brightness = 0.5
camera.addTarget(filter)
// 3.創建GPUImageView,用于顯示實時畫面
let showView = GPUImageView(frame: view.bounds)
view.insertSubview(showView, at: 0)
filter.addTarget(showView)
// 4.開始捕捉畫面
camera.startCapture()
// 拍照
camera.capturePhotoAsImageProcessedUp(toFilter: filter) { (image, error) in
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
camera.stopCapture()
}
實時畫面
- 遵守
GPUImageVideoCameraDelegate
j監聽每一幀的畫面
func willOutputSampleBuffer(_ sampleBuffer: CMSampleBuffer!) {
print("采集到畫面")
}
斗魚直播畫面 (多個濾鏡)
- 一次性使用多個濾鏡 先創建濾鏡組
GPUImageFilterGroup
// 1.創建濾鏡組(用于存放各種濾鏡:美白、磨皮等等)
let filterGroup = GPUImageFilterGroup()
// 2.創建濾鏡(設置濾鏡的引來關系)
bilateralFilter.addTarget(brightnessFilter)
brightnessFilter.addTarget(exposureFilter)
exposureFilter.addTarget(satureationFilter)
// 3.設置濾鏡組鏈初始&終點的filter
filterGroup.initialFilters = [bilateralFilter]
filterGroup.terminalFilter = satureationFilter
// 4.設置GPUImage的響應鏈
camera?.addTarget(filterGroup)
視頻編碼
為什么進行壓縮編碼?
- 視頻是由一幀幀的圖像組成
- 未經壓縮的視頻的數據量巨大
為什么視頻可以壓縮編碼?
- 存在冗余信息
- 空間冗余:圖像相鄰像素之間有較強的相關性
- 時間冗余:視頻序列的相鄰圖像之間內容相似
- 視覺冗余:人的視覺系統對某些細節不敏感
目前應用最廣泛的H.264(AVC)
- H264是新一代的編碼標準,以高壓縮高質量和支持多種網絡的流媒體傳輸著稱
編碼方式
-
編碼的方式有兩種:
- 硬編碼:使用非CPU進行編碼,如顯卡GPU、專用的DSP、FPGA、ASIC芯片等
- 軟編碼:使用CPU進行編碼,軟編碼通常使用:ffmpeg+x264
-
對比
- 軟編碼:實現直接、簡單,參數調整方便,升級易,但CPU負載重,性能較硬編碼低
- 硬編碼:性能高,對CPU沒有壓力,但是對其他硬件要求較高(如GPU等)
-
iOS中編碼方式:
- 在iOS8之前,蘋果并沒有開放硬編碼的接口,所以只能采用ffpeng+x624進行軟編碼
- 在iOS8之后,蘋果開放了接口,并且封裝了VideoToolBox&AudioToolbox兩個框架,分別用于對視頻&音頻進行硬編碼