my portraits videos gets rotated after load from url (AVURLAsset)

168 Views Asked by At

I have a picker that access the photo/video gallery but my problem is when the video is portrait, the width and the height of the video comes inverted. I've wrote a patch that check for the size of the video and when width is bigger than height, then it rotates it. But obviously it won't work with other videos than portrait.

Is there a way to access the orientation of the video? Or any other fix to this issue would be welcome.

This is the code for my adPicker method, imagePicker(didFinishPickingMediaWithInfo:) delegate method and the getFramesFromVideo(fileUrl:) method:

@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

There are 1 best solutions below

0
On

Found a solution with this property : videoTrack.preferredTransform

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

When tx comes with a value, means the video has been rotated. With a simple if else that rotates the images of the video, in case it has been rotated, will solve the issue:

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
    }
}

hope it will help someone