需要镜像视频方向并处理前置摄像头旋转问题

6

我不太清楚如何处理前置摄像头视频捕捉方向。当捕捉视频和照片时,我已经处理了后置摄像头的所有旋转,并且处理了拍摄照片时前置摄像头的所有旋转,以及将捕获的视频和图片保存为正确的方向,除了前置摄像头视频捕捉之外。

第一个问题是无论是横屏还是竖屏模式下,视频都没有以正确的方向保存。第二个问题是保存的视频是镜像的。虽然我知道如何处理使用前置摄像头拍照的镜像效果,但我不确定如何处理视频。

我在尝试寻找有关此问题的具体信息时遇到了很多麻烦,并未成功。如果有人能指导我指向解决此特定问题的线程,那就太好了。

无论如何,这是被调用的处理设备方向变化时处理视频方向的方法。如果正在使用前置摄像头,我不确定应该添加什么代码。

/**************************************************************************
    DEVICE ORIENTATION DID CHANGE
    **************************************************************************/
    func deviceOrientationDidChange() {

        println("DEVICE ORIENTATION DID CHANGE CALLED")

        let orientation: UIDeviceOrientation = UIDevice.currentDevice().orientation

        //------ IGNORE THESE ORIENTATIONS ------
        if orientation == UIDeviceOrientation.FaceUp || orientation == UIDeviceOrientation.FaceDown || orientation == UIDeviceOrientation.Unknown || orientation == UIDeviceOrientation.PortraitUpsideDown || self.currentOrientation == orientation {

            println("device orientation does not need to change --- returning...")

            return
        }


        self.currentOrientation = orientation


        //------ APPLY A ROTATION USING THE STANDARD ROTATION TRANSFORMATION MATRIX in R3 ------

        /*

            x       y       z
            ---           ---
        x | cosø    sinø    0 |
        y | -sinø   consø   0 |
        z | 0       0       1 |
            ---           ---

        */


        //----- PERFORM BUTTON AND VIDEO DATA BUFFER ROTATIONS ------
        switch orientation {

        case UIDeviceOrientation.Portrait:

            rotateButtons(self.degrees0)

            if self.usingFrontCamera == true {


            }
            else {

            }

            println("Device Orientation Portrait")

            break

        case UIDeviceOrientation.LandscapeLeft:

            println("Device Orientation LandScapeLeft")

            rotateButtons(self.degrees90)

            if self.usingFrontCamera == true {

                println("Using front camera, rotation in landscape left")

//                if let connection = self.captureConnection {
//                    
//                    connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
//                    
//                    println("Capture connection Orientation is LandScape Right")
//                }
//                else {
//                    
//                    println("Capture connection is nil, could not change video orientation")
//                }
            }
            else {

                if let connection = self.captureConnection {

                    connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight

                    println("Capture connection Orientation is LandScape Right")
                }
                else {

                    println("Capture connection is nil, could not change video orientation")
                }
            }

            break

        case UIDeviceOrientation.LandscapeRight:

            println("Device Orientation LandscapeRight")

            rotateButtons(-self.degrees90)

            if self.usingFrontCamera == true {

                println("Using front camera, rotation in landscape right")

//                if let connection = self.captureConnection {
//                    
//                    connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
//                    
//                    println("Capture connection Orientation is LandScape Left")
//                }
//                else {
//                    
//                    println("Capture connection is nil, could not change video orientation")
//                }
            }
            else {

                if let connection = self.captureConnection {

                    connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft

                    println("Capture connection Orientation is LandScape Left")
                }
                else {

                    println("Capture connection is nil, could not change video orientation")
                }
            }

            break

        default:

            break
        }
    }
3个回答

16

根据这个答案:Video Saving in the wrong orientation AVCaptureSession

我遇到了同样的问题,并且通过这篇文章解决了它。

