记录。

http://www.voidcn.com/blog/dongtinghong/article/p-5047279.html
首先要把 VideoToolbox.framework 添加到工程里,并且包含以下头文件。 
#include <VideoToolbox/VideoToolbox.h>

解码主要需要以下三个函数
VTDecompressionSessionCreate 创建解码 session
VTDecompressionSessionDecodeFrame 解码一个frame
VTDecompressionSessionInvalidate 销毁解码 session

首先要创建 decode session,方法如下:
OSStatus status = VTDecompressionSessionCreate(kCFAllocatorDefault,
                                              decoderFormatDescription,
                                              NULL, attrs,
                                              &callBackRecord,
                                              &deocderSession);

其中 decoderFormatDescription 是 CMVideoFormatDescriptionRef 类型的视频格式描述,这个需要用H.264的 sps 和 pps数据来创建,调用以下函数创建 decoderFormatDescription
CMVideoFormatDescriptionCreateFromH264ParameterSets
需要注意的是,这里用的 sps和pps数据是不包含“00 00 00 01”的start code的。

attr是传递给decode session的属性词典
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);
其中重要的属性就一个,kCVPixelBufferPixelFormatTypeKey,指定解码后的图像格式,必须指定成NV12,苹果的硬解码器只支持NV12。

callBackRecord 是用来指定回调函数的,解码器支持异步模式,解码后会调用这里的回调函数。

如果 decoderSession创建成功就可以开始解码了。 
VTDecodeFrameFlags flags = 0;
            //kVTDecodeFrame_EnableTemporalProcessing | kVTDecodeFrame_EnableAsynchronousDecompression;
            VTDecodeInfoFlags flagOut = 0;
            CVPixelBufferRef outputPixelBuffer = NULL;
            OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(deocderSession,
                                                                      sampleBuffer,
                                                                      flags,
                                                                      &outputPixelBuffer,
                                                                      &flagOut);
其中 flags 用0 表示使用同步解码,这样比较简单。 
其中 sampleBuffer是输入的H.264视频数据,每次输入一个frame。 
先用CMBlockBufferCreateWithMemoryBlock 从H.264数据创建一个CMBlockBufferRef实例。 
然后用 CMSampleBufferCreateReady创建CMSampleBufferRef实例。 
这里要注意的是,传入的H.264数据需要Mp4风格的,就是开始的四个字节是数据的长度而不是“00 00 00 01”的start code,四个字节的长度是big-endian的。 
一般来说从 视频里读出的数据都是 “00 00 00 01”开头的,这里需要自己转换下。

解码成功之后,outputPixelBuffer里就是一帧 NV12格式的YUV图像了。 
如果想获取YUV的数据可以通过 
CVPixelBufferLockBaseAddress(outputPixelBuffer, 0);
    void *baseAddress = CVPixelBufferGetBaseAddress(outputPixelBuffer);
获得图像数据的指针,需要说明baseAddress并不是指向YUV数据,而是指向一个CVPlanarPixelBufferInfo_YCbCrBiPlanar结构体,结构体里记录了两个plane的offset和pitch。

但是如果想把视频播放出来是不需要去读取YUV数据的,因为CVPixelBufferRef是可以直接转换成OpenGL的Texture或者UIImage的。
调用CVOpenGLESTextureCacheCreateTextureFromImage,可以直接创建OpenGL Texture

从 CVPixelBufferRef 创建 UIImage
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
    UIImage *uiImage = [UIImage imageWithCIImage:ciImage];

解码完成后销毁 decoder session
VTDecompressionSessionInvalidate(deocderSession)

硬解码的基本流程就是这样了,如果需要成功解码播放视频还需要一些H.264视频格式,YUV图像格式,OpenGL等基础知识。

代码https://github.com/stevenyao/iOSHardwareDecoder/blob/master/H264DecodeDemo/ViewController.m

#import "ViewController.h"
#import "VideoFileParser.h"
#import "AAPLEAGLLayer.h"
#import <VideoToolbox/VideoToolbox.h>

@interface ViewController ()
{
    uint8_t *_sps;
    NSInteger _spsSize;
    uint8_t *_pps;
    NSInteger _ppsSize;
    VTDecompressionSessionRef _deocderSession;
    CMVideoFormatDescriptionRef _decoderFormatDescription;
    AAPLEAGLLayer *_glLayer;
}

@end

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

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

}

@implementation ViewController

