AVAssetExportSession fails to export videos downloaded from iCloud

I'm trying to create a scaled down version of a video selected from the users photo album. The max dimensions of the output will be 720p. Therefore, when retrieving the video, I'm using the .mediumQualityFormat as the deliveryMode.

This causes iOS to retrieve a 720p video from iCloud if the original video or its medium quality version don't exist in the users device.

Code Block swift
let videoRequestOptions = PHVideoRequestOptions()
videoRequestOptions.deliveryMode = .mediumQualityFormat
videoRequestOptions.isNetworkAccessAllowed = true
PHImageManager.default().requestAVAsset(forVideo: asset, options: videoRequestOptions) { (asset, audioMix, info) in
// Proceess the asset
}

The problem is, when I use AVAssetExportSession to create a scaled down version of the asset, if the asset is a medium variant and not the original version, the export process fails immediately with the following error:

Code Block
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-17507), NSLocalizedDescription=İşlem tamamlanamadı, NSUnderlyingError=0x283bbcf60 {Error Domain=NSOSStatusErrorDomain Code=-17507 "(null)"}}

I couldn't find anything about the meaning of this error anywhere.

When I set the deliveryMode property to .auto or .highQualityFormat, everything is working properly.

When I checked the asset url's, I noticed that if the video has been retrieved from iCloud, its filename has a ".medium" postfix like in this example:

Code Block
file:///var/mobile/Media/PhotoData/Metadata/PhotoData/CPLAssets/group338/191B2348-5E19-4A8E-B15C-A843F9F7B5A3.medium.MP4

The weird thing is, if I use FileManager to copy the video in this url to another directory, create a new AVAsset from that file, and use that asset when creating the AVExportSession instance, the problem goes away.

I'd really appreciate if someone could provide some insight about what the problem could be.

This is how I use AVAssetExportSession to create a scaled down version of the original video.

Code Block swift
let originalVideoURL = "The url of the asset retrieved from requestAVAsset"
let outputVideoPath = NSTemporaryDirectory() + "encodedVideo.mp4"
let outputVideoURL = URL(fileURLWithPath: outputVideoPath)
guard
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality),
let videoTrack = asset.tracks(withMediaType: .video).first else {
handleError()
return
}
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = scaledSize
videoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
let transform = videoTrack.preferredTransform
layerInstruction.setTransform(transform, at: .zero)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(start: .zero, duration: asset.duration)
instruction.layerInstructions = [layerInstruction]
videoComposition.instructions = [instruction]
exportSession.videoComposition = videoComposition
exportSession.outputURL = outputVideoURL
exportSession.outputFileType = .mp4
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously(completionHandler: {[weak self] in
guard let self = self else { return }
if let url = exportSession.outputURL, exportSession.status == .completed {
// Works for local videos
} else {
// Fails with error code 17507 when loading videos with delivery size "Medium"
}
})


Replies

Looks like a sandboxing error.
Could you please elaborate on that?

The video is at a URL my app is allowed to access. That's why I can copy it to another location and make things work. I don't see the difference between creating an ExportSession using the URL of the original, locally stored version of a video and the URL of its medium variant that was downloaded from iCloud.

Thanks.
That's just an observation based on the error code. I don't know what might be causing it. Filing a bug report using the feedback assistant is likely the best way to get the bottom of it.
Hey, I've run into this problem and I've found that using
Code Block
let strongReferenceToDelegate = ResourceLoaderDelegate()
let asset = /* some AVAsset */
(asset as? AVURLAsset)?.resourceLoader.setDelegate(strongReferenceToDelegate, queue: .main)

where ResourceLoaderDelegate is simply:
Code Block
extension ResourceLoaderDelegate: AVAssetResourceLoaderDelegate {
}

resolved the issue for me. Unfortunately I couldn't tell you why this works, I suspect it's a bug with the implementation. If this works for you, and you can figure out whats going on, please let us know as I'd like to understand this as well.
Nope, that didn't change anything in my case.

@cihant Were you able to fix this issue?