import Foundation public enum NALUnitType { public static let slice: UInt8 = 1 public static let idrSlice: UInt8 = 5 public static let sei: UInt8 = 6 public static let sps: UInt8 = 7 public static let pps: UInt8 = 8 public static let accessUnitDelimiter: UInt8 = 9 } public enum AnnexB { /// Splits an Annex-B stream into NAL units. Trailing zeros are trimmed: a NAL unit never ends in 0x00. public static func nalUnits(in stream: Data) -> [Data] { let bytes = [UInt8](stream) var starts: [(code: Int, payload: Int)] = [] var i = 0 while i + 2 < bytes.count { if bytes[i] == 0, bytes[i + 1] == 0, bytes[i + 2] == 1 { starts.append((code: i, payload: i + 3)) i += 3 } else { i += 1 } } return starts.indices.compactMap { k in var end = k + 1 < starts.count ? starts[k + 1].code : bytes.count while end > starts[k].payload, bytes[end - 1] == 0 { end -= 1 } return end > starts[k].payload ? Data(bytes[starts[k].payload.. ParsedAccessUnit { var unit = ParsedAccessUnit() var sps: Data? var pps: Data? for nal in AnnexB.nalUnits(in: accessUnit) { let header = nal[nal.startIndex] guard header & 0x80 == 0 else { continue } // forbidden_zero_bit let type = header & 0x1F switch type { case NALUnitType.sps: sps = nal case NALUnitType.pps: pps = nal case NALUnitType.slice, NALUnitType.idrSlice: if type == NALUnitType.idrSlice { unit.isIDR = true } unit.sample.appendBE32(UInt32(nal.count)) unit.sample.append(nal) default: break } } // SPS and PPS must come from the same access unit; x264 repeats both on every IDR. if let sps, let pps { let sets = ParameterSets(sps: sps, pps: pps) if sets != parameterSets { parameterSets = sets unit.newParameterSets = sets } } return unit } }