2016-11-30 13 views
0

Моя цель - позволить пользователю выбрать видео с фотографий, а затем позволить ему добавлять над ним надписи.Экспорт AVAssetExportSession занимает много времени

Вот что я получил:

let audioAsset = AVURLAsset(url: selectedVideoURL) 
let videoAsset = AVURLAsset(url: selectedVideoURL) 
let mixComposition = AVMutableComposition() 
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) 
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) 
let clipVideoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0] 
let clipAudioTrack = audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0] 
do { 
    try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero) 
    try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, audioAsset.duration), of: clipAudioTrack, at: kCMTimeZero) 
    compositionVideoTrack.preferredTransform = clipVideoTrack.preferredTransform 
} catch { 
    print(error) 
} 
var videoSize = clipVideoTrack.naturalSize 
if isVideoPortrait(asset: videoAsset) { 
    videoSize = CGSize(width: videoSize.height, height: videoSize.width) 
} 
let parentLayer = CALayer() 
let videoLayer = CALayer() 
parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height) 
videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height) 
parentLayer.addSublayer(videoLayer) 

// adding label 
let helloLabelLayer = CATextLayer() 
helloLabelLayer.string = "Hello" 
helloLabelLayer.font = "Signika-Semibold" as CFTypeRef? 
helloLabelLayer.fontSize = 30.0 
helloLabelLayer.contentsScale = mainScreen.scale 
helloLabelLayer.alignmentMode = kCAAlignmentNatural 
helloLabelLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 50.0) 
parentLayer.addSublayer(helloLabelLayer) 

// creating composition 
let videoComp = AVMutableVideoComposition() 
videoComp.renderSize = videoSize 
videoComp.frameDuration = CMTimeMake(1, 30) 
videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer) 

let instruction = AVMutableVideoCompositionInstruction() 
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration) 
let layerInstruction = videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset) 
instruction.layerInstructions = [layerInstruction] 
videoComp.instructions = [instruction] 
if let assetExport = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPreset640x480) { 
    let filename = NSTemporaryDirectory().appending("video.mov") 

    if FileManager.default.fileExists(atPath: filename) { 
    do { 
     try FileManager.default.removeItem(atPath: filename) 
    } catch { 
     print(error) 
    } 
} 

let url = URL(fileURLWithPath: filename) 
assetExport.outputURL = url 
assetExport.outputFileType = AVFileTypeMPEG4 
assetExport.videoComposition = videoComp 
print(NSDate().timeIntervalSince1970) 
assetExport.exportAsynchronously { 
    print(NSDate().timeIntervalSince1970) 
    let library = ALAssetsLibrary() 
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: { 
     (url, error) in 
     switch assetExport.status { 
      case AVAssetExportSessionStatus.failed: 
       p("failed \(assetExport.error)") 
      case AVAssetExportSessionStatus.cancelled: 
       p("cancelled \(assetExport.error)") 
      default: 
       p("complete") 
       p(NSDate().timeIntervalSince1970) 
       if FileManager.default.fileExists(atPath: filename) { 
        do { 
         try FileManager.default.removeItem(atPath: filename) 
        } catch { 
         p(error) 
        } 
       } 
       print("Exported")          
     } 
    }) 
} 

Реализация isVideoPortrait функции:

func isVideoPortrait(asset: AVAsset) -> Bool { 
    var isPortrait = false 
    let tracks = asset.tracks(withMediaType: AVMediaTypeVideo) 
    if tracks.count > 0 { 
     let videoTrack = tracks[0] 
     let t = videoTrack.preferredTransform 
     if t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0 { 
      isPortrait = true 
     } 
     if t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0 { 
      isPortrait = true 
     } 
     if t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0 { 
      isPortrait = false 
     } 
     if t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0 { 
      isPortrait = false 
     } 
    } 
    return isPortrait 
} 

И последняя функция video composition layer instruction:

func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction { 
    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track) 
    let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0] 
    let transform = assetTrack.preferredTransform 
    instruction.setTransform(transform, at: kCMTimeZero) 
    return instruction 
} 

код работает хорошо, выходное видео имеет метку, но если я выберу 1-минутное видео, экспортируйте ta kes 28 секунд.

Я искал его и попытался удалить layerInsctuction преобразование, но никакого эффекта.

Пытался добавить: assetExport.shouldOptimizeForNetworkUse = false никакого эффекта.

Кроме того, попытался установить AVAssetExportPresetPassthrough для AVAssetExportSession, в этом случае экспорт видео с 1 секундой, но метки исчезли.

Любая помощь будет оценена, потому что я застрял. Спасибо за ваше время.

+0

Вы занимаетесь двумя трудоемкими вещами: экспортом и копией в фотоальбом. Какой из них занимает много времени? – matt

+0

Также: когда что-то занимает много времени, и вы хотите знать, почему, используйте Инструменты. Вы попробовали это? Что он вам сказал? – matt

+0

@matt В принципе, экспорт занимает много времени. Альбом для копирования в альбом занимает 1 секунду. Я еще не использовал инструменты, должен ли я использовать «Монитор активности», когда я запускаю Инструменты, для этого? – mikle94

ответ

1

Единственный способ, я могу думать о том, чтобы уменьшить качество с помощью скорости передачи и разрешения.

Это делается с помощью словаря, приложенного к videoSettings в AssetExporter, для этой работы я должен был использовать Framework под названием SDAVAssetExportSession

Затем, изменив videoSettings я мог играть с качеством, чтобы получить оптимальное качество/скорость.

let compression = [AVVideoAverageBitRateKey : 2097152(DESIRED_BITRATE),AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel] 

    let videoSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : maxWidth, AVVideoHeightKey : maxHeight, AVVideoCompressionPropertiesKey:compression] 

Это был единственный способ ускорить процесс.

+0

Спасибо, это помогло мне сократить вдвое время. – mikle94

+0

Я не верю, что вам нужно использовать фреймворк для этого (если я чего-то не хватает), вы можете получить пресеты из '' '' AVAssetExportSession.exportPresets (совместимого с: yourVideoAsset) '' ', а затем передать их ваш экземпляр '' AVAssetExportSession''. – AdjunctProfessorFalcon

+0

Я считаю, что эти конкретные ключи несовместимы с AVAssetExportSession, вам нужно использовать нижний AVAssetWriter, который является тем, что это падение в файле происходит под капотом. – SeanLintern88

1

Это не имеет прямого отношения к вашему вопросу, но ваш код здесь в обратном направлении:

assetExport.exportAsynchronously { 
    let library = ALAssetsLibrary() 
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: { 
     switch assetExport.status { 

Нет, нет. Сначала вы завершите экспорт активов. Затем вы можете снова скопировать в другое место, если это то, что вы хотите сделать. Таким образом, это должно идти, как это:

assetExport.exportAsynchronously { 
    switch assetExport.status { 
    case .completed: 
     let library = ALAssetsLibrary() 
     library.writeVideoAtPath... 

Другие комментарии:

  • ALAssetsLibrary мертв. Это не способ копирования в библиотеку фотографий пользователя. Используйте фреймворк.

  • Ваш оригинальный код очень странный, потому что есть много других случаев, которые вы не тестируете. Вы всего лишь Предполагаете,, что default означает .completed. Это опасно.

+0

Ty для вашего ответа, я изменю этот момент. – mikle94

+0

Не беспокойтесь. Мне жаль, что это не настоящий ответ; Мне, вероятно, придется удалить его. :) Но не было никакого способа сказать все это в комментарии. – matt