2017-01-09 12 views
2

Я начинаю говорить, что я потратил много времени на поиск документации, сообщений здесь и где-то еще, но я не могу найти решение этой проблемы.Экспорт mp4 через AVAssetExportSession завершается неудачно

Я использую AVAssetExportSession для экспорта файла .mp4, хранящегося в экземпляре AVAsset. Что я делаю:

  • Я проверить isExportable свойство AVAsset
  • Затем я получить массив exportPresets совместимый с AVAsset например
  • Я беру AVAssetExportPreset1920x1080, или, если не существует, я стараюсь экспортировать носитель с AVAssetExportPresetPassthrough (FYI, 100% от времени, заданный мне нужно всегда содержится в списке, но я попытался также сквозной вариант, и он все равно не работает)

outputFileType - AVFileTypeMPEG4, и я также попытался назначить расширение .mp4 файлу, но ничто не заставляет его работать. я всегда получаю эту ошибку

Ошибка домена = AVFoundationErrorDomain Code = -11838 "Операция" остановленного UserInfo = {NSUnderlyingError = 0x600000658c30 {Error Domain = NSOSStatusErrorDomain Код = -12109 "(нуль)"}, NSLocalizedFailureReason = операция не поддерживается для этого сред., NSLocalizedDescription = операция Остановился}

Ниже приведен код, я использую

func _getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) ->()) { 
    guard item.asset.isExportable else { 
     completion(nil) 
     return 
    } 

    let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: item.asset) 
    var preset: String = AVAssetExportPresetPassthrough 
    if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 } 

    guard 
     let exportSession = AVAssetExportSession(asset: item.asset, presetName: preset), 
     exportSession.supportedFileTypes.contains(AVFileTypeMPEG4) else { 
     completion(nil) 
     return 
    } 

    var tempFileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false) 
    tempFileUrl = URL(fileURLWithPath: tempFileUrl.path) 

    exportSession.outputURL = tempFileUrl 
    exportSession.outputFileType = AVFileTypeMPEG4 
    let startTime = CMTimeMake(0, 1) 
    let timeRange = CMTimeRangeMake(startTime, item.duration) 
    exportSession.timeRange = timeRange 

    exportSession.exportAsynchronously { 
     print("\(exportSession.error)") 
     let data = try? Data(contentsOf: tempFileUrl) 
     _ = try? FileManager.default.removeItem(at: tempFileUrl) 
     completion(data) 
    } 
} 

ответ

6

Похоже, что конвертация AVAsset экземпляра в AVMutableComposition сделала трюк. Если, пожалуйста, кто-нибудь знает причину, почему это работает, дайте мне знать.

Это новая реализация _getDataFor(_:completion:) метод

func _getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) ->()) { 
    guard item.asset.isExportable else { 
     completion(nil) 
     return 
    } 

    let composition = AVMutableComposition() 
    let compositionVideoTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid)) 
    let compositionAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid)) 

    let sourceVideoTrack = item.asset.tracks(withMediaType: AVMediaTypeVideo).first! 
    let sourceAudioTrack = item.asset.tracks(withMediaType: AVMediaTypeAudio).first! 
    do { 
     try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceVideoTrack, at: kCMTimeZero) 
     try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceAudioTrack, at: kCMTimeZero) 
    } catch(_) { 
     completion(nil) 
     return 
    } 

    let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition) 
    var preset: String = AVAssetExportPresetPassthrough 
    if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 } 

    guard 
     let exportSession = AVAssetExportSession(asset: composition, presetName: preset), 
     exportSession.supportedFileTypes.contains(AVFileTypeMPEG4) else { 
     completion(nil) 
     return 
    } 

    var tempFileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false) 
    tempFileUrl = URL(fileURLWithPath: tempFileUrl.path) 

    exportSession.outputURL = tempFileUrl 
    exportSession.outputFileType = AVFileTypeMPEG4 
    let startTime = CMTimeMake(0, 1) 
    let timeRange = CMTimeRangeMake(startTime, item.duration) 
    exportSession.timeRange = timeRange 

    exportSession.exportAsynchronously { 
     print("\(tempFileUrl)") 
     print("\(exportSession.error)") 
     let data = try? Data(contentsOf: tempFileUrl) 
     _ = try? FileManager.default.removeItem(at: tempFileUrl) 
     completion(data) 
    } 
} 
+0

Работает ли он на ios 11? – JSBach

+0

Привет, @ Dincer, извините, я до сих пор не пробовал это на iOS11, я остановился, чтобы поработать над этим проектом. –

+0

Да, это очень странно, но использование композиции решает проблему. – mojuba

0

Проверьте, если вы установите делегируют свойство для AVURLAsset правильно.

[self.playerAsset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()]; 

И соответствовать протоколу AVAssetResourceLoaderDelegate. Это все, что вам нужно.

 Смежные вопросы

  • Нет связанных вопросов^_^