使用 mediaType Video 从 PHAsset 修改元数据失败
Posted
技术标签:
【中文标题】使用 mediaType Video 从 PHAsset 修改元数据失败【英文标题】:Modifying Metadata from PHAsset with mediaType Video fails 【发布时间】:2017-06-29 12:18:16 【问题描述】:我尝试使用mediaType == .video
从PHAsset
添加/修改元数据我发现一些问题涉及类似问题:
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,其中self
是PHAsset
:
首先,您需要创建一个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 修改元数据失败的主要内容,如果未能解决你的问题,请参考以下文章
无法解析javax.ws.rs.core.MediaType类型
RestTemplate Spring 启动中的 NullPointerException MediaType
AVFoundation MediaTypes 中每个常量的含义的任何示例
Jersey REST 客户端 - 将自定义 MediaType 视为 MediaType.APPLICATION_JSON