(强烈推荐)移动端音视频从零到上手
零基础学习音视频-----编码篇(VideoToolBox)
iOS-VideoToolbox硬编码H264
使用VideoToolbox硬编码H.264
iOS编码h264
Video Toolbox视频编解码前后需要应用的数据结构进行说明。
(1)CVPixelBuffer:编码前和解码后的图像数据结构。
(2)CMTime、CMClock和CMTimebase:时间戳相关。时间以64-bit/32-bit的形式出现。
(3)CMBlockBuffer:编码后,结果图像的数据结构。
(4)CMVideoFormatDescription:图像存储方式,编解码器等格式描述。
(5)CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
参考链接:iOS8系统H264视频硬件编解码说明
调用 AVCaptureSession 拍摄输出的每一帧图像都会被包装成 CMSampleBuffer 对象,通过这个 CMSampleBuffer 对象你就可以获取到未压缩的 CVPixelBuffer 对象;如果读取 H.264 文件你也可以获取数据生成压缩的 CMBlockBuffer 对象并创建一个 CMSampleBuffer 对象给 VideoToolbox 来解码。
一个典型的 Elementary Stream 格式的 H.264 裸流如图:
参考链接:(推荐阅读)H264, H265硬件编解码基础及码流分析
VideoToolbox是iOS8以后开放的硬编码与硬解码的API,一组用C语言写的函数。使用VideoToolbox编码流程如下:
- 1、-initVideoToolBox中调用VTCompressionSessionCreate创建编码session,然后调用VTSessionSetProperty设置参数,最后调用VTCompressionSessionPrepareToEncodeFrames开始编码;
- 2、开始视频录制,获取到摄像头的视频帧,传入-encode:,调用VTCompressionSessionEncodeFrame传入需要编码的视频帧,如果返回失败,调用VTCompressionSessionInvalidate销毁session,然后释放session;
- 3、每一帧视频编码完成后会调用预先设置的编码函数didCompressH264,如果是关键帧需要用CMSampleBufferGetFormatDescription获取CMFormatDescriptionRef,然后用 CMVideoFormatDescriptionGetH264ParameterSetAtIndex取得PPS和SPS;最后把每一帧的所有NALU数据前四个字节变成0x00 00 00 01之后再写入文件;
- 4、调用VTCompressionSessionCompleteFrames完成编码,然后销毁session:VTCompressionSessionInvalidate,释放session。
参考链接:使用VideoToolbox硬编码H.264
FFmpeg parse流程
- 创建format context: avformat_alloc_context
- 打开文件流: avformat_open_input
- 寻找流信息: avformat_find_stream_info
- 获取音视频流的索引值: formatContext->streams[i]->codecpar->codec_type == (isVideoStream ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO)
- 获取音视频流: m_formatContext->streams[m_audioStreamIndex]
- 解析音视频数据帧: av_read_frame
- 获取extra data: av_bitstream_filter_filter
VideoToolbox decode流程 - 比较上一次的extra data,如果数据更新需要重新创建解码器
- 分离并保存FFmpeg parse到的extra data中分离vps, sps, pps等关键信息 (比较NALU头)
- 通过CMVideoFormatDescriptionCreateFromH264ParameterSets,CMVideoFormatDescriptionCreateFromHEVCParameterSets装载vps,sps,pps等NALU header信息.
- 指定解码器回调函数与解码后视频数据类型(yuv,RGB…)
- 创建解码器VTDecompressionSessionCreate
- 生成CMBlockBufferRef装载解码前数据,再将其转为CMSampleBufferRef以提供给解码器.
- 开始解码VTDecompressionSessionDecodeFrame
- 在回调函数中CVImageBufferRef即为解码后的数据,可转为CMSampleBufferRef传出.
参考链接:iOS利用VideoToolbox实现视频硬解码
- VTCompressionCreate的Callback是一个C语言的函数指针类型, 因此无法使用[self function]的方法, C语言看不懂self, 在呼叫callback時要把整个class传入。
H264Encode* encoder = (__bridge H264Encode*)outputCallbackRefCon;踩坑记录
- 使用appendSampleBuffer写入文件时,无法写入视频。调试发现isReadyForMoreMediaData返回false
if([self.videoWriterInput isReadyForMoreMediaData]){
[self.videoWriterInput appendSampleBuffer:sampleBuffer];
}
排查发现,苹果官方文件中有这样一段描述
Passing nil for output settings instructs the input to pass through
appended samples, doing no processing before they are written to the
output file. This is useful if, for example, you are appending buffers
that are already in a desirable compressed format. However, if not
writing to a QuickTime Movie file (i.e. the AVAssetWriter was
initialized with a file type other than AVFileTypeQuickTimeMovie),
AVAssetWriter only supports passing through a restricted set of media
types and subtypes. In order to pass through media data to files other
than AVFileTypeQuickTimeMovie, a non-NULL format hint must be provided
using +assetWriterInputWithMediaType:outputSettings:sourceFormatHint:
instead of this method.
因此,当使用[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:nil];时,应配合下列代码使用。原代码错误在于fileType的参数设置成了AVFileTypeMPEG4,应改为AVFileTypeQuickTimeMovie
self.assetWriter = [AVAssetWriter assetWriterWithURL:outputUrl fileType:AVFileTypeQuickTimeMovie error:nil];



