1
votes

Duo - Fusionnez 2 vidéos côte à côte

REMARQUE: - Fusionnez les vidéos côte à côte sans perdre la qualité vidéo

Je pense que c'est une question très très importante , après de nombreuses recherches et recherches sur Google, je n'ai trouvé aucun matériel utile lié à cette question.

Je travaille sur un projet dans lequel je dois FUSIONNER des vidéos côte à côte dans un seul fichier.

I avait fait Vidéos fusionnées à l'aide d'AVFoundation , mais le problème est que FIRST Video s'affiche en superposition à une SECOND vidéo (ne fusionne pas correctement comme SMULE App / Karaoke App ou Tiktok App ).

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")
            }
        }

    }

Et voici ma sortie entrez la description de l'image ici

Et ce que je veux

 entrez la description de l'image ici

Comme je ne sais pas quelle est la meilleure approche pour réaliser une VIDEO SIDE BY SIDE / DUET VIDEO ... Pour l'instant, j'ai utilisé AVFoundation. Je n'ai utilisé aucun framework tiers ni aucun POD.

Je voudrais demander, quelle est la MEILLEURE approche pour mettre en œuvre cela? Les vidéos doivent fusionner du côté du serveur ou d'une application? Quelle approche dois-je utiliser également?

Toute aide serait très appréciée. Merci


1 commentaires

Ceci est un tout nouveau projet de plateforme de chant qui commence - LiveJam. github.com/richardaum/livejam Jetez un œil et pensez à nous soutenir - nous recherchons des contributeurs experts et vous pourriez être l'un de nous.


3 Réponses :


1
votes

Pour y parvenir, je créerais un nouvel objet AVMutableComposition contenant 2 pistes, et définirais une transformation sur chacune pour les placer côte à côte:

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

Ensuite. enregistrez-le en utilisant:

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)

(code Swift non testé).


7 commentaires

Salut monsieur, pouvez-vous m'expliquer un peu plus? S'il vous plaît


Pouvez-vous s'il vous plaît revoir mon code?


Vous devriez maintenant travailler sur un échantillon minimal: ajoutez simplement vos 2 pistes vidéo à votre composition (pas de calque 'instructions', pas d'audio). Ensuite, assurez-vous que les pistes sont ajoutées à votre composition, exportez-la dans un fichier et ouvrez le fichier exporté dans Quicktime Player 7. Ensuite, ouvrez la fenêtre Movie Properties (raccourci ⌘ + J) et vérifiez le propriétés de vos 2 pistes vidéo. Ajustez si nécessaire.


Pour la 1ère vidéo mutableCompositionNewVideoTrack.preferredTransform = CGAffineTransform.init (translationX: 0.0, y: 0.0)


Pour la 2ème vidéo mutableCompositionSavedVideoTrack.preferredTransform = CGAffineTransform.init (translationX: self.camPreview.frame.width / 2, y: 0.0)


En utilisant Transform ci-dessus, vous ne recevez toujours qu'une seule vidéo .. Y a-t-il une erreur appropriée?


Dans le code ci-dessus, la mise à l'échelle est codée en dur, cela ne fonctionnera correctement que dans un scénario spécifique. Vous devez agrandir ou réduire les deux vidéos en général.



0
votes

en fait, AVAssetExportSession est pour des besoins simples, et c'est trop simple pour votre situation.

Vous devez utiliser AVAssetWriter .

Vous ajoutez AVAssetWriterInput à votre AVAssetWriter . < / p>

Vous pouvez configurer le trasnform de AVAssetWriterInput en utilisant sa propriété transform .

Ensuite, vous alimentez votre AVAssetWriterInput avec CMSampleBuffer (chaque tampon d'images) en utilisant des appels append .

Consultez la documentation Apple complète pour un exemple détaillé: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid / TP40010188-CH9-SW2


0 commentaires

0
votes

Cela dépend de la largeur de la taille de l'image vidéo (taille du rendu), si la largeur de rendu de l'image est de 800. vous devez traduire la seconde vidéo fusionnée en 400, (moitié moitié). Et vous devez de la même manière réduire ou augmenter. comme indiqué dans l'exemple d'extrait de code ci-dessous.

        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)


0 commentaires