-(BOOL)initH264Decoder {

if(_deocderSession) {
        return YES;
    }

const uint8_t* const parameterSetPointers[2] = { _sps, _pps };
    const size_t parameterSetSizes[2] = { _spsSize, _ppsSize };
    OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
                                                                          2, //param count
                                                                          parameterSetPointers,
                                                                          parameterSetSizes,
                                                                          4, //nal start code size
                                                                          &_decoderFormatDescription);

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,
                                              _decoderFormatDescription,
                                              NULL, attrs,
                                              &callBackRecord,
                                              &_deocderSession);
        CFRelease(attrs);

} else {

NSLog(@"IOS8VT: reset decoder session failed status=%d", status);

}

return YES;

}

-(void)clearH264Deocder {

if(_deocderSession) {
        VTDecompressionSessionInvalidate(_deocderSession);
        CFRelease(_deocderSession);
        _deocderSession = NULL;

}

if(_decoderFormatDescription) {

CFRelease(_decoderFormatDescription);
        _decoderFormatDescription = NULL;
    }

free(_sps);
   free(_pps);
    _spsSize = _ppsSize = 0;
}

-(CVPixelBufferRef)decode:(VideoPacket*)vp {

CVPixelBufferRef outputPixelBuffer = NULL;
    CMBlockBufferRef blockBuffer = NULL;

OSStatus status  = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,

(void*)vp.buffer, vp.size,
                                                          kCFAllocatorNull,

NULL, 0, vp.size,

0, &blockBuffer);

if(status == kCMBlockBufferNoErr) {

CMSampleBufferRef sampleBuffer = NULL;

const size_t sampleSizeArray[] = {vp.size};

status = CMSampleBufferCreateReady(kCFAllocatorDefault,

blockBuffer,

_decoderFormatDescription ,

1, 0, NULL, 1, sampleSizeArray,

&sampleBuffer);

if (status == kCMBlockBufferNoErr && sampleBuffer) {

VTDecodeFrameFlags flags = 0;

VTDecodeInfoFlags flagOut = 0;

OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(_deocderSession,

sampleBuffer,

flags,

&outputPixelBuffer,

&flagOut);

if(decodeStatus == kVTInvalidSessionErr) {

NSLog(@"IOS8VT: Invalid session, reset decoder session");

} else if(decodeStatus == kVTVideoDecoderBadDataErr) {

NSLog(@"IOS8VT: decode failed status=%d(Bad data)", decodeStatus);

} else if(decodeStatus != noErr) {

NSLog(@"IOS8VT: decode failed status=%d", decodeStatus);

}

CFRelease(sampleBuffer);

}

CFRelease(blockBuffer);

}

return outputPixelBuffer;

}

-(void)decodeFile:(NSString*)fileName fileExt:(NSString*)fileExt {

NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:fileExt];
    VideoFileParser *parser = [VideoFileParser alloc];
    [parser open:path];
    VideoPacket *vp = nil;
    while(true) {
        vp = [parser nextPacket];
        if(vp == nil) {
            break;
        }
        uint32_t nalSize = (uint32_t)(vp.size - 4);
        uint8_t *pNalSize = (uint8_t*)(&nalSize);
        vp.buffer[0] = *(pNalSize + 3);
        vp.buffer[1] = *(pNalSize + 2);
        vp.buffer[2] = *(pNalSize + 1);
        vp.buffer[3] = *(pNalSize);

CVPixelBufferRef pixelBuffer = NULL;
        int nalType = vp.buffer[4] & 0x1F;

switch (nalType) {

case 0x05:

NSLog(@"Nal type is IDR frame");

if([self initH264Decoder]) {

pixelBuffer = [self decode:vp];

}

break;

case 0x07:

NSLog(@"Nal type is SPS");

_spsSize = vp.size - 4;

_sps = malloc(_spsSize);

memcpy(_sps, vp.buffer + 4, _spsSize);

break;

case 0x08:

NSLog(@"Nal type is PPS");

_ppsSize = vp.size - 4;

_pps = malloc(_ppsSize);

memcpy(_pps, vp.buffer + 4, _ppsSize);

break;

default:

NSLog(@"Nal type is B/P frame");

pixelBuffer = [self decode:vp];

break;

}

if(pixelBuffer) {

dispatch_sync(dispatch_get_main_queue(), ^{
                _glLayer.pixelBuffer = pixelBuffer;

});

CVPixelBufferRelease(pixelBuffer);
        }

NSLog(@"Read Nalu size %ld", vp.size);

}

[parser close];

}

-(IBAction)on_playButton_clicked:(id)sender {

dispatch_async(dispatch_get_global_queue(0, 0), ^{

[self decodeFile:@"mtv" fileExt:@"h264"];

});

}

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

