Search code examples
iosavfoundationavvideocomposition

How to display a cutout/portion of a video in AVFoundation


I'm trying to crop a video / display a portion of a video in AVFoundation. So far my code looks right to me but the output is not what I wanted. I don't see what is wrong. Any ideas?

let item = AVPlayerItem(url: nextVideoItem.url)
let cropRect = CGRect(x: 200, y: 200, width: 300, height: 300)
let cropComposition = AVMutableVideoComposition(asset: item.asset, applyingCIFiltersWithHandler: { request in    
    let cropFilter = CIFilter(name: "CICrop")!
    cropFilter.setValue(request.sourceImage, forKey: kCIInputImageKey)
    cropFilter.setValue(CIVector(cgRect: cropRect), forKey: "inputRectangle")
    
    let imageAtOrigin = cropFilter.outputImage!.transformed(by: CGAffineTransform(translationX: -cropRect.origin.x, y: -cropRect.origin.y))
    request.finish(with: imageAtOrigin, context: nil)
})
cropComposition.renderSize = cropRect.size
item.videoComposition = cropComposition
self.player.replaceCurrentItem(with: item)
self.player.play()

Solution

  • The above code runs fine in itself, the reason you can't see it is you're probably not setting the frame of the AVPlayerViewController's view or AVPlayerLayer correctly in your surrounding UI code. Here is a Swift Playground code snippet that plays a video asset with the crop you specified above:

    import UIKit
    import AVKit
    import PlaygroundSupport
    
    class MyViewController : UIViewController {
        var player: AVPlayer!
        var avLayer: AVPlayerLayer!
        
        override func loadView() {
            let view = UIView()
            view.backgroundColor = .white
    
            player = AVPlayer()
            
            let item = AVPlayerItem(url: #fileLiteral(resourceName: "logos.mov"))
            
            let cropRect = CGRect(x: 500, y: 250, width: 480, height: 320)
            let cropComposition = AVMutableVideoComposition(asset: item.asset, applyingCIFiltersWithHandler: { request in
                let cropFilter = CIFilter(name: "CICrop")!
                cropFilter.setValue(request.sourceImage, forKey: kCIInputImageKey)
                cropFilter.setValue(CIVector(cgRect: cropRect), forKey: "inputRectangle")
                
                let imageAtOrigin = cropFilter.outputImage!.transformed(by: CGAffineTransform(translationX: -cropRect.origin.x, y: -cropRect.origin.y))
                request.finish(with: imageAtOrigin, context: nil)
            })
            cropComposition.renderSize = cropRect.size
            item.videoComposition = cropComposition
            self.player.replaceCurrentItem(with: item)
            self.player.play()
            
            avLayer = AVPlayerLayer()
            avLayer.player = self.player
            view.layer.addSublayer(avLayer)
            self.view = view
        }
        
        override func viewDidAppear(_ animated: Bool) {
            // this is the important bit
            avLayer.frame = view.bounds
        }
    }
    // Present the view controller in the Live View window
    PlaygroundPage.current.liveView = MyViewController()