VideoFlint / Cabbage

A video composition framework build on top of AVFoundation. It's simple to use and easy to extend.
MIT License
1.52k stars 221 forks source link

TrackItem with ImageResource does not work in Timeline overlays #65

Open ekurutepe opened 3 years ago

ekurutepe commented 3 years ago

Hello there,

I'm new to Cabbage and I'm struggling to figure out how to overlay image content on top of another video. Overlaying video on top another video works fine.

Here's what I'm trying to do:

let resource = ImageResource(image: CIImage(image: embed.image)!, duration: CMTime(seconds: 2.0, preferredTimeScale: 600))
let t = TrackItem(resource: resource)

t.startTime = embed.insertTime
t.duration = overlayDuration

t.videoConfiguration.frame = embed.frame
timeline.overlays = [t]

let compositionGenerator = CompositionGenerator(timeline: timeline)
guard let exporter = compositionGenerator.buildExportSession(presetName: AVAssetExportPreset1280x720) else {
       completion(nil)
       return
}

let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + UUID().uuidString + ".mp4")

exporter.outputURL = outputFileURL
exporter.outputFileType = .mp4
exporter.shouldOptimizeForNetworkUse = true

exporter.exportAsynchronously {
    switch exporter.status {
    case .completed:
        DispatchQueue.main.async {
            completion(exporter)
        }
    case .failed:
        DispatchQueue.main.async {
            completion(exporter)
        }
    case .cancelled:
        DispatchQueue.main.async {
            completion(exporter)
        }
    default:
        break
    }
}     

The video gets exported but does not contain the image overlay. I'd appreciate any help. Thanks!

vitoziv commented 3 years ago

For the question

If t.startTime = embed.insertTime is not zero, you should add a empty resource, and it's timeRange is 0 to t.startTime. Make sure the track is full.

like this |------------------| not this |[empty]----------|

let resource = Resource()
resource.duration = t.startTime
resource.selectedTimeRange = CMTimeRange(start: CMTime.zero, end:  t.startTime)
let emptyTrackItem = TrackItem(resource: )
timeline.overlays = [emptyTrackItem , t]

Better way to add overlay image

timeline.overlays is for video overlay, it will add track to AVAsset

Another way, see the demo code https://github.com/VideoFlint/Cabbage/blob/8fa17a18afc40dd00da267865edbd12923694553/Cabbage/ViewController.swift#L79-L129

simplest and better way to add overlay image

let url = Bundle.main.url(forResource: "overlay", withExtension: "jpg")!
let image = CIImage(contentsOf: url)!
let resource = ImageResource(image: image, duration: CMTime.init(seconds: 3, preferredTimescale: 600))
let imageCompositionProvider = ImageOverlayItem(resource: resource)
timeline.passingThroughVideoCompositionProvider = imageCompositionProvider
ekurutepe commented 3 years ago

Thanks for the explanation.

The users of my app can embed multiple video and image overlays on a given video background. These embeds might be temporally and spatially overlapping with each other. My simple approach adding the TrackItems to the overlays array works great if the embeds are videos. That's why I was confused when it didn't work with images.

For now I'll try the empty track item approach but I'd be happy to send a PR to make this possible with TrackItems with ImageResources as well, if you think that's possible.