I'm trying to use AVAssetDownloadTask to download and play FairPlay-encrypted audio content offline. I kept getting an error like this in urlSession:task:didCompleteWithError::

Error Domain=AVFoundationErrorDomain Code=-11863 "Operation Stopped" UserInfo={NSLocalizedFailureReason=This content is no longer available., NSLocalizedDescription=Operation Stopped}

My flow was:

All of this worked, and was done in the same way as in Apple's HLSCatalog sample code. But downloading would still give me the above error, even though plugging the same playlist and key URLs into the sample code would download fine.

What I finally figured out was that AVAssetDownloadTask will only download an AVURLAsset instance that has already been streamed and given its decryption keys (via the AVAssetResourceLoaderDelegate) and that is not associated with a player. I can't just make a new AVURLAsset using the same URL as what is already playing and download it. So it seems that in order to download arbitrary FairPlay content, I have to:

  • Make an AVURLAsset
  • Make an AVPlayer and set its volume to 0
  • Give it the asset and play it
  • Wait until it requests its keys from the resource loader and starts playing
  • Give it to a download task and deassociate it with the player

But this seems horrible. It can't be true.

So, my question: How do I download a FairPlay-encrypted AVURLAsset without having streamed that specific instance of it before?

1

There are 1 answers

1
Tom Hamming On BEST ANSWER

Turns out you set preloadsEligibleContentKeys to true on the asset's resource loader. Then you can download:

AVURLAsset *asset = [AVURLAsset assetWithURL:self.currDownload.url];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
asset.resourceLoader.preloadsEligibleContentKeys = YES;
AVAssetDownloadTask *task = [self.downloadSession assetDownloadTaskWithURLAsset:asset assetTitle:self.currDownload.title assetArtworkData:nil options:@{AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: @(265000)}];
task.taskDescription = self.currDownload.title;
[task resume];