您的位置:首页 > 理论基础 > 计算机网络

[置顶] 基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264

2017-10-12 15:34 633 查看


下载

GitHub:

client 端:https://github.com/AmoAmoAmo/Smart_Device_Client

server端:https://github.com/AmoAmoAmo/Smart_Device_Server

另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac

使用VideoToolbox硬解码H264。

关于这一部分,由于是第一次使用,便不班门弄斧,

在参考文章中列举了不少说得很详细的博客。

关于最终的效果,可以参考第一篇的文章

部分过程


初始化编码器session

- (void)initVideoToolBox {
if (!mDecodeSession) {
// 把SPS和PPS包装成CMVideoFormatDescription
const uint8_t* parameterSetPointers[2] = {mSPS, mPPS};
const size_t parameterSetSizes[2] = {mSPSSize, mPPSSize};
OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
2, //param count
parameterSetPointers,
parameterSetSizes,
4, //nal start code size
&mFormatDescription);
if(status == noErr) {
CFDictionaryRef attrs = NULL;
const void *keys[] = { kCVPixelBufferPixelFormatTypeKey };
//      kCVPixelFormatType_420YpCbCr8Planar is YUV420
//      kCVPixelFormatType_420YpCbCr8BiPlanarFullRange is NV12
uint32_t v = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
const void *values[] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &v) };
attrs = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

VTDecompressionOutputCallbackRecord callBackRecord;
callBackRecord.decompressionOutputCallback = didDecompress;
callBackRecord.decompressionOutputRefCon = NULL;

status = VTDecompressionSessionCreate(kCFAllocatorDefault,
mFormatDescription,
NULL, attrs,
&callBackRecord,
&mDecodeSession);
CFRelease(attrs);
} else {
NSLog(@"IOS8VT: reset decoder session failed status = %d", (int)status);
}
}
}

回调函数

void didDecompress(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){

CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
*outputPixelBuffer = CVPixelBufferRetain(pixelBuffer);
}


获取H.264开始码,并开始相应的工作

// 一收到数据就调用这个方法,用来刷新屏幕
- (void)updateFrame
{
// 同步 --》 顺序执行
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 替换头字节长度
uint32_t nalSize = (uint32_t)(packetSize - 4);
uint32_t *pNalSize = (uint32_t *)packetBuffer;
*pNalSize = CFSwapInt32HostToBig(nalSize);

// 在buffer的前面填入代表长度的int    // 以00 00 00 01分割之后的下一个字节就是--NALU类型--
CVPixelBufferRef pixelBuffer = NULL;
int nalType = packetBuffer[4] & 0x1F;  // NALU类型  & 0001  1111
switch (nalType) {
case 0x05:
//                NSLog(@"*********** IDR frame, I帧");
[self initVideoToolBox]; // 当读入IDR帧的时候初始化VideoToolbox,并开始同步解码
pixelBuffer = [self decode]; // 解码得到的CVPixelBufferRef会传入OpenGL ES类进行解析渲染
break;
case 0x07:
//                NSLog(@"*********** SPS");
mSPSSize = packetSize - 4;
mSPS = malloc(mSPSSize);
memcpy(mSPS, packetBuffer + 4, mSPSSize);
break;
case 0x08:
//                NSLog(@"*********** PPS");
mPPSSize = packetSize - 4;
mPPS = malloc(mPPSSize);
memcpy(mPPS, packetBuffer + 4, mPPSSize);
break;
default:
//                NSLog(@"*********** B/P frame"); // P帧?
pixelBuffer = [self decode];

break;
}

if(pixelBuffer) {
// 把解码后的数据block传给viewController
self.returnDataBlock(pixelBuffer);
CVPixelBufferRelease(pixelBuffer);

}
});
}

用CMBlockBuffer(未压缩的图像数据) 把NALUnit包装起来

CMBlockBufferRef blockBuffer = NULL;
OSStatus status  = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
(void*)packetBuffer, packetSize,
kCFAllocatorNull,
NULL, 0, packetSize,
0, &blockBuffer);

创建CMSampleBuffer

把原始码流包装成CMSampleBuffer(存放一个或者多个压缩或未压缩的媒体文件)

CMSampleBufferRef sampleBuffer = NULL;
const size_t sampleSizeArray[] = {packetSize};
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuffer,        // 用CMBlockBuffer把NALUnit包装起来
mFormatDescription, // 把SPS和PPS包装成CMVideoFormatDescription
1, 0, NULL, 1, sampleSizeArray,
&sampleBuffer);

解码并显示

VTDecodeFrameFlags flags = 0;
VTDecodeInfoFlags flagOut = 0;
// 默认是同步操作。
// 调用didDecompress,返回后再回调
OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(mDecodeSession,
sampleBuffer,
flags,
&outputPixelBuffer,
&flagOut);

上一步即可获得编码后的数据 outputPixelBuffer,再将它传给OpenGL渲染显示解码的结果。

关于OpenGL渲染显示在下一篇文章里。

参考文章:

1.  http://www.jianshu.com/p/6dfe49b5dab8

2.  http://www.jianshu.com/p/da18b979aeec


相关文章

基于iOS的网络音视频实时传输系统(一)- 前言

基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC

基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP)

基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264

基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: