视频流的解码分为软解和硬解。软解:利用CPU做视频的编码和解码,俗称软编软解。这个方法比较通用,但是占用CPU资源,编解码的效率不高。硬解:一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码。简称硬编解码,调用VideoToolbox的框架进行基本数据结构。编解码前后的视频图像均封装在CMSampleBuffer中,如果是编码后的图像,以CMBlockBuffe方式存储;解码后的图像,以CVPixelBuffer存储。CMSampleBuffer里面还有另外的时间信息CMTime和视频描述信息CMVideoFormatDesc。
--
苹果自带的框架只有在ios8.0才能支持硬编解码 (VideoToolbox.Framework),
解码主要需要以下三个函数
VTDecompressionSessionCreate 创建解码session
VTDecompressionSessionDecodeFrame 解码一个frame
VTDecompressionSessionInvalidate 销毁解码 session
- 编解码前后的视频图像均封装在CMSampleBuffer中,如果是编码后的图像,以CMBlockBuffe方式存储;解码后的图像,以CVPixelBuffer存储。CMSampleBuffer里面还有另外的时间信息CMTime和视频描述信息CMVideoFormatDesc。
硬解码使用方法。
-
将H264码流转换成解码前的CMSampleBuffer:解码前的CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer。需要从H264的码流里面提取出以上的三个信息。最后组合成CMSampleBuffer,提供给硬解码接口来进行解码工作。H264的码流由NALU单元组成,NALU单元包含视频图像数据和H264的参数信息。其中视频图像数据就是CMBlockBuffer,而H264的参数信息则可以组合成FormatDesc。具体来说参数信息包含SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)。
-
码流结构:提取sps和pps生成format description。
a,每个NALU的开始码是0x00 00 01,按照开始码定位NALU。
b,通过类型信息找到sps和pps并提取,开始码后第一个byte的后5 位,7代表sps,8代表pps。
c,CMVideoFormatDescriptionCreateFromH264ParameterSets函 数来构建CMVideoFormatDescriptionRef。具体代码可以见demo。
-
提取视频图像数据生成CMBlockBuffer。
a,通过开始码,定位到NALU。
b,确定类型为数据后,将开始码替换成NALU的长度信息(4 Bytes)。
c,CMBlockBufferCreateWithMemoryBlock接口构造CMBlockBufferRef。具体代码可以见demo。
-
根据需要,生成CMTime信息。(实际测试时,加入time信息后,有不稳定的图像,不加入time信息反而没有,需要进一步研究,这里建议不加入time信息)
根据上述得到CMVideoFormatDescriptionRef、CMBlockBufferRef和可选的时间信息,使用CMSampleBufferCreate接口得到CMSampleBuffer数据这个待解码的原始的数据。
网友评论
{
if(_bufferSize < _bufferCap && self.fileStream.hasBytesAvailable) {
NSInteger readBytes = [self.fileStream read:_buffer + _bufferSize maxLength:_bufferCap - _bufferSize];
_bufferSize += readBytes;
}
if(memcmp(_buffer, KStartCode, 4) != 0) {
return nil;
}
if(_bufferSize >= 5) {
uint8_t *bufferBegin = _buffer + 4;
uint8_t *bufferEnd = _buffer + _bufferSize;
while(bufferBegin != bufferEnd) {
if(*bufferBegin == 0x01) {
if(memcmp(bufferBegin - 3, KStartCode, 4) == 0) {
NSInteger packetSize = bufferBegin - _buffer - 3;
VideoPacket *vp = [[VideoPacket alloc] initWithSize:packetSize];
memcpy(vp.buffer, _buffer, packetSize);
memmove(_buffer, _buffer + packetSize, _bufferSize - packetSize);
_bufferSize -= packetSize;
return vp;
}
}
++bufferBegin;
}
}
return nil;
}
这个方法能详细说一下嘛,fileStream一次读取的是一个序列吗??
while循环里边面的我加了个nslog就不出视频,,也不知道怎么回事