Files
2026-09-28 18:28:32 +05:30

109 lines
4.7 KiB
Swift

import Foundation
import Testing
@testable import KnotchyCore
struct H264Tests {
private let aud: [UInt8] = [0x09, 0xF0]
private let sei: [UInt8] = [0x06, 0x05, 0x01, 0xAA, 0x80]
private let sps: [UInt8] = [0x67, 0x42, 0xC0, 0x1F, 0x8C]
private let sps2: [UInt8] = [0x67, 0x42, 0xC0, 0x28, 0x8C]
private let pps: [UInt8] = [0x68, 0xCE, 0x3C, 0x80]
private let pps2: [UInt8] = [0x68, 0xCE, 0x3C, 0x81]
private let idr: [UInt8] = [0x65, 0x88, 0x84, 0x21]
private let slice: [UInt8] = [0x41, 0x9A, 0x02]
private let four: [UInt8] = [0, 0, 0, 1]
private let three: [UInt8] = [0, 0, 1]
private func avcc(_ nals: [UInt8]...) -> Data {
nals.reduce(into: Data()) { data, nal in
data.appendBE32(UInt32(nal.count))
data.append(contentsOf: nal)
}
}
@Test func splitsOnThreeAndFourByteStartCodes() {
let stream = Data(four + aud + three + sps + four + pps + three + idr)
#expect(AnnexB.nalUnits(in: stream) == [aud, sps, pps, idr].map { Data($0) })
}
@Test func ignoresLeadingBytesAndTrailingZeros() {
let stream = Data([0xFF, 0x00] + three + slice + [0, 0] + four + slice + [0])
#expect(AnnexB.nalUnits(in: stream) == [Data(slice), Data(slice)])
}
@Test func handlesStreamsWithoutStartCodes() {
#expect(AnnexB.nalUnits(in: Data()) == [])
#expect(AnnexB.nalUnits(in: Data([0x65, 0x88])) == [])
#expect(AnnexB.nalUnits(in: Data(four)) == [])
#expect(AnnexB.nalUnits(in: Data(three + idr + three)) == [Data(idr)])
}
@Test func turnsAnIDRAccessUnitIntoAnAVCCSampleWithNewParameterSets() {
var parser = H264Parser()
let unit = parser.parse(Data(four + aud + four + sps + four + pps + four + sei + four + idr + four + idr))
#expect(unit.sample == avcc(idr, idr))
#expect(unit.isIDR)
#expect(unit.newParameterSets == ParameterSets(sps: Data(sps), pps: Data(pps)))
#expect(parser.parameterSets == unit.newParameterSets)
}
@Test func repeatedParameterSetsAreNotAChange() {
var parser = H264Parser()
_ = parser.parse(Data(four + sps + four + pps + four + idr))
let again = parser.parse(Data(four + aud + four + sps + four + pps + four + idr))
#expect(again.newParameterSets == nil)
let delta = parser.parse(Data(four + aud + four + slice))
#expect(delta.sample == avcc(slice))
#expect(!delta.isIDR)
#expect(delta.newParameterSets == nil)
// Repeated with every IDR: not a change, so the decoder isn't rebuilt.
let nextIDR = parser.parse(Data(four + aud + four + sps + four + pps + four + idr))
#expect(nextIDR.newParameterSets == nil)
}
@Test func aChangedSPSOrPPSIsANewFormat() {
var parser = H264Parser()
_ = parser.parse(Data(four + sps + four + pps + four + idr))
let changedSPS = parser.parse(Data(four + sps2 + four + pps + four + idr))
#expect(changedSPS.newParameterSets == ParameterSets(sps: Data(sps2), pps: Data(pps)))
let changedPPS = parser.parse(Data(four + sps2 + four + pps2 + four + idr))
#expect(changedPPS.newParameterSets == ParameterSets(sps: Data(sps2), pps: Data(pps2)))
}
@Test func aHalfPairIsNeverAFormat() {
var parser = H264Parser()
let spsOnly = parser.parse(Data(four + sps + four + idr))
#expect(spsOnly.newParameterSets == nil)
#expect(parser.parameterSets == nil)
_ = parser.parse(Data(four + sps + four + pps + four + idr))
let lone = parser.parse(Data(four + sps2 + four + slice))
#expect(lone.newParameterSets == nil)
#expect(parser.parameterSets == ParameterSets(sps: Data(sps), pps: Data(pps)))
}
@Test func onlyValidSliceUnitsAreDecoded() {
var parser = H264Parser()
// Type 21, a 3D extension slice.
let extensionSlice = parser.parse(Data(four + [0x75, 0x88]))
#expect(extensionSlice.sample.isEmpty)
#expect(!extensionSlice.isIDR)
// Forbidden bit set.
let forbidden = parser.parse(Data(four + [0xE5, 0x88] + four + slice))
#expect(forbidden.sample == avcc(slice))
#expect(!forbidden.isIDR)
#expect(parser.parse(Data(four + idr + four + slice)).isIDR)
let long = [UInt8(0x41)] + [UInt8](repeating: 0x9A, count: 299)
#expect(parser.parse(Data(four + long)).sample.prefix(4) == Data([0, 0, 0x01, 0x2C]))
}
@Test func slicesBeforeAnyParameterSetsHaveNoFormat() {
var parser = H264Parser()
let unit = parser.parse(Data(four + slice))
#expect(unit.sample == avcc(slice))
#expect(unit.newParameterSets == nil)
#expect(parser.parameterSets == nil)
}
}