Не удается изменить метаданные из PHAsset с помощью mediaType Video

Я пытаюсь добавить/изменить метаданные из PHAsset с помощью mediaType == .video. Я нашел несколько вопросов, относящихся к аналогичной проблеме:

Как изменить метаданные видео с помощью AVAssetWriter?

Добавление пользовательских метаданных к видео с помощью 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)"}}

person Marcel T    schedule 29.06.2017    source источник


Ответы (1)


Я нашел свою ошибку. Чтобы изменить метаданные PHAsset с помощью MediaType MediaType.video, вы можете использовать следующий фрагмент, где self — это PHAsset:

Сначала вам нужно создать PHContentEditingOutput, вы можете сделать это, запросив PHContentEditingInput из PHAsset, который вы хотите изменить. При изменении PHAsset вы также должны установить значение .adjustmentData для 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.

ПРИМЕЧАНИЕ:

Теперь, когда вы получаете доступ к видео через PHImageManager-метод .requestAVAsset(forVideo:,options:,resultHandler:), вы должны передать PHVideoRequestOptions-объект с переменной .version, установленной на .current. Он установлен как значение переменной по умолчанию, но если вы измените его на .original, вы получите неизмененное видео с помощью этого метода.

person Marcel T    schedule 11.07.2017