修改元数据失败

问题描述:

我尝试添加/与mediaType == .video修改从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修改PHAsset的元数据MediaType.video你可以用下面的代码片段,其中selfPHAsset

首先,你需要创建一个PHContentEditingOutput你可以做到这一点,要求从PHContentEditingInputPHAsset要修改。当更改PHAsset时,还必须设置的值PHContentEditingOutput否则.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))") 
         } 
        }) 
       } 
      }) 
     } 
    }) 

通过上面的代码片段,您可以更改PHAsset修改下面的PHContentEditingOutput后段,您会看到,如何设置的元数据的用户评级:

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设置多个具有相同标识符的项目。

注:

现在,当您访问过你必须通过一个PHVideoRequestOptions -object与.version变量设置为.currentPHImageManager - 方法.requestAVAsset(forVideo:,options:,resultHandler:)视频。它被设置为变量的默认值,但如果将其更改为.original,则将从该方法获取未修改的视频。