_glLayer = [[AAPLEAGLLayer alloc] initWithFrame:self.view.bounds];

[self.view.layer addSublayer:_glLayer];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

@end

iOS h264 硬解相关推荐

  1. FPV Camera(RPI 3B+/Zero W+V2.1) | wfb_ng Release 23.01 | H264硬解测试

    @[TOC](FPV Camera(RPI 3B+/Zero W+V2.1) | wfb_ng Release 23.01 | H264硬解测试) 1. 源由 RPI Zero W基于Release ...

  2. ios视频硬解异常总结,12911总结

    废话少说,直接上结果: VTDecompressionSessionCreate: -12911 原因总结: 创建session时,就是VTDecompressionSessionCreate函数: ...

  3. iOS h264硬编码

    从这里抄过来的:https://github.com/LevyGG/iOS-H.264-hareware-encode-and-decode/blob/master/VTDemoOniPad/H264 ...

  4. 硬解还是软解?手机视频播放功耗揭秘

    "硬解的功耗比软件低很多!"相信这是大多数人对硬解码和软解码的印象.然而具体低多少呢?却很少有人能够给出答案.为了揭开这个问题的谜底,北大数字媒体研究中心专门使用功耗仪进行了测试. ...

  5. H264视频高压心得——兼容华为U8800+(硬解720P)

    原文: http://www.cnblogs.com/zyl910/archive/2011/11/22/h264_encode_u8800.html H264视频高压心得--兼容华为U8800+(硬 ...

  6. Android硬编、硬解h264

    项目工程demo地址https://github.com/liluojun/PlayVideo demo包含硬编解h264.libyuv裁剪图像.opengles渲染yuv数据.ffmpeg解码裸h2 ...

  7. iOS VideoToolbox硬编H.265(HEVC)H.264(AVC):1 概述

    本文档尝试用Video Toolbox进行H.265(HEVC)硬件编码,视频源为iPhone后置摄像头.去年做完硬解H.264,没做编码,技能上感觉有些缺失.正好刚才发现CMFormatDescri ...

  8. iOS VideoToolbox 硬编指南

    引言 调用系统 VideoToolbox 的 API 实现一个硬编很容易,仔细看看文档.了解 API 的使用实现一个基本功能相信难不倒大家.但实际工作中有许多细节,一不注意就会掉坑里,甚至有些系统性问 ...

  9. MediaCodec硬解流程

    一 MediaCodec概述 MediaCodec 是Android 4.1(api 16)版本引入的低层编解码接口,同时支持音视频的编码和解码.通常与MediaExtractor.MediaMuxe ...

最新文章

  1. php imagecolorallocate 安装,PHP imagecolorallocate()和imagecolorallocatealpha():定义颜色
  2. 模拟ssh的远程网络传输
  3. 正态分布里的西格玛_西格玛和西格玛水平
  4. tarjan算法_【朝夕的ACM笔记】树上问题-最近公共祖先-倍增算法
  5. CAP与Base理论
  6. HDU4389(数位DP)
  7. python右斜杠_Python中的左斜杠、右斜杠(正斜杠和反斜杠)
  8. android打包工具多渠道批量打包,Android 快速渠道批量打包详解教程-美团多渠道打包方案...
  9. Linux SSH远程管理故障如何排查?
  10. MySQL基础篇(06):事务管理,锁机制案例详解
  11. channel9.msdn.com 微软虚拟学院MVA系列视频课程
  12. springboot中关于springMvc默认配置,配置扩展,全面接管
  13. linux RAID10测试
  14. matlab6数学建模基础教程,《数学建模基础教程》.pdf
  15. 【禁忌搜索算法】基于禁忌搜索算法求解函数极值问题含Matlab源码
  16. Function的用法
  17. 如何在谷歌地图自定义范围_如何在Google地图中创建自定义地图
  18. 广告召回率是什么意思_广告中召回和粗排的评价标准
  19. PKM个人知识管理整理(一)
  20. 电商运营风向标:数据分析。

热门文章

  1. 刷脸移动考勤系统实时掌握学生考勤动态
  2. Mac必备小工具 eul 1.4.1中文版 (菜单栏系统查看工具)
  3. linux安装微信和qq
  4. 超六成受访者 支持禁用微信布置作业
  5. sp、cp与移动运营商的三方博弈
  6. JDBC连接数据库的原理和步骤
  7. 如何快速恢复删除的文件?数据还原方法分享
  8. Matisse——Android 图片/视频选择器
  9. matlab怎样读取axt文件,MATLAB)课后实验答案
  10. 关于html 的image