简体   繁体   中英

UIImagePickerController allowsEditing = YES, getting untrimmed video after trimming

Problem:

When I record a video in my UIImagePickerController with allowsEditing set to YES, and afterwards trim the video by using the trim-interface that comes after video capture, I get returned the original video, instead of the trimmed one.

Setup:

I am using a UIImagePickerController for video capture, with the allowsEditing property set to YES. In the delegate method didFinishPickingMediaWithInfo , I use UIImagePickerControllerMediaURL from the info NSDictionary to get the path URL. The official Apple docs don't mention any Edited video URL unfortunately.

Code:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];

    if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0)
        == kCFCompareEqualTo) {

        self.tempVideoPath = [[info objectForKey:
                                UIImagePickerControllerMediaURL] path];
    }
}

I realise this question is similar to other ones posted here on SO, but there was no definitive answer why it doesn't work or why the option is even there. If it is intended like this, I don't understand why there is an 'allowsEditing' property for the picker.

EDIT: In the info dictionary I got back are the following keys:

info: {
    UIImagePickerControllerMediaType = "public.movie";
    UIImagePickerControllerMediaURL = "file://localhost/private/var/mobile/Applications/F12E4608-FE5A-4EE3-B4E2-8F7D2508C4C8/tmp/capture-T0x21d810.tmp.wabFCC/capturedvideo.MOV";
    "_UIImagePickerControllerVideoEditingEnd" = "5.498333333333333";
    "_UIImagePickerControllerVideoEditingStart" = "4.273402690887451";
}

Does this mean we have to trim it ourselves with this data? Then the Apple documentation isn't very clear about this. If so, do you know a good practice for this?

take a look at the highlighted answer on this post:

How to trim the video using AVFoundation

I think it's exactly what you want. The answer uses UIImagePickerController too

Hope it helps, Mário

You will need to use a UIVideoEditorController for this. It's Delegate Protocol specifies a method videoEditorController:didSaveEditedVideoToPath: which seems to be what you want. There is sample code available here , as referenced in this SO question .

Here's a quick and dirty Swift 5 example of how to trim the video from UIImagePickerController

extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        let mediaType = info[.mediaType] as! String

        dismiss(animated: true) { [weak self] in
            // Handle a movie capture
            if mediaType == kUTTypeMovie as String {
                guard let videoURL = info[.mediaURL] as? URL else {
                    SwiftyBeaver.error("Could not get URL for movie")
                    return
                }

                let editingEnd = UIImagePickerController.InfoKey(rawValue: "_UIImagePickerControllerVideoEditingEnd")
                let editingStart = UIImagePickerController.InfoKey(rawValue: "_UIImagePickerControllerVideoEditingStart")

                let startMilliseconds: Double?
                let endMilliseconds: Double?

                if let start = info[editingStart] as? Double, let end = info[editingEnd] as? Double {
                    startMilliseconds = start
                    endMilliseconds = end
                } else {
                    startMilliseconds = nil
                    endMilliseconds = nil
                }

                let alert = UIAlertController(title: "Creating", message: "File is being processed", preferredStyle: .alert)
                self?.present(alert, animated: true)
                self?.process(srcVideoURL: videoURL, startSeconds: startMilliseconds, endSeconds: endMilliseconds) { (error) in
                    DispatchQueue.main.async {
                        if let error = error {
                            alert.title = "Whoops"
                            alert.message = "\(error)"
                            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
                                self?.dismiss(animated: true, completion: nil)
                            }))
                            return
                        }
                        self?.dismiss(animated: true, completion: nil)
                    }
                }
            }
        }
    }
}

enum VideoError: Error {
    case error(message: String)
}

extension ViewController {
    func process(srcVideoURL: URL, startSeconds: Double?, endSeconds: Double?, completed: @escaping (_ error: Error?) -> ()) {
        DispatchQueue.global(qos: .userInitiated).async {
            let dstVideoURL: URL // some URL for the destination
            do {
                try self.handleNewVideo(srcVideoURL: srcVideoURL, dstVideoURL: dstVideoURL, startSeconds: startSeconds, endSeconds: endSeconds)
                completed(nil)
            } catch {
                completed(error)
            }
        }
    }

    func handleNewVideo(srcVideoURL: URL, dstVideoURL: URL, startSeconds: Double?, endSeconds: Double?) throws {
        guard let start = startSeconds, let end = endSeconds else {
            print("No video editing information. Copying file.")
            try FileManager.default.moveItem(at: srcVideoURL, to: dstVideoURL)
            return
        }

        print("Video editing information. Processing start \(start) end \(end).")
        let videoAsset = AVURLAsset(url: srcVideoURL)
        let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPresetHighestQuality)!
        exportSession.outputURL = dstVideoURL
        exportSession.outputFileType = AVFileType.mov
        let timeRange = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 1000), duration: CMTime(seconds: end - start, preferredTimescale: 1000))
        exportSession.timeRange = timeRange

        var error: Error? = nil
        let dispatchGroup = DispatchGroup()
        dispatchGroup.enter()
        exportSession.exportAsynchronously(completionHandler: {
            switch exportSession.status {
            case .completed:
                break
            case .failed:
                error = exportSession.error ?? VideoError.error(message: "Unknown failed error")
            case .cancelled:
                error = exportSession.error ?? VideoError.error(message: "Video Cancelled")
            case .exporting:
                error = exportSession.error ?? VideoError.error(message: "Video still exporting")
            case .unknown:
                error = exportSession.error ?? VideoError.error(message: "Unknown unknown error")
            case .waiting:
                error = exportSession.error ?? VideoError.error(message: "Waiting error")
            @unknown default:
                error = exportSession.error ?? VideoError.error(message: "Future error")
            }
            dispatchGroup.leave()
        })
        dispatchGroup.wait()

        if let error = error {
            throw error
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM