H.264 硬件解码器
基于 Apple VideoToolbox 的逐帧 NAL/SPS/PPS 解析与 PNG 帧导出 · Swift 课程实现。
架构
解码 pipeline:从 NAL 单元提取参数集,建立硬件解码会话,输出 CVPixelBuffer 后转 PNG。
flowchart LR
A[读取 video.h264] --> B[检查硬解能力]
B --> C[扫描 NAL 单元]
C --> D[提取 SPS / PPS]
D --> E[创建 VTDecompressionSession]
E --> F[逐帧 decode]
F --> G[CVPixelBuffer]
G --> H[导出 PNG]
代码亮点
SPS / PPS 提取
// 扫描 NAL 单元 type 7 (SPS) 与 type 8 (PPS)
let nalType = data[startCodeLen] & 0x1F
if nalType == 7 { spsData = data.subdata(in: ...) }
else if nalType == 8 { ppsData = data.subdata(in: ...) }
解码会话创建
VTDecompressionSessionCreate(
allocator: kCFAllocatorDefault,
formatDescription: formatDesc,
decoderSpecification: nil,
imageBufferAttributes: pixelBufferAttrs,
outputCallback: &callbackRecord,
decompressionSessionOut: &session)
CVPixelBuffer → PNG
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let context = CIContext()
let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
// 写入 PNG 到 frames/ 目录
课程笔记
NAL 结构 · H.264 码流以 0x00000001 起始码分隔 NAL 单元,每个 NAL 单元含 1 字节头(forbidden_zero_bit + nal_ref_idc + nal_unit_type)。
硬解 vs 软解 · VideoToolbox 在 Apple Silicon / Intel 都走 GPU 解码;遇到 H.265 10bit 等不支持的 profile 必须 fallback 到 FFmpeg 软解。
踩坑:startCode 长度 · NAL 起始码可能是 3 字节(0x000001)或 4 字节(0x00000001),扫描器要兼容两种。
本项目依赖本地 video.h264 文件 + Xcode 编译,无在线 demo。