【问题标题】:Duet - Merge 2 Videos Side by SideDuet - 并排合并 2 个视频
【发布时间】:2019-04-20 07:24:53
【问题描述】:

注意:-并排合并视频而不会降低视频质量

我认为这是一个非常非常重要的问题,经过大量搜索和谷歌搜索,没有找到任何与此问题相关的有用材料。

我正在做一个项目,我需要将视频并排合并到一个文件中。

我已经完成使用 AVFoundation 合并视频,但问题是 FIRST 视频显示为第二个视频的叠加层(与 SMULE 应用程序/卡拉 OK 应用程序或 Tiktok 应用程序相同的合并不正确)。

func mergeVideosFilesWithUrl(savedVideoUrl: URL, newVideoUrl: URL, audioUrl:URL)
    {
        let savePathUrl : NSURL = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/camRecordedVideo.mp4")
        do { // delete old video
            try FileManager.default.removeItem(at: savePathUrl as URL)
        } catch { print(error.localizedDescription) }

        var mutableVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition()
        var mixComposition : AVMutableComposition = AVMutableComposition()

        let aNewVideoAsset : AVAsset = AVAsset(url: newVideoUrl)
        let asavedVideoAsset : AVAsset = AVAsset(url: savedVideoUrl)

        let aNewVideoTrack : AVAssetTrack = aNewVideoAsset.tracks(withMediaType: AVMediaType.video)[0]
        let aSavedVideoTrack : AVAssetTrack = asavedVideoAsset.tracks(withMediaType: AVMediaType.video)[0]

        let mutableCompositionNewVideoTrack : AVMutableCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
        do{
            try mutableCompositionNewVideoTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aNewVideoAsset.duration), of: aNewVideoTrack, at: CMTime.zero)
        }catch {  print("Mutable Error") }

        let mutableCompositionSavedVideoTrack : AVMutableCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
        do{
            try mutableCompositionSavedVideoTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: asavedVideoAsset.duration), of: aSavedVideoTrack , at: CMTime.zero)
        }catch{ print("Mutable Error") }

        let mainInstruction = AVMutableVideoCompositionInstruction()
        mainInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: CMTimeMaximum(aNewVideoAsset.duration, asavedVideoAsset.duration) )

        let newVideoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mutableCompositionNewVideoTrack)
        let newScale : CGAffineTransform = CGAffineTransform.init(scaleX: 0.7, y: 0.7)
        let newMove : CGAffineTransform = CGAffineTransform.init(translationX: 230, y: 230)
        newVideoLayerInstruction.setTransform(newScale.concatenating(newMove), at: CMTime.zero)

        let savedVideoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mutableCompositionSavedVideoTrack)
        let savedScale : CGAffineTransform = CGAffineTransform.init(scaleX: 1.2, y: 1.5)
        let savedMove : CGAffineTransform = CGAffineTransform.init(translationX: 0, y: 0)
        savedVideoLayerInstruction.setTransform(savedScale.concatenating(savedMove), at: CMTime.zero)

        mainInstruction.layerInstructions = [newVideoLayerInstruction, savedVideoLayerInstruction]


        mutableVideoComposition.instructions = [mainInstruction]
        mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
        mutableVideoComposition.renderSize = CGSize(width: 1240 , height: self.camPreview.frame.height)

        finalPath = savePathUrl.absoluteString
        let assetExport: AVAssetExportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)!
        assetExport.videoComposition = mutableVideoComposition
        assetExport.outputFileType = AVFileType.mov

        assetExport.outputURL = savePathUrl as URL
        assetExport.shouldOptimizeForNetworkUse = true

        assetExport.exportAsynchronously { () -> Void in
            switch assetExport.status {

            case AVAssetExportSession.Status.completed:
                print("success")
            case  AVAssetExportSession.Status.failed:
                print("failed \(assetExport.error)")
            case AVAssetExportSession.Status.cancelled:
                print("cancelled \(assetExport.error)")
            default:
                print("complete")
            }
        }

    }

这是我的输出

我想要什么

因为我不知道制作 SIDE BY SIDE VIDEO/DUET VIDEO 的最佳方法是什么……至于现在,我使用了 AVFoundation。我没有使用任何 3rd 方框架或任何 POD。

我想问一下,实现这一点的最佳方法是什么?视频应该合并在服务器端还是应用程序?还有我应该使用哪种方法?

任何帮助都将受到高度赞赏。谢谢

【问题讨论】:

  • 这是一个全新的歌唱平台项目——LiveJam。 github.com/richardaum/livejam 看看并考虑支持我们 - 我们正在寻找专家贡献者,您可能是我们中的一员。

