上一篇文章讲视频编解码的第一种情况,收到H.264格式的视频数据并在应用中播放。接下来进一步讲一下当收到视频数据播放同时可以得到每一帧图像。
这里我们不从AVSampleBufferDisplayLayer中的解码器中去获取每一帧图像,而是通过VTDecompressionSession这个类来获取,跟AVSampleBufferDisplayLayer一样,它也需要CMSampleBuffers做为输入。这个类会将CMSampleBuffers转化成CVPixelBuffers并且在代码块回调中提供。
在创建VTDecompressionSession前,需要CMVideoFormatDescription对象。如果是从Elementary Stream中解码的话,就从拥有parameter sets的NAL单元中获取。如果是解码CMSampleBuffer的话,就直接将CMVideoFormatDescription对象从中抽出来。接下来,我们需要使用pixelBufferAttribute字典来做为输出的pixelBuffers的描述。最后,我们需要实现VTDecompressionOutputCallback这个代码块回调。
当我们需要输出的pixelBuffers兼容OpenGL ES时,我们只需要创建一个CFDictionary或者NSDictionary指定kCVPixelBufferOpenGLESCompatibilityKey为true。
在输出回调中CVPixelBuffers是不带时间戳的,所以我们会获取到当前的时间戳。当发生错误或者丢帧时,我们都可以从回调方法中得到相应的错误信息。
使用VTDecompressionSessionDecodeFrame这个方法来为VTDecompressionSession提供Frames,这个方法默认是同步执行的。如果想要异步执行的话需要传入标志EnableAsynchronousDecompression。当异步执行这个方法时,解码器只能提供有限的管道来解码,所以当管道是满的时,方法将会阻塞。所以不要将UI层的代码放入这个方法当中执行。
当解码一段视频时,CMVideoFormatDescription会发生改变。当收到Elementary Stream时,我们根据第一个获取到的parameter sets来创建formatDescription,并且通过这个formatDescription来创建VTDecompressionSession来解码接下来的视频帧,直到收到新的parameter sets。这时,我们要创建新的formatDescription并且调用VTDecompressionSessionCanAcceptFormatDescription来确认decompressionSession是否能够在这些formaDescription中切换。如果不能切换的话,我们就需要创建新的VTDecompressionSession,并且确保旧的VTDecompressionSession被销毁。
总结一下,这片文章主要讲了:
1、如何创建VTDecompressionSession
2、如何做一个最优决策来创建pixelBuffer(通过pixelBufferAttribute字典来指定输出的需求)
3、如何同步或异步执行decompressionSession
4、当CMVideoFormatDescription变化时,如何处理
本文翻译自WWDC14:Direct Access to Video Encoding and Decoding