Sidd Redkar
Sidd Redkar

Reputation: 39

How to Append Custom Parameters to HLS Segment URLs in AVPlayer Using AVAssetResourceLoaderDelegate in Swift?

I’m working on an iOS application that uses AVPlayer to play an HLS stream. My goal is to dynamically append custom parameters to each segment URL (e.g., .ts files) during playback.

Example:

Given an HLS playlist with the following format:

bash

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:5.672333,
ad2_segment_0000.ts
#EXTINF:2.585922,
ad2_segment_0001.ts
#EXTINF:6.756744,
ad2_segment_0002.ts
#EXTINF:3.461789,
ad2_segment_0003.ts
#EXTINF:2.210544,
ad2_segment_0004.ts
#EXTINF:3.712044,
ad2_segment_0005.ts
#EXT-X-ENDLIST

The goal is to modify each segment URL to include custom parameters, like so:

ruby

http://example.com/path/to/ad_segment_0000.ts?paramKey1=paramValue1&paramKey2=paramValue2
Here’s my current approach using AVAssetResourceLoaderDelegate:
import AVFoundation
import UIKit

class ViewController: UIViewController {

    var player: AVPlayer?
    var playerLayer: AVPlayerLayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupPlayer()
    }

    func setupPlayer() {
        // Initialize the asset with the HLS stream URL
        guard let url = URL(string: "https://your_hls_stream_url.m3u8") else {
            print("Invalid URL")
            return
        }
        
        let asset = AVURLAsset(url: url)
        let resourceLoader = asset.resourceLoader
        let loaderDelegate = CustomResourceLoaderDelegate()
        
        // Set the custom resource loader delegate
        resourceLoader.setDelegate(loaderDelegate, queue: DispatchQueue.main)
        
        // Create the player item and player
        let playerItem = AVPlayerItem(asset: asset)
        player = AVPlayer(playerItem: playerItem)
        
        // Setup player layer
        playerLayer = AVPlayerLayer(player: player)
        playerLayer?.frame = self.view.bounds
        playerLayer?.videoGravity = .resizeAspect
        if let playerLayer = playerLayer {
            self.view.layer.addSublayer(playerLayer)
        }
        
        // Start playing
        player?.play()
    }
}

class CustomResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate {
    
    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        guard let originalURL = loadingRequest.request.url else {
            return false
        }
        
        // Check if the request is for a .ts segment file
        if originalURL.pathExtension == "ts" {
            // Append custom parameters
            var urlComponents = URLComponents(url: originalURL, resolvingAgainstBaseURL: false)
            
            // Add the parameters you want to include for each .ts request
            let customParameters = [
                URLQueryItem(name: "param1", value: "value1"),
                URLQueryItem(name: "param2", value: "value2")
            ]
            
            urlComponents?.queryItems = (urlComponents?.queryItems ?? []) + customParameters
            
            // Replace the URL in the request with the modified one
            if let modifiedURL = urlComponents?.url {
                let redirectedRequest = URLRequest(url: modifiedURL)
                loadingRequest.redirect = redirectedRequest
            }
        }
        
        // Complete the request
        loadingRequest.finishLoading()
        return true
    }
}

Problem:

The above code doesn't seem to work as expected, and I'm unable to modify the segment URLs with custom parameters during playback. Has anyone successfully implemented a similar solution? Any guidance on this approach would be highly appreciated.

Upvotes: 1

Views: 41

Answers (0)

Related Questions