iOS 系统中,H.264 视频流可以硬件解码吗? 具体如何实现?

https://www.zhihu.com/question/20692215


16 个回答
这个问题都问了两年多了,没有很好的回答,我最近正好搞定了iOS的硬解码 H.264,借这个问题来分享下经验。

其实至少从iPhone4开始,苹果就是支持硬件解码了,但是硬解码API一直是私有API,不开放给开发者使用,只有越狱才能使用,正常的App如果想提交到AppStore是不允许使用私有API的。

从iOS8开始,可能是苹果想通了,开放了硬解码和硬编码API,就是名为 VideoToolbox.framework的API,需要用iOS 8以后才能使用,iOS 7.x上还不行。

这套硬解码API是几个纯C函数,在任何OC或者 C++代码里都可以使用。

首先要把 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等基础知识。


还是有很多小细节要处理的,无法在这里一一说明了,有人有问题可以在评论里讨论。

从解码到播放,大约1000行代码左右,主要是OpenGL渲染的代码比较多。


苹果官方的示例代码:

WWDC - Apple Developer


苹果的例子下载链接实效了,我也找不到那个例子,我自己写了一个。

stevenyao/iOSHardwareDecoder · GitHub

姚大的demo是基于文件的。实际场景大多是基于流。基于此,我写了个demo。
H264实时硬件编解码demo

iOS 支持 H.264 視頻流硬解碼:

HTTP Live Streaming Overview: Frequently Asked Questions | developer.apple.com/lib

The protocol specification does not limit the encoder selection. However, the current Apple implementation should interoperate with encoders that produce MPEG-2 Transport Streams containing H.264 video and AAC audio (HE-AAC or AAC-LC). Encoders that are capable of broadcasting the output stream over UDP should also be compatible with the current implementation of the Apple provided segmenter software.

具體實現過程請完整閱讀以上文檔,和其它相關文檔:

HTTP Live Streaming Overview: HTTP Streaming Architecture | developer.apple.com/lib
可以给我发一份完整得代码吗?谢谢1507904849@qq.com
GitHub - newOcean/HDLiveStreamingSdk: ios live hardware encoder and decoder
发现一个偷懒的sdk,iOS视频推流,播放,录制,拍照,demo。
你好,我想请问一下,如果我要做视频直播类型的,如何直接解码传过来了的H264流而不是解码文件呢?
可以发一份完整代码吗?正在学习相关内容,谢谢啦。allenchan0311@qq.com
具体流程可以参考github上的一个例子,github.com/adison/-Vide 注意create session和decode session时codecCtx和packet的数据可能会有一些小差别,可以根据具体情况修改
但是对于不同的嵌入式编码版
有的编码器的264码流可以解码成功,有的编码器死活解不了
不知道是否苹果官方存在这方面的限制
使用函数CMVideoFormatDescriptionCreate( ),不可以创建decoderFormatDescription吗?如果其他codec类型,CMVideoFormatDescriptionCreateFromH264ParameterSets( )就不能用了。
@姚冬,大神,需要你的帮助啊,我解码出来后,用pixelBuffer创建CIImage和UIImage都提示结果为null,创建失败。然后我将pixelBuffer用gles显示,也遇到了图像无色彩、图像偏移的情况,你有遇到过类似情况吗?
看到你的文章,像找到了新大陆一样。我也正在学习这方面,如果可以的话,能给我发一份code吗,谢谢!cx0928@126.com
正在学习这方面的东西,同求代码,谢谢!!zhu770277@163.com
可以给我发一份完整得代码吗?谢谢kuchengxiansheng@163.com
google到VLC的一个videotoolbox的patch
[PATCH 1/2] video chroma: add a Nv12 copy function which outputs I420
[PATCH 2/2] Add VideoToolbox based decoder
但是找了个mp4封装的视频,用VLCKit跑了一下,CPU变化不大,反而FPS降了一半,效果比软解码还差一大截,不清楚是哪个环节没搞对。。。
按照你说的步骤调用硬件解码的接口,发现到VTDecompressionSessionDecodeFrame这步就卡着不动了,不知道你是否有碰到这种情况?
可以给我发一份完整得代码吗?谢谢mengqin1199@163.com







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值