3 Replies
      Latest reply on Oct 31, 2019 12:58 PM by mreyesblim
      Robert_Developer Level 1 Level 1 (0 points)

        Hi guys,

         

        I have implemented download manager to support multiple iOS versions and 2 types of videos - individual files (eg. MP4) and HLS streams (based on m3u8 manifests) for offline playback. I used following solution:

         

        1. iOS 9 - URLSessionDownloadTask for MP4

        2. iOS 10 - URLSessionDownloadTask for MP4

        3. iOS 10 - AVAssetDownloadTask for HLS

        4. iOS 11, 12 - URLSessionDownloadTask for MP4

        5. iOS 11, 12 - AVAggregateAssetDownloadTask for HLS

         

        Solution 5 supports also downloading all audio and subtitle tracks, but I need to implement the same for iOS 10, but iOS 10 SDK doesn't support AVAggregateAssetDownloadTask.

         

        I used AVAssetDownloadTask and approach described here:

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

         

        However downloading additional assets doesn't work and I'm getting an error:

         

        (String) $R2 = "Optional(Error Domain=AVFoundationErrorDomain Code=-11800 \"The operation could not be completed\" UserInfo={NSLocalizedDescription=The operation could not be completed, NSLocalizedFailureReason=An unknown error occurred (-12782)})"

         

        I tried following approaches:

         

        SOLUTION 1:

        - The main download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, destinationURL: destinationURL, options: nil)

        - Asset download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, assetTitle: option.displayName, assetArtworkData: nil, options: options)

         

        In this case I download the main part (video and default audio) into destination folder.

        Then I try to download other tracks, hoping that AVURLAsset knows it needs to add downloaded content into destination folder.

         

         

        SOLUTION 2:

        - The main download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, assetTitle: option.displayName, assetArtworkData: nil, options: nil)

        - Asset download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, assetTitle: option.displayName, assetArtworkData: nil, options: options)

         

        Here I leave asset download task to handle destination and just take care of setting options.

         

         

        SOLUTION 3:

        - The main download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, assetTitle: option.displayName, assetArtworkData: nil, options: nil)

        - Asset download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, destinationURL: destinationURL, options: options)

         

        In this case I download the main part (video and default audio) and leave the download task to inform me of the destination.

        Then I try to download other tracks to the same destination.

         

         

        SOLUTION 4:

        - The main download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, destinationURL: destinationURL, options: nil)

        - Asset download: makeAssetDownloadTask(asset: assetDownloadTask.urlAsset, destinationURL: destinationURL, options: options)

         

        In this case I download the main part (video and default audio) into destination folder.

        Then I try to download other tracks to the same destination (it's a folder, so it could just add additional bits and pieces).

         

         

        No matter what I do, when trying to download additional assets using existing AVURLAsset I get the error mentioned above. The only way I get additional assets downloaded is creating new instance of AVURLAsset, but in that case video gets downloaded as well, so if there are 4 subtitle tracks, I get video download 4 times. I could merge all downloads and it would probably work, but it's waste of bandwidth and takes too long. I tried setting AVAssetDownloadTaskMinimumRequiredMediaBitrateKey when downloading additional tracks, but it still downloads video as well.

         

        I guess when I create new instance of AVURLAsset I would also need to set property 'assetCache' to the instance held by previous AVURLAsset, but it's read only property.

        Having cache info in AVURLAsset makes it download only what's missing - similar to playback, when switching audio track or subtitle track.

         

        Do you think I should create derived class based on AVURLAsset, so I could update the cache reference?

        I have already wasted too much time on this and don't want to start another round of experiments using trial & error approach.

         

        I was also thinking about using AVPlayerItem and loading assets into cache this way, but that's awkward and really bad way to implement download of HLS streams.

         

        Another approach would be parsing m3u8 and use URLSessionDownloadTask for all fragments, but that's overkill.

         

        Last resort would be telling clients that additional audio and subtitle tracks are not supported on iOS 10 (well it really isn't, since Apple introduced Aggregate Downloads on iOS 11), but they may still have quite a few subscribers on iOS 10.

         

        Any idea how to resolve this?

         

        Thanks for any advice on iOS 10 HLS downloads.