计算视频之间淡入淡出的时间
compute times for cross-fade between videos
我必须在视频中应用不透明度。我必须在一秒钟的视频结束前应用它。我正在使用 "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)
接下来,合成中 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
:
// 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]
第三条指令是在适当的时间范围内仅通过 compositionTrack1
:
let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
现在我们已经掌握了所有三个指令,我们可以将它们交给视频组合:
videoComposition.instructions = [compositionTrack0PassThroughInstruction, crossFadeInstruction, compositionTrack1PassThroughInstruction]
现在我们可以一起使用 composition
和 videoComposition
,例如导出一个新的电影文件:
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()
结果:
请注意,由于 Stack Overflow 对图像文件大小的限制,我不得不将动画制作得很小且颜色很浅。
输入视频由 Jeffrey Beach 提供。
我必须在视频中应用不透明度。我必须在一秒钟的视频结束前应用它。我正在使用 "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)
接下来,合成中 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
:
// 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]
第三条指令是在适当的时间范围内仅通过 compositionTrack1
:
let compositionTrack1PassThroughInstruction = AVMutableVideoCompositionInstruction.passThrough(
trackId: compositionTrack1Id, timeRange: compositionTrack1PassThroughTimeRange)
现在我们已经掌握了所有三个指令,我们可以将它们交给视频组合:
videoComposition.instructions = [compositionTrack0PassThroughInstruction, crossFadeInstruction, compositionTrack1PassThroughInstruction]
现在我们可以一起使用 composition
和 videoComposition
,例如导出一个新的电影文件:
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()
结果:
请注意,由于 Stack Overflow 对图像文件大小的限制,我不得不将动画制作得很小且颜色很浅。
输入视频由 Jeffrey Beach 提供。