对于iPhone SE上的高FPS视频,PHAssetChangeRequest.creationRequestForAssetFromVideo(url:)`失败

时间:2018-01-13 20:33:04

标签: ios iphone video photokit phphotolibrary

我们已经收到有关在我们的应用程序中录制慢动作视频的问题的报告。我们已经在iPhone X,iPhone 6和iPhone SE上测试了这个问题。 6和X都工作正常,但在尝试将录制的视频添加到照片时SE失败。

要添加到照片的视频文件:

  • h.264,推荐设置
  • Quicktime(.mov)
  • 120/200/240 FPS
  • 没有自定义元数据
  • 推荐设置的AAC音频

我们添加视频的代码:

PHPhotoLibrary.shared().performChanges {  
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)  
}  

返回的错误没有提供太多有用的信息,这在处理照片时似乎是一个反复出现的问题。

Error Domain=NSCocoaErrorDomain Code=-1 "(null)"  

我们使用编码器设置的干净光圈参数为视频应用宽高比。显然,更改视频宽高比会影响结果(请参阅底部的列表)。

我们尝试过:

  • 由于宽高比影响了结果,我们认为问题可能与要存储的数据量有关。降低比特率/文件大小并未改变任何内容
  • 也许有些东西还在使用这个文件?我们在添加文件之前等了几秒钟,但被授予相同的错误
  • 搜索文档,开发论坛,SO,博客和一般谷歌无济于事

再一次 - iPhone X和6上的一切正常。

分辨率-fps-ratio组合及其结果:

1080

  • 120 @ 16:9 - 错误
  • 120@2.35 - 错误
  • 120 @ 1:1 - 好的

720P

  • 240 @ 16:9 - 错误
  • 240@2.35 - 错误
  • 240 @ 1:1 - 好的
  • 200 @ 16:9 - 错误
  • 200@2.35 - 好的
  • 200 @ 1:1 - 好的
  • 120 @ 16:9 - 好的
  • 120@2.35 - 好的
  • 120 @ 1:1 - 好的

您是否知道问题可能是什么?

4 个答案:

答案 0 :(得分:0)

我也有这个问题。我通过在授权块内部处理请求来解决它。一旦用户响应了提示,您的performChanges块将被调用并且它应该成功:

PHPhotoLibrary.requestAuthorization { (status) in 
  PHPhotoLibrary.shared().performChanges {  
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)  
  }
}

答案 1 :(得分:0)

编辑#4

好吧,这个家伙再次咬我,我可能又花了两个小时来尝试弄清它是如何回归的,因为代码未被触及并且先前已确认可以正常工作。我最初以为它必须与设备或编码有关。我最终“修复”了它,但对我来说意义非零。在我将.mov附加到下载后用于存储远程视频的本地文件后,此问题已解决。为什么将任意扩展名附加到文件路径会有所作为,这超出了我的范围。

let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let temporaryFilename = "\(ProcessInfo().globallyUniqueString).mov"
let fileURL = documentsURL.appendingPathComponent(temporaryFilename)

答案 2 :(得分:0)

我一直在与这个晦涩的错误作斗争一段时间,才找到解决方案。

修复的问题是:我必须通过AVAssetExportSession来确保用于合成视频的导出图像的Width x Height高度,与使用的质量设置匹配。我的代码将图像大小调整为1200 x 1920(对于我而言,这是不正确的分辨率)。产生的视频尺寸为1080 x1920。因此AVAssetExportSession能够很好地导出具有错误分辨率的图像,但是该视频无法保存到图库中,从而导致此通用错误。一旦使用了正确的分辨率图像(1080w x 1920h),使用PHPhotoLibrary.shared()。performChanges进行的导出在iPhone 5s和其他手机上就可以正常工作。

答案 3 :(得分:0)

我最近一直在解决此错误,我能够通过导出文件而不使用预置名称AVAssetExportPresetPassthrough来解决该问题,而是使用AVAssetExportPresetHighestQuality导出。

通过这种方式,文件将被导出为当前设备可以处理的最高质量(在不牺牲原始文件的前提下),从而不会将文件导入Cameraroll

private func encodeVideo(at videoURL: URL, completion: ((Swift.Result<URL, Error>) -> Void)?) {

    let avAsset = AVURLAsset(url: videoURL, options: nil)

    // Create Export session BUT WITH AVAssetExportPresetHighestQuality 
    guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetHighestQuality) else {
        completion?(.failure(VideoEncodingError
            .invalidAVAssetSession))
        return
    }

    // Creating temp path to save the converted video
    let documentsDirectory = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL)
    let fileName = "\(UUID().uuidString).\(MCConstants.BGDownload.mp4Extension)"
    let filePath = documentsDirectory.appendingPathComponent(fileName)

    exportSession.outputURL = filePath
    exportSession.outputFileType = AVFileType.mp4
    exportSession.shouldOptimizeForNetworkUse = true
    let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)
    let range = CMTimeRangeMake(start: start, duration: avAsset.duration)
    exportSession.timeRange = range

    exportSession.exportAsynchronously {
        switch exportSession.status {
        case .failed: completion?(.failure(exportSession.error ?? VideoEncodingError.unknown))
        case .cancelled: completion?(.failure(VideoEncodingError.cancelled))
        case .completed:

            if let _url = exportSession.outputURL {
                completion?(.success(_url))
            } else {
                completion?(.failure(VideoEncodingError.encodedURLUnavailable))
            }

        default: completion?(.failure(VideoEncodingError.unknown))
        }
    }
}