开发者

How to trim the video using AVFoundation

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 duratio开发者_如何学运维n/time. Can any one help me.

Thanks, Siva Krishna.


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


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


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