var videoConnection:AVCaptureConnection?
  for connection in self.fileOutput.connections {
    for port in connection.inputPorts! {
      if port.mediaType == AVMediaTypeVideo {
        videoConnection = connection as? AVCaptureConnection
          if videoConnection!.supportsVideoMirroring {
            videoConnection!.videoMirrored = true
          }
        }
      }
   }
}

请告诉我这是否对您有帮助,James。


1
谢谢您的回答。我希望明天能够检查一下这个方法是否有效,然后相应地更新问题。 - James Combs
1
你节省了我好几个小时的时间。所有其他类似的帖子都只涉及预览层,而不是文件输出。 - Vincenzo
2
仅在预览中显示镜像视频。保存时,会保存实际视频。该怎么办? - Aaina Jain
1
你可以使用 self.videoOutput?.connection(with: .video)?.isVideoMirrored = true 来获取连接。 - Kesong Xie

3

被采纳的答案仅会在预览中镜像视频。你需要对你的视频进行转换。

    func mirrorVideo(inputURL: URL, completion: @escaping (_ outputURL : URL?) -> ())
{
    let videoAsset: AVAsset = AVAsset( url: inputURL )
    let clipVideoTrack = videoAsset.tracks( withMediaType: AVMediaType.video ).first! as AVAssetTrack

    let composition = AVMutableComposition()
    composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())

    let videoComposition = AVMutableVideoComposition()
    videoComposition.renderSize = CGSize(width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.width)
    videoComposition.frameDuration = CMTimeMake(1, 30)

    let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack)

    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
    var transform:CGAffineTransform = CGAffineTransform(scaleX: -1.0, y: 1.0)
    transform = transform.translatedBy(x: -clipVideoTrack.naturalSize.width, y: 0.0)
    transform = transform.rotated(by: CGFloat(Double.pi/2))
    transform = transform.translatedBy(x: 0.0, y: -clipVideoTrack.naturalSize.width)

    transformer.setTransform(transform, at: kCMTimeZero)

    instruction.layerInstructions = [transformer]
    videoComposition.instructions = [instruction]

    // Export

    let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPreset640x480)!
    let fileName = UniqueIDGenerator.generate().appending(".mp4")
    let filePath = documentsURL.appendingPathComponent(fileName)
    let croppedOutputFileUrl = filePath
    exportSession.outputURL = croppedOutputFileUrl
    exportSession.outputFileType = AVFileType.mp4
    exportSession.videoComposition = videoComposition
    exportSession.exportAsynchronously {
        if exportSession.status == .completed {
            DispatchQueue.main.async(execute: {
                completion(croppedOutputFileUrl)
            })
            return
        } else if exportSession.status == .failed {
            print("Export failed - \(String(describing: exportSession.error))")
        }

        completion(nil)
        return
    }
}

在你的AVCaptureFileOutputRecordingDelegate
   func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
    self.mirrorVideo(inputURL: outputFileURL) { (url) in
        self.delegate!.videoRecordingEnded(videoURL: url!)
    }
}

有人能帮我解决第二部分self.delegate!.videoRecordingEnded(videoURL: url!)的问题吗?它显示“类型'ViewController'的值没有成员类型delegate”。我正在使用Swift 3。 - Jorge Manuel Bello

0

Kesong在Sam的回答中的评论适用于转换视频本身,而不仅仅是预览。请确保首先检查是否支持videoMirroring,并在设置isVideoMirrored属性之前确保禁用自动调整视频镜像,否则应用程序可能会崩溃。

下面的代码适用于Swift 5。您可以在将AVCaptureSession附加到AVCaptureMovieFileOutput后使用此代码。

  if let videoConnection = movieFileOutput.connection(with: .video) {
        if  videoConnection.isVideoMirroringSupported {
            if frontCamera { //For selfie camera, flip the output video so it doesn't appear mirrored
              videoConnection.automaticallyAdjustsVideoMirroring = false
              videoConnection.isVideoMirrored = true
            } else {
              videoConnection.automaticallyAdjustsVideoMirroring = true
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接