如何解决使用iCloud PHAsset请求AVAsset会返回没有VideoTracks的AVAsset
最近,开始注意到从照片库导入某些资产时未显示它们。有问题的资产存储在iCloud中,并且不会缓存在设备上。 我认为这是一个iOS 14问题,因为我从未在iOS 13上遇到过此问题。(由于无法将个人设备推广到iOS 13,因此我不确定100%。
这是我在 iOS 14 中所做的事情:
- 使用新的选择器视图控制器导入视频资产
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = PHPickerFilter.videos
let videoPickerController = PHPickerViewController(configuration: configuration)
videoPickerController.delegate = self
present(videoPickerController,animated: true,completion: nil)
func picker(_ picker: PHPickerViewController,didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true,completion: nil)
guard results.count > 0 else {
return
}
guard let firstPHAssetIdentifier = results.first?.assetIdentifier else {
fatalError("No asset identifier")
}
let fetchOptions = PHFetchOptions()
guard let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [firstPHAssetIdentifier],options: fetchOptions).firstObject else {
fatalError("No matching PHAsset")
}
guard phAsset.mediaType == .video else {
fatalError("Asset not of the video type")
}
}
- 然后,我为匹配的PHAsset请求一个AVAsset
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
options.progressHandler = { progress,_,_ in
print("Progress: \(progress)")
}
PHCachingImageManager.default().requestAVAsset(forVideo: phAsset,options: options) { [weak self] avAsset,info in
guard info?[PHImageCancelledKey] == nil && info?[PHImageErrorKey] == nil else {
print("Error or cancelled. Info: \(String(describing: info))")
return
}
guard let avAsset = avAsset else {
print("Asset is nil. Info: \(String(describing: info))")
return
}
guard let videoTrack = avAsset.tracks(withMediaType: .video).first else {
print("Cound not extract video track from AvAsset") // <- My issue
return
}
}
问题:通常,当资产来自iCloud时,我没有videoTrack。 avAsset.duration也将为 0.0 。
我将看到下载进度,但是我将保留在最后一个保护声明中。 有时,一旦资产下载完毕并无法加载videoTrack,重试就会立即失败(它不会尝试再次加载资产,似乎已损坏)。它会落在最后一个保护声明中。
我注意到在PHVideoRequestOptions上使用deliveryMode = .highQualityFormat可以正常工作,但我宁愿只下载720p视频,而不下载高质量视频。
我想我在这里做错了。我应该这样从PHPickerResult获取phAsset吗?任何指针/帮助将不胜感激。 我创建了此仓库以复制https://github.com/SwiftRabbit/RequestAVAssetIssues/tree/main/RequestAVAssetIssues。不能100%复制,而必须是不再在设备上的iCloud视频。
其他注释/尝试:
- 我在使用PHCachingImageManager和PHImageManager时遇到了同样的问题
- 某些流行的应用似乎对某些资产(例如Instagram,TikTok ...)存在相同的问题
- PHCachingImageManager.default()。requestPlayerItem也不起作用。它返回一个不包含任何轨道的AVPlayerItem。
- iPhone XS,企业托管设备
解决方法
我能够通过在 iPad 上录制视频来重现该问题,等到它同步到 iCloud,然后在我的 iPhone SE 2016 上请求 AVAsset。我在 github 上创建了一个 repo 来说明这一点。这是一个Objective-C项目,但结论对于Swift应该是一样的。存储库的重要部分位于 https://github.com/Jan-E/ikyle.me-code-examples/blob/master/Photo%20Picker%20ObjC/Photo%20Picker%20ObjC/PHPickerController.m
这是我使用的代码:
for (PHAsset *phAsset in assetResults) {
float recordingDuration = phAsset.duration;
NSLog(@"recordingDuration %f",recordingDuration);
NSDate *PHAssetCreationDate = phAsset.creationDate;
NSLog(@"PHAssetCreationDate %@",PHAssetCreationDate);
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.networkAccessAllowed = NO;
[[PHImageManager defaultManager] requestAVAssetForVideo:phAsset
options:options
resultHandler:^(AVAsset *avAsset,AVAudioMix *audioMix,NSDictionary *info) {
NSURL *videoURL = (NSURL *)[[(AVURLAsset *)avAsset URL] fileReferenceURL];
NSLog(@"videoURL absoluteString = %@",[videoURL absoluteString]);
NSLog(@"videoURL relativePath = %@",[videoURL relativePath]);
AVURLAsset *avUrl = [AVURLAsset assetWithURL:videoURL];
CMTime time = [avUrl duration];
float recordingDuration;
recordingDuration = time.value/time.timescale;
NSArray *tracks = [avUrl tracksWithMediaType:AVMediaTypeVideo];
NSLog(@"duration %f,tracks %lu",recordingDuration,(unsigned long)tracks.count);
}];
}
当视频仍然只在 iCloud 上时,这些是 NSLog 返回的结果:
- phAsset.duration 返回正确的值
- phAsset.creationDate 返回正确的值
- avAsset URL 返回(空)
- avAsset 的持续时间为 0.0 秒
- avAsset 的轨道数为 0
通过使用 result.itemProvider loadFileRepresentationForTypeIdentifier
,您可以强制将视频从 iCloud 下载到设备。那么结果如下:
- phAsset.duration 返回正确的值
- phAsset.creationDate 返回正确的值
- avAsset URL 返回正确的路径
- avAsset 的持续时间是正确的值
- avAsset 的轨道数为 1
完整的 NSLog 输出位于 2 次提交中的最后一次下方的 2 条注释中:https://github.com/Jan-E/ikyle.me-code-examples/commit/2d0174c2766abf742b6d76d053abc0a46199358f
编辑:请注意,在设备上没有视频的示例中,从 iCloud 下载 1.2GB 视频需要 4:23 分钟 (2020-12-24 15:45:13.669505 +0100 - 2020-12-24 15:49:36.224240+0100)。并非在所有情况下都需要下载。 phAsset.duration 的值为 680.044983 秒的信息可能是您应用中需要的所有信息,因为这意味着该视频至少有 1 个轨道。
编辑 2:通过使用 options:nil
,我使用了隐式 networkAccessAllowed = NO
。通过编辑,我明确表示。它也可能对持续时间 0.0 和跟踪 0 给出解释。如果允许网络访问,但网络连接失败怎么办?然后你仍然会得到一个有意义的 phAsset.duration 值(来自早期的 iCloud 同步),但没有 avAsset 的实时值。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。