iOS利用VideoToolbox实现视频硬解码

需求

本文主要将含有编码的H.264,H.265视频流文件解码为原始视频数据,解码后即可渲染到屏幕或用作其他用途.

实现原理

正如我们所知,编码数据仅用于传输,无法直接渲染到屏幕上,所以这里利用苹果原生框架VideoToolbox解析文件中的编码的视频流,并将压缩视频数据(h264/h265)解码为指定格式(yuv,RGB)的视频原始数据,以渲染到屏幕上.

注意: 本例主要为解码,需要借助FFmpeg搭建模块,视频解析模块,渲染模块,这些模块在下面阅读前提皆有链接可直接访问.

阅读前提

代码地址 : Video Decoder

掘金地址 : Video Decoder

简书地址 : Video Decoder

博客地址 : Video Decoder

1、总体架构

总体思想即将FFmpeg parse到的数据装到CMBlockBuffer中,将extra data分离出的vps,sps,pps装到CMVideoFormatDesc中,将计算好的时间戳装到CMTime中,最后即可拼成完成的CMSampleBuffer以用来提供给解码器.

CMSampleBufferCreate

1.1 简易流程

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传出.

1.2 文件结构

image

1.3 快速使用

  • 初始化preview

    解码后的视频数据将渲染到该预览层

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
}
​
- (void)setupUI {
    self.previewView = [[XDXPreviewView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:self.previewView];
    [self.view bringSubviewToFront:self.startBtn];
}
  • 解析并解码文件中视频数据

- (void)startDecodeByVTSessionWithIsH265Data:(BOOL)isH265 {
    NSString *path = [[NSBundle mainBundle] pathForResource:isH265 ? @"testh265" : @"testh264"  ofType:@"MOV"];
    XDXAVParseHandler *parseHandler = [[XDXAVParseHandler alloc] initWithPath:path];
    XDXVideoDecoder *decoder = [[XDXVideoDecoder alloc] init];
    decoder.delegate = self;
    [parseHandler startParseWithCompletionHandler:^(BOOL isVideoFrame, BOOL isFinish, struct XDXParseVideoDataInfo *videoInfo, struct XDXParseAudioDataInfo *audioInfo) {
        if (isFinish) {
            [decoder stopDecoder];
            return;
        }
        
        if (isVideoFrame) {
            [decoder startDecodeVideoData:videoInfo];
        }
    }];
}
  • 将解码后数据渲染到屏幕上

注意: 如果数据中含有B帧则需要做一个重排序才能渲染,本例提供两个文件,一个不含B帧的h264类型文件,一个含B帧的h265类型文件.

- (void)getVideoDecodeDataCallback:(CMSampleBufferRef)sampleBuffer {
    if (self.isH265File) {
        // Note : the first frame not need to sort.
        if (self.isDecodeFirstFrame) {
            self.isDecodeFirstFrame = NO;
            CVPixelBufferRef pix = CMSampleBufferGetImageBuffer(sampleBuffer);
            [self.previewView displayPixelBuffer:pix];
        }
        
        XDXSortFrameHandler *sortHandler = [[XDXSortFrameHandler alloc] init];
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值