iOS 0.5.45: decrypt F1TV cbcs audio with constant IV when senc is absent.

This commit is contained in:
Jos Vooges | STH 2026-09-28 00:32:29 +02:00
parent 9658ec6465
commit 622e2cdb56
3 changed files with 17 additions and 13 deletions

View file

@ -345,7 +345,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 57; CURRENT_PROJECT_VERSION = 58;
DEVELOPMENT_TEAM = X25NYX3K49; DEVELOPMENT_TEAM = X25NYX3K49;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -360,7 +360,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks" "@executable_path/Frameworks"
); );
MARKETING_VERSION = 0.5.44; MARKETING_VERSION = 0.5.45;
OTHER_LDFLAGS = ("$(inherited)", "-ObjC"); OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios; PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -378,7 +378,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 57; CURRENT_PROJECT_VERSION = 58;
DEVELOPMENT_TEAM = X25NYX3K49; DEVELOPMENT_TEAM = X25NYX3K49;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -393,7 +393,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks" "@executable_path/Frameworks"
); );
MARKETING_VERSION = 0.5.44; MARKETING_VERSION = 0.5.45;
OTHER_LDFLAGS = ("$(inherited)", "-ObjC"); OTHER_LDFLAGS = ("$(inherited)", "-ObjC");
PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios; PRODUCT_BUNDLE_IDENTIFIER = nl.vonas.mediacluster.ios;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

View file

@ -456,7 +456,11 @@ enum CencMp4Decryptor {
) )
} }
guard !sampleSizes.isEmpty, sencSamples.count == sampleSizes.count || sencSamples.isEmpty == false else { // cbcs audio (F1TV): tenc heeft constant_IV + iv_size=0, fragmenten hebben géén senc.
// ISO 23001-7: elk sample ontsleutelen met die IV (geen per-sample IV, geen subsample).
let constantIv = crypto?.constantIv
let canUseConstantIv = (constantIv?.count ?? 0) >= 8
guard !sampleSizes.isEmpty, !sencSamples.isEmpty || canUseConstantIv else {
diag("frag track=\(trackId) skip: samples=\(sampleSizes.count) senc=\(sencSamples.count) tfhd=0x\(String(tfhdFlagsSeen, radix: 16)) trun=0x\(String(trunFlagsSeen, radix: 16))") diag("frag track=\(trackId) skip: samples=\(sampleSizes.count) senc=\(sencSamples.count) tfhd=0x\(String(tfhdFlagsSeen, radix: 16)) trun=0x\(String(trunFlagsSeen, radix: 16))")
// Advance cursor even if we cannot decrypt. // Advance cursor even if we cannot decrypt.
for sz in sampleSizes { mdatCursor += sz } for sz in sampleSizes { mdatCursor += sz }
@ -478,34 +482,34 @@ enum CencMp4Decryptor {
let cryptBlocks = crypto?.cryptByteBlock ?? 0 let cryptBlocks = crypto?.cryptByteBlock ?? 0
let skipBlocks = crypto?.skipByteBlock ?? 0 let skipBlocks = crypto?.skipByteBlock ?? 0
var didDecrypt = false var decryptedCount = 0
for i in 0..<sampleSizes.count { for i in 0..<sampleSizes.count {
let size = sampleSizes[i] let size = sampleSizes[i]
guard size > 0, mdatCursor + size <= data.count else { break } guard size > 0, mdatCursor + size <= data.count else { break }
let sampleRange = mdatCursor..<(mdatCursor + size) let sampleRange = mdatCursor..<(mdatCursor + size)
var sample = data.subdata(in: sampleRange) var sample = data.subdata(in: sampleRange)
let senc = i < sencSamples.count ? sencSamples[i] : nil let senc = i < sencSamples.count ? sencSamples[i] : nil
if let senc { let iv = senc?.iv ?? constantIv
if let iv {
if let decrypted = decryptSample( if let decrypted = decryptSample(
sample, sample,
key: key, key: key,
iv: senc.iv, iv: iv,
subsamples: senc.subsamples, subsamples: senc?.subsamples,
scheme: scheme, scheme: scheme,
cryptByteBlock: cryptBlocks, cryptByteBlock: cryptBlocks,
skipByteBlock: skipBlocks skipByteBlock: skipBlocks
) { ) {
sample = decrypted sample = decrypted
data.replaceSubrange(sampleRange, with: sample) data.replaceSubrange(sampleRange, with: sample)
didDecrypt = true decryptedCount += 1
} }
} }
mdatCursor += size mdatCursor += size
} }
let decryptedCount = didDecrypt ? min(sencSamples.count, sampleSizes.count) : 0
diag("frag track=\(trackId) crypto=\(crypto != nil) scheme=\(scheme) pat=\(cryptBlocks):\(skipBlocks) samples=\(sampleSizes.count) senc=\(sencSamples.count) iv=\(sencSamples.first?.iv.count ?? 0) subs=\(sencSamples.first?.subsamples?.count ?? -1) size0=\(sampleSizes.first ?? 0) dec=\(decryptedCount) tfhd=0x\(String(tfhdFlagsSeen, radix: 16)) trun=0x\(String(trunFlagsSeen, radix: 16)) mdat=\(mdat.payloadLength)") diag("frag track=\(trackId) crypto=\(crypto != nil) scheme=\(scheme) pat=\(cryptBlocks):\(skipBlocks) samples=\(sampleSizes.count) senc=\(sencSamples.count) iv=\(sencSamples.first?.iv.count ?? 0) subs=\(sencSamples.first?.subsamples?.count ?? -1) size0=\(sampleSizes.first ?? 0) dec=\(decryptedCount) tfhd=0x\(String(tfhdFlagsSeen, radix: 16)) trun=0x\(String(trunFlagsSeen, radix: 16)) mdat=\(mdat.payloadLength)")
// AVPlayer/VLC mogen geen tweede decrypt-poging doen op clear samples. // AVPlayer/VLC mogen geen tweede decrypt-poging doen op clear samples.
if didDecrypt { if decryptedCount > 0 {
for senc in findBoxesInRange(data, type: "senc", from: traf.payloadStart, to: traf.payloadEnd) { for senc in findBoxesInRange(data, type: "senc", from: traf.payloadStart, to: traf.payloadEnd) {
writeFourCC(&data, senc.start + 4, "free") writeFourCC(&data, senc.start + 4, "free")
} }

View file

@ -96,7 +96,7 @@ struct SettingsScreen: View {
Section("Info") { Section("Info") {
LabeledContent("App", value: "VMC iOS") LabeledContent("App", value: "VMC iOS")
LabeledContent("Versie", value: "0.5.44") LabeledContent("Versie", value: "0.5.45")
LabeledContent("Live TV", value: app.iptvEnabled ? "Aan" : "Uit") LabeledContent("Live TV", value: app.iptvEnabled ? "Aan" : "Uit")
LabeledContent("Events", value: app.eventsEnabled ? "Aan" : "Uit") LabeledContent("Events", value: app.eventsEnabled ? "Aan" : "Uit")
} }