2016-11-30 510 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凱斯28秒。

我已經搜索了它,並試圖刪除layerInsctuction變換,但沒有效果。

試圖添加: assetExport.shouldOptimizeForNetworkUse = false 也沒有效果。

此外,試圖設置AVAssetExportPresetPassthroughAVAssetExportSession,在這種情況下視頻出口1秒但標籤已經消失。

任何幫助將不勝感激,因爲我陷入困境。謝謝你的時間。

+0

您正在做兩件耗時的事情:導出和複製到相冊中。哪一個花了很長時間? – matt

+0

另外:當某些事情需要很長時間並且您想知道爲什麼時,請使用儀器。你嘗試過嗎?它告訴你什麼? – matt

+0

@matt基本上,出口需要很多時間。複製到照片專輯需要1秒。我還沒有使用過儀器,當我運行儀器時,是否應該使用「活動監視器」? – mikle94

回答

1

我能想到的唯一方法是通過比特率和分辨率來降低質量。

這是通過作用於AssetExporter的videoSettings字典完成的,這工作我不得不使用一個名爲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(compatibleWith: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已經死了。這不是複製到用戶照片庫的方式。使用Photo框架。

  • 你原來的代碼很奇怪,因爲還有很多你沒有測試的其他案例。你只是,假設default表示.completed。這很危險。

+0

Ty爲您的回覆,我會改變這一點。 – mikle94

+0

不用擔心。對不起,這不是一個真正的答案。我可能不得不刪除它。 :)但是沒有辦法在評論中說出所有這些。 – matt