Geräte, die auf iOS17 verblieben sind, funktionieren weiterhin normal und die Debugging-Protokolle sind eine Plausibilitätsprüfung, die dies beweist. Hat noch jemand dieses Problem? Wir haben dieses Problem in den letzten zwei Monaten mehrmals bei Apple angesprochen und bisher noch keine Antwort von den Ingenieuren oder dem Entwicklerforum erhalten.
Hier ist der Code (Sie sollten ihn in ein neues iOS-Xcode-Projekt einfügen und eine Server-URL, eine Inhalts-URL und ein Zertifikat bereitstellen können). Vielen Dank im Voraus und an diejenigen, die bereits auf meinen schlechten ersten Versuch, um Hilfe zu bitten, geantwortet haben.
Der auf iOS26 zurückgegebene Fehler ist
Code: Select all
> signalled err=-12860 at :1522 > signalled err=-12860 at :1522 > signalled err=-12860 at :1522
Code: Select all
import UIKit
import AVFoundation
class ViewController: UIViewController {
private var player: AVPlayer?
private var keyDelegate: ContentKeyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .orange
self.configureAudioSession()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate
, let hlsURL = appDelegate.hlsURL()
, let certificateData = appDelegate.certificate()
, let licenseServerURL = appDelegate.licenseServerURL() else {
return
}
let delegate = ContentKeyDelegate(appCertificate: certificateData,licenseServerURL: licenseServerURL)
self.keyDelegate = delegate
let asset = AVURLAsset(url: hlsURL, options: [
AVURLAssetPreferPreciseDurationAndTimingKey: NSNumber(value: false)
])
delegate.setAsset(asset)
let item = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: item)
player.automaticallyWaitsToMinimizeStalling = true
player.allowsExternalPlayback = false
player.preventsDisplaySleepDuringVideoPlayback = true
self.player = player
let layer = AVPlayerLayer(player: player)
layer.frame = view.bounds
layer.videoGravity = .resizeAspect
self.view.layer.addSublayer(layer)
player.play()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let playerLayer = view.layer.sublayers?.first(where: { $0 is AVPlayerLayer }) as? AVPlayerLayer {
playerLayer.frame = view.bounds
}
}
private func configureAudioSession() {
do {
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, mode: .moviePlayback, options: [])
try audioSession.setActive(true)
print("✅ Audio session configured successfully")
} catch {
print("❌ Failed to configure audio session: \(error)")
}
}
}
final class ContentKeyDelegate: NSObject, AVContentKeySessionDelegate {
private let appCertificate: Data
private let licenseServerURL: URL
private let keySession: AVContentKeySession
init(appCertificate: Data, licenseServerURL: URL) {
self.appCertificate = appCertificate
self.licenseServerURL = licenseServerURL
self.keySession = AVContentKeySession(keySystem: .fairPlayStreaming)
super.init()
self.keySession.setDelegate(self, queue: DispatchQueue.main)
}
func setAsset(_ asset: AVURLAsset) {
self.keySession.addContentKeyRecipient(asset)
}
func contentKeySession(_ session: AVContentKeySession, didProvide keyRequest: AVContentKeyRequest) {
process(keyRequest: keyRequest)
}
func contentKeySession(_ session: AVContentKeySession, didProvideRenewingContentKeyRequest keyRequest: AVContentKeyRequest) {
process(keyRequest: keyRequest)
}
private func process(keyRequest: AVContentKeyRequest) {
guard let assetIDString = keyRequest.identifier as? String, let assetIDData = assetIDString.data(using: .utf8) else {
keyRequest.processContentKeyResponseError(NSError(domain: "ContentKey", code: -1))
return
}
Task {
do {
let spcData = try await keyRequest.makeStreamingContentKeyRequestData(forApp: appCertificate,contentIdentifier: assetIDData, options: nil)
self.fetchCKC(spcData: spcData, assetId: assetIDString) { result in
switch result {
case .success(let ckcOpt):
guard let ckcData = ckcOpt else {
DispatchQueue.main.async {
keyRequest.processContentKeyResponseError(ProgramError.noCKCReturnedByKSM)
}
return
}
let response = AVContentKeyResponse(fairPlayStreamingKeyResponseData: ckcData)
DispatchQueue.main.async {
keyRequest.processContentKeyResponse(response)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
if let err = keyRequest.error {
Swift.print("keyRequest.error: \(err.localizedDescription) / \(err)")
} else {
Swift.print("keyRequest.error: nil")
}
}
}
case .failure(let error):
Swift.print("fetchCKC error: \(error)")
DispatchQueue.main.async {
keyRequest.processContentKeyResponseError(error)
}
}
}
} catch {
Swift.print("SPC generation error: \(error)")
DispatchQueue.main.async {
keyRequest.processContentKeyResponseError(error)
}
}
}
}
private func fetchCKC(spcData: Data, assetId: String, completion: @escaping (Result) -> Void) {
if let contentKeyData = Data(base64Encoded: "X7sBORoVsqx/M96GnKLUqA==")
, let initialisationVectorData = Data(base64Encoded: "beXQ7IjxPAxSk29JU3MgvA==") {
let endpoint = "https://fairplay-key-server.compilerdefault.com/fps/"
if let url = URL(string: endpoint) {
let spcBase64 = spcData.base64EncodedString()
let contentKeyHex = ContentKeyDelegate.hex(forData: contentKeyData)
let initialisationVectorHex = ContentKeyDelegate.hex(forData: initialisationVectorData)
let parameters: [String: Any] = [
"fairplay-streaming-request": [
"version": 1,
"create-ckc": [[
"id": 1,
"asset-info": [[
"content-key": contentKeyHex,
"content-iv": initialisationVectorHex,
"encryption-scheme": "cbcs",
"hdcp-type": 0,
"content-type": "hd",
"lease-duration": 1200
]],
"spc": spcBase64
]]
]
]
do {
let postData = try JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) {data, response, error in
if let response = response as? HTTPURLResponse {
Swift.print("KSM status code: \(response.statusCode)")
}
if let error = error {
Swift.print("KSM request error: \(error)")
completion(.failure(error))
return
}
guard let data = data, var responseString = String(data: data, encoding: .utf8) else {
Swift.print("KSM response empty or not UTF-8")
completion(.failure(ProgramError.noCKCReturnedByKSM))
return
}
if responseString.hasPrefix("Result") {
responseString.removeFirst("Result".count)
}
guard let jsonData = responseString.data(using: .utf8) else {
Swift.print("Failed to convert trimmed response to data")
completion(.failure(ProgramError.noCKCReturnedByKSM))
return
}
do {
if let dictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any]
, let fairplayStreamingResponse = dictionary["fairplay-streaming-response"] as? [String: Any]
, let ckcArray = fairplayStreamingResponse["create-ckc"] as? [[String: Any]]
, let ckcDictionary = ckcArray.first
, let ckc = ckcDictionary["ckc"] as? String
, let thatData = Data(base64Encoded: ckc) {
completion(.success(thatData))
} else {
completion(.failure(ProgramError.noCKCReturnedByKSM))
}
} catch {
Swift.print("JSON parse error: \(error)")
completion(.failure(error))
}
}
task.resume()
} catch let error {
Swift.print(error)
completion(.failure(error))
}
} else {
let error = NSError()
completion(.failure(error))
}
}
}
enum ProgramError: Error {
case missingApplicationCertificate
case noCKCReturnedByKSM
}
func contentKeySession(_ session: AVContentKeySession, didFailWithError error: Error) {
Swift.print("AVContentKeySession didFailWithError: \(error.localizedDescription) — \(error)")
}
public static func hex(forData theData: Data) -> String {
return theData.map { String(format: "%02hhx", $0) }.joined()
}
}
Mobile version