рассчитать время для плавного перехода между видео - PullRequest
0 голосов
/ 10 января 2019

Я должен применить непрозрачность в видео. Я должен применить его до конца видео секунды. Я использую "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)

1 Ответ

0 голосов
/ 10 января 2019

Я бы использовал три инструкции для применения перекрестного затухания:

  1. Инструкция «pass-through», которая показывает только первую видеодорожку, за одну секунду до окончания первого актива.
  2. Инструкция кроссфейдера, которая одновременно показывает последнюю секунду первой видео дорожки и первую секунду второй видео дорожки с линейными изменениями непрозрачности.
  3. Инструкция «pass-through», которая показывает только вторую дорожку видео, начиная с одной секунды до второй дорожки видео.

Итак, во-первых, давайте получим треки:

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()

Результат:

demo

Обратите внимание, что мне пришлось сделать анимацию крошечной и невысокой по цвету из-за ограничения переполнения стека на размер файла изображения.

Входные видео любезно предоставлены Джеффри Бич .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...