微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用iCloud PHAsset请求AVAsset会返回没有VideoTracks的AVAsset

如何解决使用iCloud PHAsset请求AVAsset会返回没有VideoTracks的AVAsset

最近,开始注意到从照片库导入某些资产时未显示它们。有问题的资产存储在iCloud中,并且不会缓存在设备上。 我认为这是一个iOS 14问题,因为我从未在iOS 13上遇到过此问题。(由于无法将个人设备推广到iOS 13,因此我不确定100%。

这是我在 iOS 14 中所做的事情:

  1. 使用新的选择器视图控制器导入视频资产
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = PHPickerFilter.videos
let videoPickerController = PHPickerViewController(configuration: configuration)
videoPickerController.delegate = self
present(videoPickerController,animated: true,completion: nil)
  1. 我从[PHPickerResult](委托方法)中提取了PHAsset
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")
  }
}
  1. 然后,我为匹配的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 返回的结果:

  1. phAsset.duration 返回正确的值
  2. phAsset.creationDate 返回正确的值
  3. avAsset URL 返回(空)
  4. avAsset 的持续时间为 0.0 秒
  5. avAsset 的轨道数为 0

通过使用 result.itemProvider loadFileRepresentationForTypeIdentifier,您可以强制将视频从 iCloud 下载到设备。那么结果如下:

  1. phAsset.duration 返回正确的值
  2. phAsset.creationDate 返回正确的值
  3. avAsset URL 返回正确的路径
  4. avAsset 的持续时间是正确的值
  5. 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 举报,一经查实,本站将立刻删除。