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

Posted

技术标签:

【中文标题】使用 mediaType Video 从 PHAsset 修改元数据失败【英文标题】:Modifying Metadata from PHAsset with mediaType Video fails 【发布时间】:2017-06-29 12:18:16 【问题描述】:

我尝试使用mediaType == .videoPHAsset 添加/修改元数据我发现一些问题涉及类似问题:

How to change video metadata using AVAssetWriter?

Add custom metadata to video using AVFoundation

关于这些问题中的答案,我构建了以下 sn-p,它是 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)
            
        )
    
)

当我执行这个 sn-p 时,exportSession 失败并出现以下错误:

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

【问题讨论】:

【参考方案1】:

我发现了我的错误。要使用MediaType MediaType.video 修改PHAsset 的元数据,可以使用以下sn-p,其中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))")
                        
                    )
                
            )
        
    )

使用上面的sn-p,你在修改PHContentEditingOutput之后修改PHAsset,下面的sn-p你会看到,如何设置用户评分的元数据:

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)
        
    )

请考虑,如果您不删除具有与您要添加的标识符相同的标识符的AVMetadataItem,则AVAssetExportSession 将为AVAsset 设置具有相同标识符的多个项目。

注意:

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

【讨论】:

感谢分享一个好的解决方案!但是我不明白这部分,PHAdjustmentData(formatIdentifier: starRatingIdentifier, formatVersion: formatVersion, data: NSKeyedArchiver.archivedData(withRootObject: rating)),尤其是NSKeyedArchiver.archivedData(withRootObject: rating)。为什么使用rating 值? PHContentEditingoutput 强制要求。一般来说,它背后的计划是,任何在编辑视频或照片数据时进行另一次迭代的应用程序都可以读取这些信息并进行处理。当您添加另一个过滤器时,这可能特别有用。在这种情况下,我只添加它来描述对资产所做的更改。 NSKeyedArchiver 不是强制性的,它只是将评级对象转换为 data 的一种简单方法,但您可以使用其他方式来归档,特别是如果您想添加可编码对象。

以上是关于使用 mediaType Video 从 PHAsset 修改元数据失败的主要内容,如果未能解决你的问题,请参考以下文章

正确使用 Apache Tika MediaType

无法解析javax.ws.rs.core.MediaType类型

RestTemplate Spring 启动中的 NullPointerException MediaType

AVFoundation MediaTypes 中每个常量的含义的任何示例

奏鸣曲媒体包修改 MediaType

Jersey REST 客户端 - 将自定义 MediaType 视为 MediaType.APPLICATION_JSON