使用 mediaType Video 从 PHAsset 修改元数据失败

Modifying Metadata from PHAsset with mediaType Video fails

我用 mediaType == .video 尝试 adding/modifying 来自 PHAsset 的元数据 我发现了一些涉及类似问题的问题:

How to change video metadata using AVAssetWriter?

Add custom metadata to video using AVFoundation

关于这些问题的答案,我构建了以下片段,它是 PHAsset 的扩展:

let options = PHVideoRequestOptions()
options.version = .original

PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {
    asset, audioMix, info in

    if asset != nil && asset!.isKind(of: AVURLAsset.self) {
        let urlAsset = asset as! AVURLAsset

        let start = CMTimeMakeWithSeconds(0.0, 1)
        let duration = asset!.duration                    


        var exportSession = AVAssetExportSession(asset: asset!, presetName: AVAssetExportPresetPassthrough)
        exportSession!.outputURL = urlAsset.url
        exportSession!.outputFileType = AVFileTypeAppleM4V
        exportSession!.timeRange = CMTimeRange(start: start, duration: duration)

        var modifiedMetadata = asset!.metadata

        let metadataItem = AVMutableMetadataItem()
        metadataItem.keySpace = AVMetadataKeySpaceQuickTimeUserData
        metadataItem.key = AVMetadataQuickTimeMetadataKeyRatingUser as NSString
        metadataItem.value = NSNumber(floatLiteral: Double(rating))

        modifiedMetadata.append(metadataItem)

        exportSession!.metadata = modifiedMetadata

        LogInfo("\(modifiedMetadata)")


        exportSession!.exportAsynchronously(completionHandler: {
            let status = exportSession?.status
            let success = status == AVAssetExportSessionStatus.completed
            if success {
                completion(true)
            } else {
                LogError("\(exportSession!.error!)")
                completion(false)
            }
        })
    }
})

当我执行此代码段时,exportSession 失败并出现以下错误:

Error Domain=NSURLErrorDomain 
Code=-3000 "Cannot create file" 
UserInfo={NSLocalizedDescription=Cannot create file, 
NSUnderlyingError=0x1702439f0 
{Error Domain=NSOSStatusErrorDomain Code=-12124 "(null)"}}

我发现了我的错误。要使用 MediaType MediaType.video 修改 PHAsset 的元数据,您可以使用以下代码片段,其中 selfPHAsset:

首先您需要创建一个 PHContentEditingOutput,您可以通过从要修改的 PHAsset 请求一个 PHContentEditingInput 来做到这一点。更改 PHAsset 时,您还必须设置 PHContentEditingOutput.adjustmentData 值,否则 .performChanges() 块将失败。

   self.requestContentEditingInput(with: options, completionHandler: {
        (contentEditingInput, _) -> Void in

        if contentEditingInput != nil {

            let adjustmentData = PHAdjustmentData(formatIdentifier: starRatingIdentifier, formatVersion: formatVersion, data: NSKeyedArchiver.archivedData(withRootObject: rating))

            let contentEditingOutput = PHContentEditingOutput(contentEditingInput: contentEditingInput!)
            contentEditingOutput.adjustmentData = adjustmentData
            self.applyRatingToVideo(rating, contentEditingInput, contentEditingOutput, completion: {
                output in
                if output != nil {
                    PHPhotoLibrary.shared().performChanged({
                        let request = PHAssetChangeRequest(for: self)
                        request.contentEditingOutput = output
                    }, completionHandler: {
                        success, error in
                        if !success {
                            print("can't edit asset: \(String(describing: error))")
                        }
                    })
                }
            })
        }
    })

使用上面的代码片段,您可以在修改 PHContentEditingOutput 之后更改 PHAsset,您将在下面的代码片段中看到如何设置用户评分的元数据:

private func applyRatingToVideo(_ rating: Int, input: PHContentEditingInput, output: PHContentEditingOutput, completion: @escaping (PHContentEditingOutput?) -> Void) {
    guard let avAsset = input.audiovisualAsset else { return }

    guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) else { return }

    var mutableMetadata = exportSession.asset.metadata
    let metadataCopy = mutableMetadata

    for item in metadataCopy {
        if item.identifier == AVMetadataIdentifierQuickTimeMetadataRatingUser {
            mutableMetadata.remove(object: item)
        }
    }

    let metadataItem = AVMutableMetadataItem()
    metadataItem.identifier = AVMetadataIdentifierQuickTimeMetadataRatingUser
    metadataItem.keySpace = AVMetadataKeySpaceQuickTimeMetadata
    metadataItem.key = AVMetadataQuickTimeMetadataKeyRatingUser as NSString
    metadataItem.value = NSNumber(floatLiteral: Double(rating))

    exportSession.outputURL = output.renderedContentURL
    mutableMetadata.append(metadataItem)
    exportSession.metadata = mutableMetadata
    exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession.shouldOptimizeForNetworkUse = true
    exportSession.exportAsynchronously(completionHandler: {
        if exportSession.status == .completed {
            completion(output)
        } else if exportSession.error != nil {
            completion(nil)
        }
    })
}

考虑一下,如果您不删除与要添加的标识符相同的 AVMetadataItemAVAssetExportSession 将为 [=28] 设置多个具有相同标识符的项目=].

注意:

当您现在通过 PHImageManager-方法 .requestAVAsset(forVideo:,options:,resultHandler:) 访问视频时,您必须传递一个 PHVideoRequestOptions- 对象并将 .version 变量设置为 .current.它被设置为变量的默认值,但如果将其更改为 .original,您将从该方法获得未修改的视频。