AVMutableComposition 调整大小问题

Posted

技术标签:

【中文标题】AVMutableComposition 调整大小问题【英文标题】:AVMutableComposition resizing issue 【发布时间】:2018-06-05 12:27:18 【问题描述】:

我正在尝试使用 AVMutableComposition 将图像渲染为前置摄像头拍摄的视频。生成的视频(包括图像)的大小非常好。

但是,初始视频将被调整大小,如下图所示:

我正在使用NextLevelSessionExporter,这是我的代码 sn-p:

// * MARK - Creating composition
        /// Create AVMutableComposition object. This object will hold the AVMutableCompositionTrack instances.
        let mainMutableComposition = AVMutableComposition()

        /// Creating an empty video track
        let videoTrack = mainMutableComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
        let videoAssetTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]

        do 
            //Adding the video track
            try videoTrack?.insertTimeRange(CMTimeRange(start: kCMTimeZero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: AVMediaType.video).first!, at: kCMTimeZero)

         catch 
            completion(false,  nil)
        

        /// Adding audio if user wants to.
        if withAudio 
            do 
                //Adding the video track
                let audio = videoAsset.tracks(withMediaType: AVMediaType.audio).first
                if audio != nil 
                    let audioTrack = mainMutableComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
                    try audioTrack?.insertTimeRange(CMTimeRange(start: kCMTimeZero, duration: videoAsset.duration), of: audio!, at: kCMTimeZero)

                

             catch 
                completion(false,  nil)
            

        

        // * MARK - Composition is ready ----------

        // Create AVMutableVideoCompositionInstruction
        let compositionInstructions = AVMutableVideoCompositionInstruction()
        compositionInstructions.timeRange = CMTimeRange(start: kCMTimeZero, duration: videoAsset.duration)

        // Create an AvmutableVideoCompositionLayerInstruction
        let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction.init(assetTrack: videoTrack!)
        videoLayerInstruction.setTransform(videoAssetTrack.preferredTransform, at: kCMTimeZero)
        compositionInstructions.layerInstructions = [videoLayerInstruction]

        //Add instructions
        let videoComposition = AVMutableVideoComposition()

        let naturalSize : CGSize = videoAssetTrack.naturalSize

        ///Rendering image into video
        let renderWidth = naturalSize.width
        let renderHeight = naturalSize.height

        //Assigning instructions and rendering size
        videoComposition.renderSize = CGSize(width: renderWidth, height: renderHeight)
        videoComposition.instructions = [compositionInstructions]
        videoComposition.frameDuration = CMTime(value: 1, timescale: Int32((videoTrack?.nominalFrameRate)!))

        //Applying image to instruction
        self.applyVideoImage(to: videoComposition, withSize: naturalSize, image: image)

        // Getting the output path
        let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        let outputPath = documentsURL?.appendingPathComponent("lastEditedVideo.mp4")
        if FileManager.default.fileExists(atPath: (outputPath?.path)!) 
            do 
                try FileManager.default.removeItem(atPath: (outputPath?.path)!)
            
            catch 
                completion(false, nil)
            
        



        // Create exporter
        let exporter = NextLevelSessionExporter(withAsset: mainMutableComposition)
        exporter.outputURL = outputPath
        exporter.outputFileType = AVFileType.mp4
        exporter.videoComposition = videoComposition

        let compressionDict: [String: Any] = [
            AVVideoAverageBitRateKey: NSNumber(integerLiteral: 2300000),
            AVVideoProfileLevelKey: AVVideoProfileLevelH264BaselineAutoLevel as String
            ]

        exporter.videoOutputConfiguration = [
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoWidthKey: NSNumber(integerLiteral: Int(naturalSize.width)),
            AVVideoHeightKey: NSNumber(integerLiteral: Int(naturalSize.height)),
            AVVideoCompressionPropertiesKey: compressionDict
        ]

        exporter.audioOutputConfiguration = [
            AVFormatIDKey: kAudioFormatMPEG4AAC,
            AVEncoderBitRateKey: NSNumber(integerLiteral: 128000),
            AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
            AVSampleRateKey: NSNumber(value: Float(44100))
        ]

        completion(true, exporter)
    

这是我的 applyVideoImage() 函数。

private func applyVideoImage(to composition: AVMutableVideoComposition, withSize size: CGSize, image: UIImage)  //Adds an image to a video composition

    //Creating image layer
    let overlayLayer = CALayer()
    let overlayImage: UIImage = image
    overlayLayer.contents = overlayImage.cgImage
    overlayLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    overlayLayer.contentsGravity = kCAGravityResizeAspectFill
    overlayLayer.masksToBounds = true

    //Creating parent and video layer
    let parentLayer = CALayer()
    let videoLayer = CALayer()
    parentLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    videoLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    parentLayer.addSublayer(videoLayer)
    parentLayer.addSublayer(overlayLayer)

    //Adding those layers to video
    composition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)

编辑 1: 只有在导出使用前置摄像头拍摄的镜像视频时才会出现此错误。

【问题讨论】:

【参考方案1】:

这真的很棘手:您需要检查视频轨道的preferredTransform 以确定它是否是纵向视频。

    var videoAssetOrientation = UIImageOrientation.up
    var isVideoAssetPortrait = false
    var videoTransform = videoAssetTrack.preferredTransform
    if needsMirroring == true  
        isVideoAssetPortrait = true
    else if videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0 
        videoAssetOrientation = .right
        isVideoAssetPortrait = true
    else if videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0 
        videoAssetOrientation = .left
        isVideoAssetPortrait = true
    else if videoTransform.a == 1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == 1.0 
        videoAssetOrientation = .up
    else if videoTransform.a == -1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == -1.0 
        videoAssetOrientation = .down
    

    //Add instructions
    mainInstruction.layerInstructions = [videoLayerInstruction]
    let mainCompositionInst = AVMutableVideoComposition()
    let naturalSize : CGSize!
    if isVideoAssetPortrait 
        naturalSize = CGSize(width: videoAssetTrack.naturalSize.height, height: videoAssetTrack.naturalSize.width)
     else 
        naturalSize = videoAssetTrack.naturalSize
    

希望对您有所帮助。

【讨论】:

只是一个简单的问题,但是需要镜像是什么以及它是如何定义的?【参考方案2】:

尝试在镜像时应用负比例变换来翻转视频:

// Create an AvmutableVideoCompositionLayerInstruction
let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction.init(assetTrack: videoTrack!)
let flipped = videoAssetTrack.preferredTransform.scaledBy(x: -1.0, y: 1.0)
videoLayerInstruction.setTransform(flipped, at: kCMTimeZero)
compositionInstructions.layerInstructions = [videoLayerInstruction]    

【讨论】:

不幸的是,这并不能解决我的问题。无论如何,谢谢!

以上是关于AVMutableComposition 调整大小问题的主要内容,如果未能解决你的问题,请参考以下文章

将音频与视频完美融合

无法使用 AVAssetExportSession 导出 AVMutableComposition

AVMutableComposition - 是不是可以有 2 个并行音轨?

AVMutableComposition - 只播放第一首曲目 (Swift)

使用 AVPlayer 音频播放 AVMutableComposition 会不同步

AVFoundation - 视频没有在 AVplayer 的 AVMutablecomposition 中播放