【问题标题】:AVFoundation exporting orientation wrongAVFoundation 导出方向错误
【发布时间】:2016-02-09 01:12:47
【问题描述】:

我正在尝试合并图像和视频。我让它们组合和导出,但是它是侧向旋转的。

对于大量代码粘贴,我们深表歉意。我已经看到了有关将转换应用于compositionVideoTrack.preferredTransform 的答案,但是这无济于事。添加到AVMutableVideoCompositionInstruction 也无济于事。

我觉得这个区域是事情开始出错的地方。在这里:

// I feel like this loading here is the problem
        let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]

        // because it makes our parentLayer and videoLayer sizes wrong
        let videoSize       = videoTrack.naturalSize

        // this is returning 1920x1080, so it is rotating the video
        print("\(videoSize.width) , \(videoSize.height)")

所以到这里我们的帧大小对于该方法的其余部分是错误的。现在,当我们尝试创建覆盖图像层时,框架不正确:

    let aLayer = CALayer()
    aLayer.contents = UIImage(named: "OverlayTestImageOverlay")?.CGImage
    aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height)
    aLayer.opacity = 1

这是我的完整方法。

  func combineImageVid() {

        let path = NSBundle.mainBundle().pathForResource("SampleMovie", ofType:"MOV")
        let fileURL = NSURL(fileURLWithPath: path!)

        let videoAsset = AVURLAsset(URL: fileURL)
        let mixComposition = AVMutableComposition()

        let compositionVideoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

        var clipVideoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)

        do {
            try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: clipVideoTrack[0], atTime: kCMTimeZero)
        }
        catch _ {
            print("failed to insertTimeRange")
        }


        compositionVideoTrack.preferredTransform = videoAsset.preferredTransform

        // I feel like this loading here is the problem
        let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]

        // because it makes our parentLayer and videoLayer sizes wrong
        let videoSize       = videoTrack.naturalSize

        // this is returning 1920x1080, so it is rotating the video
        print("\(videoSize.width) , \(videoSize.height)")

        let aLayer = CALayer()
        aLayer.contents = UIImage(named: "OverlayTestImageOverlay")?.CGImage
        aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height)
        aLayer.opacity = 1


        let parentLayer     = CALayer()
        let videoLayer      = CALayer()

        parentLayer.frame   = CGRectMake(0, 0, videoSize.width, videoSize.height)
        videoLayer.frame    = CGRectMake(0, 0, videoSize.width, videoSize.height)

        parentLayer.addSublayer(videoLayer)
        parentLayer.addSublayer(aLayer)


        let videoComp = AVMutableVideoComposition()
        videoComp.renderSize = videoSize
        videoComp.frameDuration = CMTimeMake(1, 30)
        videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)

        let instruction = AVMutableVideoCompositionInstruction()

        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)

        let mixVideoTrack = mixComposition.tracksWithMediaType(AVMediaTypeVideo)[0]
        mixVideoTrack.preferredTransform = CGAffineTransformMakeRotation(CGFloat(M_PI * 90.0 / 180))

        let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mixVideoTrack)
        instruction.layerInstructions = [layerInstruction]
        videoComp.instructions = [instruction]


        //  create new file to receive data
        let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let docsDir: AnyObject = dirPaths[0]
        let movieFilePath = docsDir.stringByAppendingPathComponent("result.mov")
        let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath)

        do {
            try NSFileManager.defaultManager().removeItemAtPath(movieFilePath)
        }
        catch _ {}


        // use AVAssetExportSession to export video
        let assetExport = AVAssetExportSession(asset: mixComposition, presetName:AVAssetExportPresetHighestQuality)
        assetExport?.videoComposition = videoComp
        assetExport!.outputFileType = AVFileTypeQuickTimeMovie
        assetExport!.outputURL = movieDestinationUrl
        assetExport!.exportAsynchronouslyWithCompletionHandler({
            switch assetExport!.status{
            case  AVAssetExportSessionStatus.Failed:
                print("failed \(assetExport!.error)")
            case AVAssetExportSessionStatus.Cancelled:
                print("cancelled \(assetExport!.error)")
            default:
                print("Movie complete")


                // play video
                NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                    print(movieDestinationUrl)
                })
            }
        })
    }

这就是我要导出的内容:


我尝试添加这两种方法来旋转视频:

class func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {

    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)

    let assetTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0]

    let transform = assetTrack.preferredTransform
    let assetInfo = orientationFromTransform(transform)
    var scaleToFitRatio = UIScreen.mainScreen().bounds.width / assetTrack.naturalSize.width

    if assetInfo.isPortrait {

        scaleToFitRatio = UIScreen.mainScreen().bounds.width / assetTrack.naturalSize.height
        let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio)
        instruction.setTransform(CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor),
            atTime: kCMTimeZero)
    } else {

        let scaleFactor = CGAffineTransformMakeScale(scaleToFitRatio, scaleToFitRatio)
        var concat = CGAffineTransformConcat(CGAffineTransformConcat(assetTrack.preferredTransform, scaleFactor), CGAffineTransformMakeTranslation(0, UIScreen.mainScreen().bounds.width / 2))
        if assetInfo.orientation == .Down {
            let fixUpsideDown = CGAffineTransformMakeRotation(CGFloat(M_PI))
            let windowBounds = UIScreen.mainScreen().bounds
            let yFix = assetTrack.naturalSize.height + windowBounds.height
            let centerFix = CGAffineTransformMakeTranslation(assetTrack.naturalSize.width, yFix)
            concat = CGAffineTransformConcat(CGAffineTransformConcat(fixUpsideDown, centerFix), scaleFactor)
        }
        instruction.setTransform(concat, atTime: kCMTimeZero)
    }

    return instruction
}

