从 url (AVURLAsset) 加载后,我的肖像视频会旋转

Posted

技术标签:

【中文标题】从 url (AVURLAsset) 加载后,我的肖像视频会旋转【英文标题】:my portraits videos gets rotated after load from url (AVURLAsset) 【发布时间】:2018-08-05 21:08:17 【问题描述】:

我有一个可以访问照片/视频库的选择器,但我的问题是当视频是纵向时,视频的宽度和高度会反转。 我写了一个补丁来检查视频的大小,当宽度大于高度时,它会旋转它。但显然它不适用于肖像以外的其他视频。

有没有办法访问视频的方向?或者欢迎任何其他解决此问题的方法。

这是我的 adPicker 方法、imagePicker(didFinishPickingMediaWithInfo:) 委托方法和 getFramesFromVideo(fileUrl:) 方法的代码:

@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 
    if let image = info[UIImagePickerControllerOriginalImage] as? UIImage
        editableView.drawnImage.image = image
        initialVC.updateState(newState: .photoAdded)
        initialVC.viewUpdater.updateContainerHeight()
    
    else if let videoUrl = info[UIImagePickerControllerMediaURL] as? URL
    
        imageManager.getFramesFromVideo(fileURL: videoUrl)
    
    initialVC.dismiss(animated: true, completion: nil)



func addPicker()->UIAlertController

    let myAlert = UIAlertController()
    guard let imagePicker = self.initialVC.imagePicker else 
        fatalError()
        return myAlert
    
    for type in alertTypes
        let alertText = type == .photoGallery ? AlertText.typeGalery : type == .camera ? AlertText.typeCamera : type == .video ? AlertText.typeVideo : AlertText.typeCancel
        myAlert.title = AlertText.title
        myAlert.message = ""
        let cameraAction = UIAlertAction(title : alertText, style : .default)  (action) in
            imagePicker.delegate = self
            imagePicker.allowsEditing = false

            //type photo gallery
            if type == .photoGallery && UIImagePickerController.isSourceTypeAvailable(.photoLibrary) 
                imagePicker.sourceType = .photoLibrary
                self.initialVC.present(imagePicker, animated: true, completion: nil)
            

            //type camera
            if type == .camera && UIImagePickerController.isSourceTypeAvailable(.camera) 
                imagePicker.sourceType = .camera
                self.initialVC.present(imagePicker, animated: true, completion: nil)
            

            if type == .video 

                imagePicker.mediaTypes = [String(kUTTypeMovie)]
                imagePicker.videoQuality = .typeHigh
                imagePicker.videoMaximumDuration = VideoTime.maxDuration
                self.initialVC.present(imagePicker, animated: true, completion: nil)
            
            if type == .cancel 
                self.initialVC.dismiss(animated: true, completion: nil)
            
        
        myAlert.addAction(cameraAction)
    
    return myAlert




func getFramesFromVideo(fileURL: URL)

    let asset = AVURLAsset(url: fileURL, options: nil)
    let videoDuration = asset.duration
    print(asset.metadata)
    let generator = AVAssetImageGenerator(asset: asset)
    generator.requestedTimeToleranceAfter = kCMTimeZero
    generator.requestedTimeToleranceBefore = kCMTimeZero

    var imageError : Error?

    frameForTimes = [NSValue]()

    let totalTimeLength = Int(videoDuration.seconds * Double(videoDuration.timescale))
    let step = totalTimeLength / VideoTime.maxSampleCounts

    for i in 0 ..< VideoTime.maxSampleCounts 
        let cmTime = CMTimeMake(Int64(i * step), Int32(videoDuration.timescale))
        frameForTimes.append(NSValue(time: cmTime))
    

    generator.generateCGImagesAsynchronously(forTimes: frameForTimes, completionHandler: requestedTime, image, actualTime, result, error in
        DispatchQueue.main.async 
            guard imageError == nil else return
            if let image = image 

                if self.tempImages.count != self.frameForTimes.count //avoid crash
                
                    self.videoFrameDelegate?.fillFramesFromVideo(frame: UIImage(cgImage: image))
                    self.tempImages.updateValue(self.frameForTimes[self.tempImages.count] as! CMTime, forKey: UIImage(cgImage: image))
                    print("image \(actualTime.seconds) loaded")
                

            
            else if (error != nil) 
                imageError = error
                //TODO: Error handler
                generator.cancelAllCGImageGeneration()
                self.tempImages.removeAll()
                self.videoFrameDelegate?.removeFrames()
                print(error as Any)
            
            // Completion handler
            if self.tempImages.count == self.frameForTimes.count
            
                print("frames ended loading")
                self.tempImages.removeAll()
                self.frameForTimes.removeAll()
                self.videoFrameDelegate?.loadFrames()
            
        
    )

【问题讨论】:

【参考方案1】:

找到具有此属性的解决方案:videoTrack.preferredTransform

 ▿ CGAffineTransform
 - a : 0.0
 - b : 1.0
 - c : -1.0
 - d : 0.0
 - tx : 360.0
 - ty : 0.0

当 tx 带有一个值时,表示视频已经旋转。使用简单的if else 旋转视频的图像,如果它已被旋转,将解决问题:

extension UIImage 
    func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage 
        //Calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
        let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
        rotatedViewBox.transform = t
        let rotatedSize: CGSize = rotatedViewBox.frame.size
        //Create the bitmap context
        UIGraphicsBeginImageContext(rotatedSize)
        let bitmap: CGContext = UIGraphicsGetCurrentContext()!
        //Move the origin to the middle of the image so we will rotate and scale around the center.
        bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
        //Rotate the image context
        bitmap.rotate(by: (degrees * CGFloat.pi / 180))
        //Now, draw the rotated/scaled image into the context
        bitmap.scaleBy(x: 1.0, y: -1.0)
        bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    

希望对某人有所帮助

【讨论】:

以上是关于从 url (AVURLAsset) 加载后,我的肖像视频会旋转的主要内容,如果未能解决你的问题,请参考以下文章

AVURLAsset 拒绝加载视频

AVURLAsset 加载 webvtt 文件

为啥我的 AVURLAsset 没有持续时间?

为啥 AVURLAsset 不加载文件?

AVURLAsset的缓存行为

如何从视频中获取最后一帧?