2016-05-16 50 views
1

將視頻文件添加到濾鏡的最佳方法是什麼? 我是否必須從視頻中提取所有幀,然後將濾鏡添加到每個幀?或者有更好的方法來解決這個問題。如何將CIFilter添加到從畫廊導入的視頻文件

遇到了這一點,但不知道如何使用它

let vidComp = AVVideoComposition(asset: self.asset, applyingCIFiltersWithHandler: { 
    request in 
    }) 

請幫助。

回答

1

這是一個相當漫長的過程。你坐得舒服嗎?然後我開始...

作爲我VideoEffects項目的一部分,我創建了一個名爲FilteredVideoVendor的類,它從電影文件中提取過濾後的圖像幀。

供應商類別的第一份工作是真正從「加載」按鈕,在控制面板中提供的網址打開一個電影:

func openMovie(url: NSURL){ 
player = AVPlayer(URL: url) 

guard let player = player, 
    currentItem = player.currentItem, 
    videoTrack = currentItem.asset.tracksWithMediaType(AVMediaTypeVideo).first else { 
    fatalError("** unable to access item **") 
} 

currentURL = url 
failedPixelBufferForItemTimeCount = 0 

currentItem.addOutput(videoOutput) 

videoTransform = CGAffineTransformInvert(videoTrack.preferredTransform) 

player.muted = true 

} 

有幾個有趣的點這裏:首先,我重置一個名爲failedPixelBufferForItemTimeCount的變量 - 這是一個解決方案,我認爲這是AVFoundation中的一個錯誤,視頻有時會無法加載而沒有明顯的錯誤。其次,爲了支持橫向和縱向視頻,我創建了視頻軌道首選變換的倒序版本。

廠商包含CADisplayLink它調用step(_:)

func step(link: CADisplayLink) { 
guard let player = player, 
    currentItem = player.currentItem else { 
    return 
} 

let itemTime = videoOutput.itemTimeForHostTime(CACurrentMediaTime()) 

displayVideoFrame(itemTime) 

let normalisedTime = Float(itemTime.seconds/currentItem.asset.duration.seconds) 

delegate?.vendorNormalisedTimeUpdated(normalisedTime) 

if normalisedTime >= 1.0 
{ 
    paused = true 
} 
} 

隨着CADisplayLink,我計算基於CACurrentMediaTimeAVPlayerItem的時間。標準化時間(即在0和1之間)是通過將玩家項目的時間除以資產持續時間來計算的,這由UI組件用來在回放期間設置擦洗杆的位置。在itemTime創建從影片的幀CIImagedisplayVideoFrame(_:)完成:

func displayVideoFrame(time: CMTime) { 
guard let player = player, 
    currentItem = player.currentItem where player.status == .ReadyToPlay && currentItem.status == .ReadyToPlay else { 
    return 
} 

if videoOutput.hasNewPixelBufferForItemTime(time) { 
    failedPixelBufferForItemTimeCount = 0 

    var presentationItemTime = kCMTimeZero 

    guard let pixelBuffer = videoOutput.copyPixelBufferForItemTime(
    time, 
    itemTimeForDisplay: &presentationItemTime) else { 
     return 
    } 

    unfilteredImage = CIImage(CVImageBuffer: pixelBuffer) 

    displayFilteredImage() 
} 
else if let currentURL = currentURL where !paused { 
    failedPixelBufferForItemTimeCount += 1 

    if failedPixelBufferForItemTimeCount > 12 { 
    openMovie(currentURL) 
    } 
} 
} 

之前從視頻輸出複製像素緩衝區,我需要確保有一個可用。如果這一切都很好,那麼從該像素緩衝區創建CIImage是一個簡單的步驟。但是,如果hasNewPixelBufferForItemTime(_:)失敗太多次(12似乎工作),我認爲AVFoundation默默失敗,我重新打開電影。

隨着人口CIImage,我申請一個過濾器(如果有的話),並返回所呈現的結果返回給委託(這是主視圖),以顯示:

func displayFilteredImage() { 
guard let unfilteredImage = unfilteredImage, 
    videoTransform = videoTransform else { 
    return 
} 

let ciImage: CIImage 

if let ciFilter = ciFilter { 
    ciFilter.setValue(unfilteredImage, forKey: kCIInputImageKey) 

    ciImage = ciFilter.outputImage!.imageByApplyingTransform(videoTransform) 
} 
else { 
    ciImage = unfilteredImage.imageByApplyingTransform(videoTransform) 
} 

let cgImage = ciContext.createCGImage(
    ciImage, 
    fromRect: ciImage.extent) 

delegate?.finalOutputUpdated(UIImage(CGImage: cgImage)) 
} 

如果你想將過濾後的電影寫回文件系統,我在this blog post中討論過。

Simon