How to trim the video using AVFoundation

后端 未结 4 1651
醉话见心
醉话见心 2020-12-02 17:51

Iam able to record the video by using AVFoundation or UIImagePickerController. But i am unable to trim the video from one particular second to another particular duration/ti

相关标签:
4条回答
  • 2020-12-02 18:20

    Swift version of above

    import UIKit
    import AVFoundation
    import MobileCoreServices
    
    func pickVideo(){
        if UIImagePickerController.isSourceTypeAvailable(.Camera) {
            let videoRecorder = UIImagePickerController()
            videoRecorder.sourceType = .Camera
            videoRecorder.mediaTypes = [kUTTypeMovie as String]
            videoRecorder.allowsEditing = true
            videoRecorder.delegate = self
    
            presentViewController(videoRecorder, animated: true, completion: nil)
        }
    }
    
    
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        picker.dismissViewControllerAnimated(true, completion: nil)
        let manager = NSFileManager.defaultManager()
    
        guard let documentDirectory = try? manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) else {return}
        guard let mediaType = info[UIImagePickerControllerMediaType] as? String else {return}
        guard let url = info[UIImagePickerControllerMediaURL] as? NSURL else {return}
    
        if mediaType == kUTTypeMovie as String || mediaType == kUTTypeVideo as String {
            let asset = AVAsset(URL: url)
            let length = Float(asset.duration.value) / Float(asset.duration.timescale)
            print("video length: \(length) seconds")
    
            let start = info["_UIImagePickerControllerVideoEditingStart"] as? Float
            let end = info["_UIImagePickerControllerVideoEditingEnd"] as? Float
    
    
            var outputURL = documentDirectory.URLByAppendingPathComponent("output")
    
    
            do {
                try manager.createDirectoryAtURL(outputURL, withIntermediateDirectories: true, attributes: nil)
                outputURL = outputURL.URLByAppendingPathComponent("output.mp4")
            }catch let error {
                print(error)
            }
    
            //Remove existing file
             _ = try? manager.removeItemAtURL(outputURL)
    
    
            guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {return}
            exportSession.outputURL = outputURL
            exportSession.outputFileType = AVFileTypeMPEG4
    
            let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
            let endTime = CMTime(seconds: Double(end ?? length), preferredTimescale: 1000)
            let timeRange = CMTimeRange(start: startTime, end: endTime)
    
            exportSession.timeRange = timeRange
            exportSession.exportAsynchronouslyWithCompletionHandler{
                switch exportSession.status {
                case .Completed:
                    print("exported at \(outputURL)")
    
                case .Failed:
                    print("failed \(exportSession.error)")
    
                case .Cancelled:
                    print("cancelled \(exportSession.error)")
    
                default: break
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 18:22

    You can have the UIImagePickerController enable trimming

    UIImagePickerController *videoRecorder = [[UIImagePickerController alloc]init];         
            NSArray *sourceTypes = [UIImagePickerController availableMediaTypesForSourceType:videoRecorder.sourceType];
            NSLog(@"Available types for source as camera = %@", sourceTypes);
            if (![sourceTypes containsObject:(NSString*)kUTTypeMovie] ) {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil 
                                                                message:@"Device Not Supported for video Recording."                                                                       delegate:self 
                                                      cancelButtonTitle:@"Yes" 
                                                      otherButtonTitles:@"No",nil];
                [alert show];
                [alert release];
                return;
            }
            videoRecorder.allowsEditing = YES;
    

    Unfortunately after you get back from the imagePickerController, You are forced to convert the video manually.

    -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 
    {
        if ([self.popoverLibraryBrowser isPopoverVisible])
        {
            [self.popoverLibraryBrowser dismissPopoverAnimated:YES];
        }
        NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
        if ([type isEqualToString:(NSString *)kUTTypeVideo] || 
            [type isEqualToString:(NSString *)kUTTypeMovie]) { // movie != video
            NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
    
    
            NSNumber *start = [info objectForKey:@"_UIImagePickerControllerVideoEditingStart"];
            NSNumber *end = [info objectForKey:@"_UIImagePickerControllerVideoEditingEnd"];
    
            // if start and end are nil then clipping was not used.
            // You should use the entire video.
    
    
            int startMilliseconds = ([start doubleValue] * 1000);
            int endMilliseconds = ([end doubleValue] * 1000);
    
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *documentsDirectory = [paths objectAtIndex:0];
    
            NSFileManager *manager = [NSFileManager defaultManager];
    
            NSString *outputURL = [documentsDirectory stringByAppendingPathComponent:@"output"] ;
            [manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
    
            outputURL = [outputURL stringByAppendingPathComponent:@"output.mp4"];
            // Remove Existing File
            [manager removeItemAtPath:outputURL error:nil];
    
    
            //[self loadAssetFromFile:videoURL];
    
            [self.recorder dismissModalViewControllerAnimated:YES];
    
            AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil]; 
    
    
            AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
            exportSession.outputURL = [NSURL fileURLWithPath:outputURL];
            exportSession.outputFileType = AVFileTypeQuickTimeMovie;
            CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startMilliseconds, 1000), CMTimeMake(endMilliseconds - startMilliseconds, 1000));
            exportSession.timeRange = timeRange;
    
            [exportSession exportAsynchronouslyWithCompletionHandler:^{
                switch (exportSession.status) {
                    case AVAssetExportSessionStatusCompleted:
                        // Custom method to import the Exported Video
                        [self loadAssetFromFile:exportSession.outputURL];
                        break;
                    case AVAssetExportSessionStatusFailed:
                        //
                        NSLog(@"Failed:%@",exportSession.error);
                        break;
                    case AVAssetExportSessionStatusCancelled:
                        //
                        NSLog(@"Canceled:%@",exportSession.error);
                        break;
                    default:
                        break;
                }
            }];
    
    
    
            //NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
            //NSString *videoStoragePath;//Set your video storage path to this variable
            //[videoData writeToFile:videoStoragePath atomically:YES];
            //You can store the path of the saved video file in sqlite/coredata here.
        }
    }
    
    0 讨论(0)
  • 2020-12-02 18:30

    you should add kUTTypeMovie in the setMediaTypes array and it will work.

    0 讨论(0)
  • 2020-12-02 18:32

    The best solution for swift 4, i have found there. I did fixes it for my needs, but it's really clear and convenience.

    The code:

    import AVFoundation
    import Foundation
    
    extension FileManager {
        func removeFileIfNecessary(at url: URL) throws {
            guard fileExists(atPath: url.path) else {
                return
            }
    
            do {
                try removeItem(at: url)
            }
            catch let error {
                throw TrimError("Couldn't remove existing destination file: \(error)")
            }
        }
    }
    
    struct TrimError: Error {
        let description: String
        let underlyingError: Error?
    
        init(_ description: String, underlyingError: Error? = nil) {
            self.description = "TrimVideo: " + description
            self.underlyingError = underlyingError
        }
    }
    
    extension AVMutableComposition {
        convenience init(asset: AVAsset) {
            self.init()
    
            for track in asset.tracks {
                addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
            }
        }
    
        func trim(timeOffStart: Double) {
            let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
            let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
    
            for track in tracks {
                track.removeTimeRange(timeRange)
            }
    
            removeTimeRange(timeRange)
        }
    }
    
    extension AVAsset {
        func assetByTrimming(timeOffStart: Double) throws -> AVAsset {
            let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
            let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
    
            let composition = AVMutableComposition()
    
            do {
                for track in tracks {
                    let compositionTrack = composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
                    try compositionTrack?.insertTimeRange(timeRange, of: track, at: kCMTimeZero)
                }
            } catch let error {
                throw TrimError("error during composition", underlyingError: error)
            }
    
            return composition
        }
    
        func export(to destination: URL) throws {
            guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetPassthrough) else {
                throw TrimError("Could not create an export session")
            }
    
            exportSession.outputURL = destination
            exportSession.outputFileType = AVFileType.m4v
            exportSession.shouldOptimizeForNetworkUse = true
    
            let group = DispatchGroup()
    
            group.enter()
    
            try FileManager.default.removeFileIfNecessary(at: destination)
    
            exportSession.exportAsynchronously {
                group.leave()
            }
    
            group.wait()
    
            if let error = exportSession.error {
                throw TrimError("error during export", underlyingError: error)
            }
        }
    }
    
    func time(_ operation: () throws -> ()) rethrows {
        let start = Date()
    
        try operation()
    
        let end = Date().timeIntervalSince(start)
        print(end)
    
    let sourceURL =  URL(fileURLWithPath: CommandLine.arguments[1])
    let destinationURL = URL(fileURLWithPath: CommandLine.arguments[2])
    
    do {
        try time {
            let asset = AVURLAsset(url: sourceURL)
            let trimmedAsset = try asset.assetByTrimming(timeOffStart: 1.0)
            try trimmedAsset.export(to: destinationURL)
        }
    } catch let error {
        print("                                                                    
    0 讨论(0)
提交回复
热议问题