Наложение текста на видео происходит очень медленно, любые более быстрые альтернативы - PullRequest
1 голос
/ 01 августа 2020
    let asset = AVURLAsset(url: videoURL)
    let composition = AVMutableComposition()
    
    guard
        let compositionTrack = composition.addMutableTrack(
            withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid),
        let assetTrack = asset.tracks(withMediaType: .video).first
        else {
            print("Something is wrong with the asset.")
            onComplete(nil)
            return
    }
    
    do {
        let timeRange = CMTimeRange(start: .zero, duration: asset.duration)
        try compositionTrack.insertTimeRange(timeRange, of: assetTrack, at: .zero)
        
        if let audioAssetTrack = asset.tracks(withMediaType: .audio).first,
            let compositionAudioTrack = composition.addMutableTrack(
                withMediaType: .audio,
                preferredTrackID: kCMPersistentTrackID_Invalid) {
            try compositionAudioTrack.insertTimeRange(
                timeRange,
                of: audioAssetTrack,
                at: .zero)
        }
    } catch {
        //print(error)
        onComplete(nil)
        return
    }
    
    compositionTrack.preferredTransform = assetTrack.preferredTransform
    let videoInfo = orientation(from: assetTrack.preferredTransform)
    
    let videoSize: CGSize
    if videoInfo.isPortrait {
        videoSize = CGSize(
            width: assetTrack.naturalSize.height,
            height: assetTrack.naturalSize.width)
    } else {
        videoSize = assetTrack.naturalSize
    }
    
    let videoLayer = CALayer()
    videoLayer.frame = CGRect(origin: .zero, size: videoSize)

    let textLayer = CATextLayer()
    textLayer.string = mainTextview.text
    textLayer.shouldRasterize = true
    textLayer.rasterizationScale = UIScreen.main.scale
    //textLayer.backgroundColor = UIColor.black.cgColor
    
    switch textAlignment {
    case .center:
        textLayer.alignmentMode = .center
    case .justified:
        textLayer.alignmentMode = .justified
    case .left:
        textLayer.alignmentMode = .left
    case .right:
        textLayer.alignmentMode = .right
    default:
        textLayer.alignmentMode = .center
    }
    
    textLayer.isWrapped = true
    textLayer.foregroundColor = mainTextview.textColor?.cgColor
    
    textLayer.fontSize = 70
    
    switch textWeight {
    case .bold:
        textLayer.font = UIFont.systemFont(ofSize: 0, weight: .bold)
    case .light:
        textLayer.font = UIFont.systemFont(ofSize: 0, weight: .light)
    case .regular:
        textLayer.font = UIFont.systemFont(ofSize: 0, weight: .regular)
    case .medium:
        textLayer.font = UIFont.systemFont(ofSize: 0, weight: .medium)
    }
    
    switch textviewPosition {
    case .Center:
        textLayer.frame = CGRect(x: 0, y: videoSize.height * 0.30, width: videoSize.width, height: 500)
    case .Top:
        textLayer.frame = CGRect(x: 0, y: videoSize.height * 0.60, width: videoSize.width, height: 500)
    case .Bottom:
        textLayer.frame = CGRect(x: 10, y: 0, width: videoSize.width, height: 500)
    }
    textLayer.displayIfNeeded()
    
    let outputLayer = CALayer()
    outputLayer.frame = CGRect(origin: .zero, size: videoSize)
    outputLayer.addSublayer(videoLayer)
    outputLayer.addSublayer(textLayer)
    
    let videoComposition = AVMutableVideoComposition()
    videoComposition.renderSize = videoSize
    videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
    videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(
        postProcessingAsVideoLayer: videoLayer,
        in: outputLayer)
    
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRange(
        start: .zero,
        duration: composition.duration)
    videoComposition.instructions = [instruction]
    let layerInstruction = compositionLayerInstruction(
        for: compositionTrack,
        assetTrack: assetTrack)
    instruction.layerInstructions = [layerInstruction]
    
    guard let export = AVAssetExportSession(
        asset: composition,
        presetName: AVAssetExportPresetHighestQuality)
        else {
            //print("Cannot create export session.")
            onComplete(nil)
            return
    }
    
    let videoName = UUID().uuidString
    let exportURL = URL(fileURLWithPath: NSTemporaryDirectory())
        .appendingPathComponent(videoName)
        .appendingPathExtension(".mp4")
    
    export.videoComposition = videoComposition
    export.outputFileType = .mp4
    export.outputURL = exportURL
    
    export.exportAsynchronously {
        DispatchQueue.main.async {
            switch export.status {
            case .completed:
                onComplete(exportURL)
            default:
                onComplete(nil)
                break
            }
        }
    }
}

Это функция, которую я использую для наложения текста на видео (videoURL), я передаю URL-адрес видео, сохраненного в каталоге документов, и использую текстовое представление, уже созданное для пользователя, чтобы получить текст, вес, шрифт и выравнивание для оверлея спасибо. В большинстве случаев этот процесс занимает 15 секунд, что для пользователей - долгое время.

Ответы [ 2 ]

1 голос
/ 03 августа 2020

Экспорт видео трудоемок и занимает много времени, и вы ничего не можете с этим поделать.

Однако процесс является асинхронным, поэтому нет необходимости заставлять пользователя «ждать». Кроме того, экспортер продает объект Progress, так что вы можете легко показать прогресс как психологический трюк (удивительно, как время летит, когда вы смотрите индикатор прогресса).

0 голосов
/ 03 августа 2020

Спасибо за ваш ответ, где я инициировал сеанс AVAssetExportSession. Я добавил таймер:

 self.exportTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(displayData), userInfo: nil, repeats: true) 

Теперь я могу правильно обновить отображение с помощью этого метода:

@objc private func displayData() {
   self.progressBar.progress = exportSession.progress;
    if self.progressBar.progress > .99) {
        self.exportTimer.invalidate()
    }
}

Я также добавил Invalidate () в оператор switch export.status на случай ошибки.

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