Save HLS m3u8 on disk - Swift

Hi everyone!


I'm developing an application that will show a stream video feed (Instagram like feed) and I was thinking about how to make it.

Actually I am using a CollectionViewController and instantiating AVPlayers inside each cell.

I'm also managing pause/resume task on scroll view scrolling.


Videos come from cdn and are m3u8 format (for HLS).

When I play with

let player = AVPlayer(url: videoURL)

Everything works good.


But what I really need is to cache this video in order to improve performance and reactivity.


I tried with AVAssetDownloadURLSession, following this guide https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/HTTPLiveStreaming/HTTPLiveStreaming.html


Strange behavior: the first time I launched the app, it worked! Now, it does not.

I also found out that downloading is not monitored (print (percentageComplete) does not work!)


This is the code I used:

func setupAssetDownload(url:URL) {
        /
        let configuration = URLSessionConfiguration.background(withIdentifier: "streamVideo")
       
        /
        let downloadSession = AVAssetDownloadURLSession(configuration: configuration,
                                                    assetDownloadDelegate: self,
                                                    delegateQueue: OperationQueue.main)
       
        let asset = AVURLAsset(url: url)
       
        /
        let downloadTask = downloadSession.makeAssetDownloadTask(asset: asset,
                                                                 assetTitle: "",
                                                                 assetArtworkData: nil,
                                                                 options: nil)
        /
        downloadTask?.resume()
    }
   
    func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange) {
        var percentComplete = 0.0
        /
        for value in loadedTimeRanges {
            /
            let loadedTimeRange = value.timeRangeValue
            /
            percentComplete += loadedTimeRange.duration.seconds / timeRangeExpectedToLoad.duration.seconds
        }
        percentComplete *= 100
        print("Percent complete \(percentComplete)")
        /
    }
   
    func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
        /
        UserDefaults.standard.set(location.relativePath, forKey: "assetPath")
    }


What's wrong with this code?


Thanks for your help, it's very urgent!

Post not yet marked as solved Up vote post of giuscapo Down vote post of giuscapo
6.2k views

Replies

I have the same problem.


I've download the demo from Apple and noticed that the download there is not working also.

AVAssetDownloadDelegate methods are not called after task.resume().


If you send the asset directly to AVPlayerViewController, the video plays without problem.

without seeing the entire code, it's hard to tell where the issue is.

I would suggest implmenting

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)


and see if it gets called.

It doesn't unfortunately.

Please checkout the new samples in the FPS SDK. We have two samples, one if you want to support iOS 10 version of offline viewing, and one versin that support iOS 11s version of offline viewing.


The code in those samples shoudl help you resove these issues.

I found that the problem was with the underlying Background URL Session. All it takes is for one download of your session to get stuck due to some persistent issue and since the timeout period for them by default is 7 DAYS it can lead to nothing being downloaded until that download is freed. You need to set the timeoutIntervalForResource value to something lower like 30-60 seconds. This is even more important if you are doing your downloads serially. I think that this is also an issue due to the multipart nature of the HLS downloads. You should restart your device to gaurantee any prior background downloads associated with the old timeout are purged.


Documenation: "Any upload or download tasks created by a background session are automatically retried if the original request fails due to a timeout. To configure how long an upload or download task should be allowed to be retried or transferred, use the

timeoutIntervalForResource
property."


https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1408259-timeoutintervalforrequest

https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1408153-timeoutintervalforresource


It would be nice if Apple would allow us to use a non background URL Session configuration for those of use who are not interested in background downloads.