标签: ios swift avfoundation swift4.2


【解决方案1】:

为此,我将创建一个包含 2 个轨道的新 AVMutableComposition 对象,并在每个轨道上设置变换以将它们并排放置:

let composition = AVMutableComposition(urlAssetInitializationOptions: <your options>)
let videoTrackA = composition.addMutableTrack(withMediaType:.video, preferredTrackID:kCMPersistentTrackID_Invalid);
let videoTrackB = composition.addMutableTrack(withMediaType:.video, preferredTrackID:kCMPersistentTrackID_Invalid);

videoTrackA.preferredTransform = CGAffineTransform(translationX: <yourX_for_A>, y:0.0)
videoTrackB.preferredTransform = CGAffineTransform(translationX: <yourX_for_B>, y:0.0)

那么。保存使用:

let exporter = AVAssetExportSession(asset:<yourAsset>, presetName:<yourPresetName>)
exporter.exportAsynchronously(completionHandler: <yourCompletionHandler>)

(Swift 代码未测试)。

【讨论】:

  • 您好,先生,您能再解释一下吗?请
  • 你能检查我的代码吗?
  • 您现在应该处理一个最小的样本:只需将您的 2 个视频轨道添加到您的作品中(没有“指令”层,没有音频)。然后,确保将轨道添加到您的作品中,将其导出到文件并在 Quicktime Player 7 中打开导出的文件。然后,打开 Movie Properties 窗口(⌘+J 快捷键)并验证 2 个视频轨道的属性.根据需要进行调整。
  • 对于第一个视频 mutableCompositionNewVideoTrack.preferredTransform = CGAffineTransform.init(translationX: 0.0, y:0.0)
  • 对于第二个视频 mutableCompositionSavedVideoTrack.preferredTransform = CGAffineTransform.init(translationX: self.camPreview.frame.width/2, y:0.0)
【解决方案2】:

其实AVAssetExportSession是为了简单的需求,对你的情况来说太简单了。

您必须使用AVAssetWriter

您将AVAssetWriterInput 添加到您的AVAssetWriter

您可以使用AVAssetWriterInputtransform 属性配置其转换。

然后,您使用 append 调用为您的 AVAssetWriterInput 提供 CMSampleBuffer(每个图像缓冲区)。

有关详细示例,请参阅完整的 Apple 文档:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/TP40010188-CH9-SW2

【讨论】:

    【解决方案3】:

    这取决于您的视频帧大小(渲染大小)的宽度,如果您的帧渲染宽度是 800。您必须将第二个合并视频转换为 400(一半)。同样,您必须缩小或放大。如下示例代码sn-p所示。

            var renderSize = CGSize(width: 800, height: 534) // 534
            let assetInfo = AVMutableComposition.orientationFromTransform(firstTrack.preferredTransform)
            renderSize = assetInfo.isPortrait ? CGSize(width: renderSize.height, height: renderSize.width) : CGSize(width: renderSize.width, height: renderSize.height)
            
            let scaleRatioX = (renderSize.width / 2) / firstTrack.naturalSize.width
            let scaleRatioY = (renderSize.height) / firstTrack.naturalSize.height
            
            let scaleRatio2X = (renderSize.width / 2) / secondTrack.naturalSize.width
            let scaleRatio2Y = renderSize.height / secondTrack.naturalSize.height
            
            print("Scale Video 1 : \(scaleRatioX), \(scaleRatioY)") // print("Scale Video 1 : \(scaleRatioY), \(scaleRatioX)")
            print("Scale Video 2 : \(scaleRatio2X), \(scaleRatio2Y)")
            
    //        let scale  = CGAffineTransform(scaleX: scaleRatioY, y: scaleRatioX) // 1, 1
            let scale  = CGAffineTransform(scaleX: scaleRatioX, y: scaleRatioY)
            let move = CGAffineTransform(translationX: 0, y: 0)
    
            firstlayerInstruction.setTransform(scale.concatenating(move), at: .zero)
            let secondlayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: secondTrack)
    
            let secondScale = CGAffineTransform(scaleX: scaleRatio2X, y: scaleRatio2Y) // 0.13, 0.13
            let secondMove = CGAffineTransform(translationX: 400, y: 0) // 160, 0
            secondlayerInstruction.setTransform(secondScale.concatenating(secondMove), at: .zero)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-29
      • 1970-01-01
      • 2016-08-14
      • 2019-12-31
      • 2020-11-21
      • 2018-10-14
      • 1970-01-01
      • 2020-07-28
      相关资源
      最近更新 更多