Ios 计算视频之间的交叉淡入淡出时间

Ios 计算视频之间的交叉淡入淡出时间,ios,swift,avfoundation,Ios,Swift,Avfoundation,我必须在视频中应用不透明度。我必须在一秒钟的视频结束前应用它。我使用“firstInstruction”来计算视频的总持续时间。然而,当我调用“firstInstruction.setOpacityRamp”方法时,我不能减去第二个方法 let mainInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTim

我必须在视频中应用不透明度。我必须在一秒钟的视频结束前应用它。我使用“firstInstruction”来计算视频的总持续时间。然而,当我调用“firstInstruction.setOpacityRamp”方法时,我不能减去第二个方法

    let mainInstruction = AVMutableVideoCompositionInstruction()
    mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeAdd(firstAsset.duration, secondAsset.duration))
    let firstInstruction = VideoHelper.videoCompositionInstruction(firstTrack, asset: firstAsset)
    firstInstruction.setOpacityRamp(fromStartOpacity: 1, toEndOpacity: 0.1, timeRange: mainInstruction.timeRange)

我将使用三个说明来应用交叉淡入度:

  • “通过”指令,仅显示第一个视频曲目,直到第一个资源结束前一秒
  • 一种交叉淡入淡出指令,它同时显示第一个视频轨迹的最后一秒和第二个视频轨迹的第一秒,并带有不透明度渐变
  • 一种“直通”指令,仅显示第二个视频轨道,从一秒钟开始进入第二个视频轨道
  • 那么,首先,让我们来了解一下轨迹:

    import AVFoundation
    import CoreVideo
    
    func crossFade(asset0: AVAsset, asset1: AVAsset, crossFadeDuration: CMTime, to outputUrl: URL) throws {
        guard
            let asset0Track = asset0.tracks(withMediaType: .video).first,
            let asset1Track = asset1.tracks(withMediaType: .video).first,
            case let composition = AVMutableComposition(),
            case let compositionTrack0Id = composition.unusedTrackID(),
            let compositionTrack0 = composition.addMutableTrack(
                withMediaType: .video, preferredTrackID: compositionTrack0Id),
            case let compositionTrack1Id = composition.unusedTrackID(),
            let compositionTrack1 = composition.addMutableTrack(
                withMediaType: .video, preferredTrackID: compositionTrack1Id)
            else { return }
    
    现在让我们计算所有需要的时间。首先,构图中的整个
    asset0Track
    范围包括通过和交叉淡入时间段:

        // When does asset0Track start, in the composition?
        let asset0TrackStartTime = CMTime.zero
    
        // When does asset0Track end, in the composition?
        let asset0TrackEndTime = asset0TrackStartTime + asset0Track.timeRange.duration
    
    接下来,交叉淡入淡出的时间范围:

        // When does the cross-fade end, in the composition?
        // It should end exactly at the end of asset0's video track.
        let crossFadeEndTime = asset0TrackEndTime
    
        // When does the cross-fade start, in the composition?
        let crossFadeStartTime = crossFadeEndTime - crossFadeDuration
    
        // What is the entire time range of the cross-fade, in the composition?
        let crossFadeTimeRange = CMTimeRangeMake(
            start: crossFadeStartTime,
            duration: crossFadeDuration)
    
        // I'm using a helper function defined below.
        let compositionTrack0PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack0Id, timeRange: compositionTrack0PassThroughTimeRange)
    
        let crossFadeLayer0Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer0Instruction.trackID = compositionTrack0Id
        crossFadeLayer0Instruction.setOpacityRamp(fromStartOpacity: 1, toEndOpacity: 0, timeRange: crossFadeTimeRange)
    
        let crossFadeLayer1Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer1Instruction.trackID = compositionTrack1Id
        crossFadeLayer1Instruction.setOpacityRamp(fromStartOpacity: 0, toEndOpacity: 1, timeRange: crossFadeTimeRange)
    
        let crossFadeInstruction = AVMutableVideoCompositionInstruction()
        crossFadeInstruction.timeRange = crossFadeTimeRange
        crossFadeInstruction.layerInstructions = [crossFadeLayer0Instruction, crossFadeLayer1Instruction]
    
        let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
    
    接下来,构图中的整个
    asset1Track
    范围包括交叉淡入和通过周期:

        // When does asset1Track start, in the composition?
        // It should start exactly at the start of the cross-fade.
        let asset1TrackStartTime = crossFadeStartTime
    
        // When does asset1Track end, in the composition?
        let asset1TrackEndTime = asset1TrackStartTime + asset1Track.timeRange.duration
    
    最后,两个穿越时间范围:

        // What is the time range during which only asset0 is visible, in the composition?
        let compositionTrack0PassThroughTimeRange = CMTimeRangeMake(
            start: asset0TrackStartTime,
            duration: crossFadeStartTime - asset0TrackStartTime)
    
        // What is the time range during which only asset1 is visible, in the composition?
        let compositionTrack1PassThroughTimeRange = CMTimeRangeMake(
            start: crossFadeEndTime,
            duration: asset1TrackEndTime - crossFadeEndTime)
    
    现在我们可以将输入曲目插入合成曲目中:

        // Put asset0Track into compositionTrack0.
        try compositionTrack0.insertTimeRange(
            asset0Track.timeRange,of: asset0Track, at: asset0TrackStartTime)
    
        // Put asset1Track into compositionTrack1.
        try compositionTrack1.insertTimeRange(
            asset1Track.timeRange, of: asset1Track, at: asset1TrackStartTime)
    
    这就是我们需要为
    AVMutableComposition
    所做的一切。但是我们还需要制作一个
    AVMutableVideoComposition

        let videoComposition = AVMutableVideoComposition()
        videoComposition.frameDuration =
            min(asset0Track.minFrameDuration, asset1Track.minFrameDuration)
        videoComposition.renderSize = CGSize(
            width: max(asset0Track.naturalSize.width, asset1Track.naturalSize.width),
            height: max(asset0Track.naturalSize.height, asset1Track.naturalSize.height))
    
    我们需要设置视频合成的说明。第一条指令是在适当的时间范围内仅通过
    compositionTrack0

        // When does the cross-fade end, in the composition?
        // It should end exactly at the end of asset0's video track.
        let crossFadeEndTime = asset0TrackEndTime
    
        // When does the cross-fade start, in the composition?
        let crossFadeStartTime = crossFadeEndTime - crossFadeDuration
    
        // What is the entire time range of the cross-fade, in the composition?
        let crossFadeTimeRange = CMTimeRangeMake(
            start: crossFadeStartTime,
            duration: crossFadeDuration)
    
        // I'm using a helper function defined below.
        let compositionTrack0PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack0Id, timeRange: compositionTrack0PassThroughTimeRange)
    
        let crossFadeLayer0Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer0Instruction.trackID = compositionTrack0Id
        crossFadeLayer0Instruction.setOpacityRamp(fromStartOpacity: 1, toEndOpacity: 0, timeRange: crossFadeTimeRange)
    
        let crossFadeLayer1Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer1Instruction.trackID = compositionTrack1Id
        crossFadeLayer1Instruction.setOpacityRamp(fromStartOpacity: 0, toEndOpacity: 1, timeRange: crossFadeTimeRange)
    
        let crossFadeInstruction = AVMutableVideoCompositionInstruction()
        crossFadeInstruction.timeRange = crossFadeTimeRange
        crossFadeInstruction.layerInstructions = [crossFadeLayer0Instruction, crossFadeLayer1Instruction]
    
        let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
    
    第二条指令用于交叉淡入,因此更复杂。它需要两个子指令,交叉渐变中每层一个。每个层指令和整个交叉淡入淡出指令使用相同的时间范围:

        // When does the cross-fade end, in the composition?
        // It should end exactly at the end of asset0's video track.
        let crossFadeEndTime = asset0TrackEndTime
    
        // When does the cross-fade start, in the composition?
        let crossFadeStartTime = crossFadeEndTime - crossFadeDuration
    
        // What is the entire time range of the cross-fade, in the composition?
        let crossFadeTimeRange = CMTimeRangeMake(
            start: crossFadeStartTime,
            duration: crossFadeDuration)
    
        // I'm using a helper function defined below.
        let compositionTrack0PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack0Id, timeRange: compositionTrack0PassThroughTimeRange)
    
        let crossFadeLayer0Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer0Instruction.trackID = compositionTrack0Id
        crossFadeLayer0Instruction.setOpacityRamp(fromStartOpacity: 1, toEndOpacity: 0, timeRange: crossFadeTimeRange)
    
        let crossFadeLayer1Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer1Instruction.trackID = compositionTrack1Id
        crossFadeLayer1Instruction.setOpacityRamp(fromStartOpacity: 0, toEndOpacity: 1, timeRange: crossFadeTimeRange)
    
        let crossFadeInstruction = AVMutableVideoCompositionInstruction()
        crossFadeInstruction.timeRange = crossFadeTimeRange
        crossFadeInstruction.layerInstructions = [crossFadeLayer0Instruction, crossFadeLayer1Instruction]
    
        let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
    
    第三条指令是在适当的时间范围内仅通过
    compositionTrack1

        // When does the cross-fade end, in the composition?
        // It should end exactly at the end of asset0's video track.
        let crossFadeEndTime = asset0TrackEndTime
    
        // When does the cross-fade start, in the composition?
        let crossFadeStartTime = crossFadeEndTime - crossFadeDuration
    
        // What is the entire time range of the cross-fade, in the composition?
        let crossFadeTimeRange = CMTimeRangeMake(
            start: crossFadeStartTime,
            duration: crossFadeDuration)
    
        // I'm using a helper function defined below.
        let compositionTrack0PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack0Id, timeRange: compositionTrack0PassThroughTimeRange)
    
        let crossFadeLayer0Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer0Instruction.trackID = compositionTrack0Id
        crossFadeLayer0Instruction.setOpacityRamp(fromStartOpacity: 1, toEndOpacity: 0, timeRange: crossFadeTimeRange)
    
        let crossFadeLayer1Instruction = AVMutableVideoCompositionLayerInstruction()
        crossFadeLayer1Instruction.trackID = compositionTrack1Id
        crossFadeLayer1Instruction.setOpacityRamp(fromStartOpacity: 0, toEndOpacity: 1, timeRange: crossFadeTimeRange)
    
        let crossFadeInstruction = AVMutableVideoCompositionInstruction()
        crossFadeInstruction.timeRange = crossFadeTimeRange
        crossFadeInstruction.layerInstructions = [crossFadeLayer0Instruction, crossFadeLayer1Instruction]
    
        let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
            trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
    
    现在我们有了所有三个指令,我们可以给他们视频合成:

        videoComposition.instructions = [compositionTrack0PassThroughInstruction, crossFadeInstruction, compositionTrack1PassThroughInstruction]
    
    现在我们可以一起使用
    合成
    视频合成
    ,例如导出新的电影文件:

        let export = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality)!
        export.outputURL = outputUrl
        export.videoComposition = videoComposition
        export.exportAsynchronously {
            exit(0)
        }
    }
    
    下面是我用来创建直通指令的助手:

    extension AVMutableVideoCompositionInstruction {
        static func passThrough(trackId: CMPersistentTrackID, timeRange: CMTimeRange) -> AVMutableVideoCompositionInstruction {
            let layerInstruction = AVMutableVideoCompositionLayerInstruction()
            layerInstruction.trackID = trackId
    
            let instruction = AVMutableVideoCompositionInstruction()
            instruction.timeRange = timeRange
            instruction.layerInstructions = [layerInstruction]
    
            return instruction
        }
    }
    
    这是我的测试代码。我使用macOS命令行应用程序进行测试:

    let asset0 = AVURLAsset(url: URL(fileURLWithPath: "/tmp/asset0.mp4"))
    let asset1 = AVURLAsset(url: URL(fileURLWithPath: "/tmp/asset1.mp4"))
    
    let outputUrl = URL(fileURLWithPath: "/tmp/output.mp4")
    try! crossFade(asset0: asset0, asset1: asset1, crossFadeDuration: CMTimeMake(value: 1, timescale: 1), to: outputUrl)
    
    dispatchMain()
    
    结果:

    注意,由于堆栈溢出对图像文件大小的限制,我不得不使动画变小,颜色变浅


    由提供的输入视频。

    您不需要添加我的所有代码。您可能不想添加
    exit(0)
    语句。但是你应该试着理解我所有的代码。