使用前置摄像头时需要镜像视频方向和手柄旋转

Posted

技术标签:

【中文标题】使用前置摄像头时需要镜像视频方向和手柄旋转【英文标题】:Need to Mirror Video Orientation and Handle Rotation When Using Front Camera 【发布时间】:2015-08-05 01:12:15 【问题描述】:

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

第一个问题是在任一横向模式下,视频都无法以正确的方向正确保存。第二个问题是保存的视频是镜像的。虽然我知道如何使用前置摄像头处理图片的这种镜像效果,但我不确定要调用什么来处理视频。

我在试图找到专门针对这一问题的任何东西时遇到了很多麻烦,但未能做到。如果有人能指出我解决这个特定问题的线程,那就太好了。

无论哪种方式,这里都是在设备方向改变时处理视频方向的方法。如果正在使用前置摄像头,我不确定要在我的代码中添加什么。

/**************************************************************************
    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
        
    

【问题讨论】:

【参考方案1】:

基于此答案: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
          
        
      
   

如果对你有帮助,请告诉我詹姆斯

【讨论】:

感谢您的回答。明天我会检查这是否可行,然后会相应地更新问题。 你为我节省了几个小时。所有其他类似的线程只处理预览层而不是文件输出。 仅在预览中显示镜像视频。保存时,它正在保存实际视频。怎么办? 可以使用self.videoOutput?.connection(with: .video)?.isVideoMirrored = true获取连接【参考方案2】:

接受的答案只会镜像预览中的视频。您需要转换视频。

    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' 类型的值没有成员类型委托”我正在使用 swift 3【参考方案3】:

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
            
        
    

【讨论】:

以上是关于使用前置摄像头时需要镜像视频方向和手柄旋转的主要内容,如果未能解决你的问题,请参考以下文章

setOrientationHint 在某些手机的前置摄像头 (HTC) 上逆时针旋转视频

如何查看前置摄像头 ID?捕获的图像也是来自前置摄像头的镜像。

使用前置摄像头录制视频时分别获取视频和音频缓冲区

Android翻盖前置摄像头镜头翻转视频

如何让视频有camera

去除前置摄像头的镜像效果