3

I am working on Downloading and playing HLS content, To download the HLS I am using following code

func downloadTask() {

let videoUrl =  URL(string: "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8")!

    configuration = URLSessionConfiguration.background(withIdentifier: downloadIdentifier)
    downloadSession = AVAssetDownloadURLSession(configuration: configuration!, assetDownloadDelegate: self, delegateQueue: OperationQueue.main)

    let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    destinationUrl = documentsDirectoryURL.appendingPathComponent(videoUrl.lastPathComponent)


    var urlComponents = URLComponents(
        url: videoUrl,
        resolvingAgainstBaseURL: false
        )!
    urlComponents.scheme = "https"
    do {
        let asset = try AVURLAsset(url: urlComponents.url!)
        asset.resourceLoader.setDelegate(self, queue: DispatchQueue(label: "com.example.AssetResourceLoaderDelegateQueue"))

        if #available(iOS 10.0, *) {
            assetDownloadTask = downloadSession!
                .makeAssetDownloadTask(
                    asset: asset,
                    assetTitle: "RG-TVVideo",
                    assetArtworkData: nil,
                    options: nil
            )
            APP_DELEGATE.isProgressRunning = true
            assetDownloadTask?.resume()
        } else {
            // Fallback on earlier versions
        }
    } catch { print("Erorr while parsing the URL.") }
}

Download finished

 func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {

    if #available(iOS 11.0, *) {
        let storageManager = AVAssetDownloadStorageManager.shared()
        let newPolicy = AVMutableAssetDownloadStorageManagementPolicy()
        newPolicy.expirationDate = Date()
        newPolicy.priority = .important
        let baseURL = URL(fileURLWithPath: NSHomeDirectory())
        let assetURL = baseURL.appendingPathComponent(location.relativePath)
        storageManager.setStorageManagementPolicy(newPolicy, for: assetURL)

        UserDefaults.standard.set(location.relativePath, forKey: "videoPath")
        strDownloadStatus = "5"
        let dictVideoInfo = ["strDownloadStatus" : "5","VideoID":self.strID]

// Here I am Storing Downloaded location in to database 
            DBManager.shared.updateVideoStatus(strVideoID: APP_DELEGATE.arrTempVideoIds.object(at: 0) as! String, strStatus: "5", strSavePath: location.relativePath) { (status) in }

        DispatchQueue.main.async {
            NotificationCenter.default.post(name: NSNotification.Name.init("UpdateProgress"), object: self.percentageComplete, userInfo: dictVideoInfo)
        }

    }
}

Now I am trying to get Video path from the location which is stored in Database and trying to play it offline(Without Internet) using following code

  func setLocalPlayer(strDownloadPath: String) {

    let strDownloadPath = “”

    //Getting path from database
    DBManager.shared.getDownloadedPath(videoID:  VideoID) { (strPath) in
        strDownloadPath = strPath
    }

    activityIndicator.isHidden = false

    let baseURL = URL(fileURLWithPath: NSHomeDirectory())
    let assetURL = baseURL.appendingPathComponent(strDownloadPath)
    let asset = AVURLAsset(url: assetURL)

    //        if let cache = asset.assetCache, cache.isPlayableOffline {
    //            let videoAsset = AVURLAsset(url: assetURL)
    asset.resourceLoader.preloadsEligibleContentKeys = true

    asset.resourceLoader.setDelegate(self, queue: DispatchQueue(label: "com.example.AssetResourceLoaderDelegateQueue"))
    let playerItem = AVPlayerItem(asset: asset)
    avPlayer = AVPlayer(playerItem: playerItem)
    avPlayerLayer = AVPlayerLayer()
    avPlayerLayer.frame = CGRect(x: 0, y: 0, width: playerContainer.frame.width, height: playerContainer.frame.height)
    avPlayerLayer.videoGravity = .resize

    avPlayerLayer.player = avPlayer

    playerContainer.layer.addSublayer(avPlayerLayer)

    let interval = CMTime(seconds: 0.01, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
    timeObserver = avPlayer?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsedTime in
        self.updateVideoPlayerState()

        if self.avPlayer != nil {
            self.bufferState()
        }
    })

    self.slider.setThumbImage(UIImage(named: "slider_dot"), for: UIControl.State.normal)
    resetTimer()

    avPlayer.play()
    isPlaying = true
    //        }
}

NOTE: This code is working fine when internet is on

I have referred following links

https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/HTTPLiveStreaming/HTTPLiveStreaming.html

https://assist-software.net/snippets/how-play-encrypted-http-live-streams-offline-avfoundation-ios-using-swift-4

Downloading and playing offline HLS Content - iOS 10

Please guide what I am doing wrong.

Thanks

Khush
  • 181
  • 1
  • 16
  • Hi @Khush, I know I'm late, but which error do you have ? You can check AVPlayer error with command : `po avPlayer.currentItem?.errorLog()` – vmeyer Dec 18 '19 at 18:36

1 Answers1

0

Well, I don't know if it's your error, but for further readings :

Don't do newPolicy.expirationDate = Date() it's a mistake. According to Advances in HTTP Live Streaming 2017 WWDC session, it will delete your file as soon as possible.

Before playing your offline playback, you can check if it's still on your device in Settings -> General -> Storage -> MyApp

The expiration date property is there in case your asset at some point becomes no longer eligible to be played. For instance, you may find that you may be in a situation where a particular show may be leaving your catalog, you no longer have rights to stream it.

If that's the case you can set the expiration date and it will be sort of bumped up in the deletion queue. So, using it is fairly straight forward.

Community
  • 1
  • 1
vmeyer
  • 1,998
  • 21
  • 23