Unknown error -12881 when using AVAssetResourceLoader

Here we are focusing to change the cookie at every 120 seconds while playing , in apple avplayer we can't modify cookie after initialisation due to that we followed the approach to using " Resource loader delegate " to pass cookie as a header value .

What I notice is that the playlist file (.m3u8) gets downloaded correctly. Then video file (.m4a) some chunks also gets downloaded. I know that the .ts file is downloaded because I can see the GET request completing on the web server with status 200. I also set a breakpoint at the following line:

loadingRequest.dataRequest?.respond(with: data)

immediately got error from avplayer status as

"The operation could not be completed. An unknown error occurred (-12881) From core media"

  1. Need confirmation on why I am unable to load HLS using resource loader.
  2. is it possible to update cookie value while paying continuously on avplayer.
   override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let urlString = "localhost://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"
        guard let url = URL(string: urlString) else {
            print("Invalid URL")
            return
        }
        
        //Create cookie to prepare for player asset
        
      let cookie =  HTTPCookie(properties: [
            .name: "dazn-token",
            .value: "cookie value",
            .domain: url.host() ?? "",
            .path: "/",
            .discard: true
        ])

        //Create cookie key to set AVURLAsset
        let options =  [AVURLAssetHTTPCookiesKey: [cookie]]
        
        let asset = AVURLAsset(url: url,options: options)
        proxy = ReverseProxyResourceLoader()
        proxy?.cookie = "exampleCookie"
     
        // Set resource loader delegate to moniter the chunks
        asset.resourceLoader.setDelegate(proxy, queue: DispatchQueue.global())
        // Load asset keys asynchronously (e.g., "playable")
        let keys = ["playable"]
        
        // Initialize the AVPlayer with the URL
        let playerItem = AVPlayerItem(asset: asset)
        self.player = AVPlayer(playerItem: playerItem)
        playerItem.addObserver(self, forKeyPath: "status", options: [.new, .initial], context: nil)

           // Observe 'error' property (if needed)
        playerItem.addObserver(self, forKeyPath: "error", options: [.new], context: nil)
        
        let contentKeySessionDelegate = ContentKeyDelegate()
        // Initialize AVContentKeySession
        let contentKeySession = AVContentKeySession(keySystem: .clearKey)
        self.contentKeySession = contentKeySession
        contentKeySession.setDelegate(contentKeySessionDelegate, queue: DispatchQueue.main)

        // Associate the asset with the content key session
        contentKeySession.addContentKeyRecipient(asset)

        
        // Create a layer for the AVPlayer and add it to the view
        playerLayer = AVPlayerLayer(player: player)
        playerLayer?.frame = view.bounds
        playerLayer?.videoGravity = .resizeAspect
        if let playerLayer = playerLayer {
            view.layer.addSublayer(playerLayer)
        }
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(playerDidFinishPlaying),
            name: .AVPlayerItemDidPlayToEndTime,
            object: player?.currentItem
        )
        
        // Start playback
        player?.play()
    }
    
    // Update cookie when ever needed
    func updateCookie() {
        proxy?.cookie = "update exampleCookie"
    }
    
    @objc private func playerDidFinishPlaying(notification: Notification) {
        print("Playback finished!")
        // Optionally, handle end-of-playback actions here
    }
//
//  ReverseProxyResourceLoader.swift
//  HLSDemo
//
//  Created by Gajje.Venkatarao on 12/12/24.
//

import Foundation
import AVKit
import AVFoundation


class ReverseProxyResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
    
    var cookie = ""
    
    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        
        resourceLoader.preloadsEligibleContentKeys = true
        guard let interceptedURL = loadingRequest.request.url else {
            loadingRequest.finishLoading(with: NSError(domain: "ReverseProxy", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]))
            return false
        }
        
        if interceptedURL.scheme == "skd" {
            
            print("Token updated Cookie:", interceptedURL )
            return false
        }
        
        var components = URLComponents(url: interceptedURL, resolvingAgainstBaseURL: false)
        components?.scheme = "https" // Replace with the original scheme
        guard let originalURL = components?.url else {
            loadingRequest.finishLoading(with: NSError(domain: "ReverseProxy", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to map URL"]))
            loadingRequest.finishLoading()
            return false
        }
        
        var request = URLRequest(url: originalURL)
        
        request.httpMethod = "GET"
        

        if let storeCoockie = HTTPCookie(properties: [
            .name: "dazn-token",
            .value: cookie,
            .domain: originalURL.host ?? "",
            .path: "/",
            .discard: true
        ]){
            
            HTTPCookieStorage.shared.setCookie(storeCoockie)
        }
        
        let headers = loadingRequest.request.allHTTPHeaderFields ?? [:]
        for (key, value) in headers {
            request.addValue(value, forHTTPHeaderField: key)
            
        }
        request.addValue(cookie, forHTTPHeaderField: "Cookie")
        URLSession.shared.configuration.httpShouldSetCookies = true
        request.httpShouldHandleCookies = true
        
        
        let task = (URLSession.shared.dataTask(with: originalURL) { data, response, error in
            if let error = error {
                print("Error Received:", error)
                loadingRequest.finishLoading(with: error)
                
                return
            }
            print(originalURL)
            guard let data = data , let url = response?.url else {
                loadingRequest.finishLoading(with: NSError(domain: "ReverseProxy", code: -1, userInfo: [NSLocalizedDescriptionKey: "No data received"]))
                return
            }
            
            loadingRequest.dataRequest?.respond(with: data)
            
            loadingRequest.finishLoading()
        } as URLSessionDataTask)
        
        task.resume()
        
        
        return true
    }
}

Example project

Unknown error -12881 when using AVAssetResourceLoader
 
 
Q