手机上可以通过imageView作为图像的载体对一副图像进行显示。另一方面,在iOS端图像处理中,可以通过GPU对图像进行处理和渲染,并且通过metal框架,可以将处理后的图像直接显示在MTKView上。但是如果不做复杂的处理或者更加有利于将处理后的图像与MTKView更好的解耦,如何将图像进行显示了呢?一方面我们可以将CVpixelbuffer的图像数据个是转换成UIImage,另一方面,在iOS8之后,可以通过AVSampleBufferDisplayLayer这个图层进行显示。猛一看这个类,让我想起了AVPlayer()这个最为基础的iOS端的播放神器,它就是通过AVPlayerLayer()图层进行图像的显示。
通过AVSampleBufferDisplayLayer这个类对图像进行渲染显示,需要改图像的格式为CMSampleBuffer。尽管和视频编解码接触的不多,但是知道CMSampleBuffer的图像格式和编解码联系紧密,因为其内部包含了PTS和DTS等相关信息。当有了CMSampleBuffer图像格式的数据,通过调用AVSampleBufferDisplayLayer的enqueue(_ sampleBuffer: CMSampleBuffer)方法,即可将该帧的图像进行显示。
var sampleBufferLayer = AVSampleBufferDisplayLayer()
AVSampleBufferDisplayLayer比较容易理解,直接对该图层包括frame在内的一些属性进行设置后,即可调用方法。
self.sampleBufferLayer.frame = self.view.bounds
self.sampleBufferLayer.videoGravity = AVLayerVideoGravity.resizeAspect
self.sampleBufferLayer.isOpaque = true
self.view.layer.addSublayer(self.sampleBufferLayer)
但是难点在于如何将CVPixeBuffer的图像格式转换成CMSampleBuffer。网络上现在OC版本挺多的,但是swift暂时还很缺少,这里就提供一下:
func displayPixelBuffer(pixelBuffer : CVPixelBuffer){
var newSampleBuffer: CMSampleBuffer? = nil
var timimgInfo: CMSampleTimingInfo = CMSampleTimingInfo.invalid
var videoInfo: CMVideoFormatDescription? = nil
CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: pixelBuffer, formatDescriptionOut: &videoInfo)
CMSampleBufferCreateForImageBuffer(allocator: kCFAllocatorDefault, imageBuffer: pixelBuffer, dataReady: true, makeDataReadyCallback: nil, refcon: nil, formatDescription: videoInfo!, sampleTiming: &timimgInfo, sampleBufferOut: &newSampleBuffer)
self.setSampleBufferAttachments(newSampleBuffer!)
self.sampleBufferLayer.enqueue(newSampleBuffer!)
}
其中的setSampleBufferAttachments函数为设置CMSampleBuffer内部的一些属性。因为这里不涉及到视频的解码信息,所有不必设置解码时间DTS和渲染时间PTS。并且也包含其他的一些属性设置。
func setSampleBufferAttachments(_ sampleBuffer: CMSampleBuffer) {
let attachments: CFArray! = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true)
let dictionary = unsafeBitCast(CFArrayGetValueAtIndex(attachments, 0),
to: CFMutableDictionary.self)
let key = Unmanaged.passUnretained(kCMSampleAttachmentKey_DisplayImmediately).toOpaque()
let value = Unmanaged.passUnretained(kCFBooleanTrue).toOpaque()
CFDictionarySetValue(dictionary, key, value)
}
到了这里,图像应该能够正常显示。但是bug也不会少了,比如,当进入后台等一些事件导致渲染中断,再次进入该渲染时间后,你会发现图象不见了。网上如何解决这类bug的博客不在少数,这里就不在介绍。