class func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {
    var assetOrientation = UIImageOrientation.Up
    var isPortrait = false
    if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
        assetOrientation = .Right
        isPortrait = true
    } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
        assetOrientation = .Left
        isPortrait = true
    } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
        assetOrientation = .Up
    } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
        assetOrientation = .Down
    }
    return (assetOrientation, isPortrait)
}

更新后的我的combineImageVid() 方法添加了这个

let instruction = AVMutableVideoCompositionInstruction()

instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)

let mixVideoTrack = mixComposition.tracksWithMediaType(AVMediaTypeVideo)[0]

//let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mixVideoTrack)
//layerInstruction.setTransform(videoAsset.preferredTransform, atTime: kCMTimeZero)

let layerInstruction = videoCompositionInstructionForTrack(compositionVideoTrack, asset: videoAsset)

这给了我这个输出:

所以我越来越接近了,但我觉得由于轨道最初加载的方式错误,我需要解决那里的问题。另外,我不知道为什么现在有巨大的黑匣子。我想这可能是由于我的图像层在此处获取了加载的视频资产的边界:

aLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height)

但是,将其更改为较小的宽度/高度并没有什么不同。然后我想添加一个裁剪记录来摆脱黑色方块,但这也不起作用:(


遵循 Allens 不使用这两种方法的建议:

class func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction

class func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) 

但是将我原来的方法更新为如下所示:

videoLayer.frame    = CGRectMake(0, 0, videoSize.height, videoSize.width) //notice the switched width and height
...
videoComp.renderSize = CGSizeMake(videoSize.height,videoSize.width) //this make the final video in portrait
...
layerInstruction.setTransform(videoTrack.preferredTransform, atTime: kCMTimeZero) //important piece of information let composition know you want to rotate the original video in output

我们已经非常接近了,但是现在问题似乎是编辑renderSize。如果我将其更改为横向大小以外的任何内容,我会得到:

【问题讨论】:

  • 此链接可能对您有所帮助stackoverflow.com/questions/10034337/…
  • 我也试过了 :( 不过我很欣赏这个建议。
  • 你可以尝试在 combineImageVid() 方法中更改它吗 --- compositionVideoTrack.preferredTransform = CGAffineTransformMakeRotation(M_PI_2);
  • @AnkitGupta 根本没有帮助 :( 它似乎从不尊重该设置

标签: ios swift camera orientation avassetexportsession


【解决方案1】:

这是 Apple 培训的文档:

https://developer.apple.com/library/ios/qa/qa1744/_index.html

如果您的原始视频是在 iOS 纵向模式下拍摄的,它的自然尺寸仍然是横向的,但它在 mov 文件中带有旋转元数据。为了旋转您的视频,您需要使用以下代码更改您的第一段代码:

videoLayer.frame    = CGRectMake(0, 0, videoSize.height, videoSize.width) //notice the switched width and height
...
videoComp.renderSize = CGSizeMake(videoSize.height,videoSize.width) //this make the final video in portrait
...
layerInstruction.setTransform(videoTrack.preferredTransform, atTime: kCMTimeZero) //important piece of information let composition know you want to rotate the original video in output

是的,你真的很亲密!

【讨论】:

  • 感谢您的帮助!我们已经非常接近了,但是在更改 renderSize 时似乎存在问题。我已经用 .gif 更新了任何问题。
  • @random,你在模拟器中运行吗?那就不要了,在真机iOS上运行,模拟器翻译不好。
  • 是的,在模拟器中运行它就是问题所在!你太棒了,非常感谢你的帮助!!
  • 我试过这段代码,它适用于后置摄像头,但前面仍然得到相同的结果。视频方向正确,但被切成两半。
  • 查看前置摄像头链接stackoverflow.com/questions/43818429/…
【解决方案2】:

也许你应该检查videoTrack的preferredTransform以便给它一个准确的renderSize和transform:

CGAffineTransform transform = assetVideoTrack.preferredTransform;
CGFloat rotation = [self rotationWithTransform:transform]; 
//if been rotated
        if (rotation != 0)
        {
            //if rotation is 360°
            if (fabs((rotation - M_PI * 2)) >= valueOfError) {

                CGFloat m = rotation / M_PI;
                CGAffineTransform t1;
                //rotation is 90° or 270°
                if (fabs(m - 1/2.0) < valueOfError || fabs(m - 3/2.0) < valueOfError) {
                    self.mutableVideoComposition.renderSize = CGSizeMake(assetVideoTrack.naturalSize.height,assetVideoTrack.naturalSize.width);
                    t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.height, 0);
                }
                //rotation is 180°
                if (fabs(m - 1.0) < valueOfError) {
                    t1 = CGAffineTransformMakeTranslation(assetVideoTrack.naturalSize.width, assetVideoTrack.naturalSize.height);
                }
                CGAffineTransform t2 = CGAffineTransformRotate(t1,rotation);
                //                CGAffineTransform transform = makeTransform(1.0, 1.0, 90, videoTrack.naturalSize.height, 0);
                [passThroughLayer setTransform:t2 atTime:kCMTimeZero];
            }
        }

//convert transform to radian
- (CGFloat)rotationWithTransform:(CGAffineTransform)t
{
    return atan2f(t.b, t.a);
}

【讨论】:

  • 对不起,我已经翻译了扩展。 @luk2302
猜你喜欢
  • 2018-04-29
  • 1970-01-01
  • 2016-05-02
  • 2014-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
相关资源
最近更新 更多