jjjjjjjj
jjjjjjjj

Reputation: 4503

compressing a video in iOS

I've looked at this question and this question, neither have been able to help.

I have tried the following:

- (void)compress:(NSURL *)videoPath completionBlock:(void(^)(id data, BOOL result))block{
    self.outputFilePath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
    NSURL *outputURL = [NSURL fileURLWithPath:self.outputFilePath];
    [self compressVideoWithURL:self.movieURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession) {

    }];
}

- (void)compressVideoWithURL:(NSURL*)inputURL
                   outputURL:(NSURL*)outputURL
                     handler:(void (^)(AVAssetExportSession*))handler {

    AVURLAsset *asset = [AVURLAsset assetWithURL:self.movieURL];
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
    exportSession.fileLengthLimit = 3000000;
    exportSession.outputURL = outputURL;
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    exportSession.shouldOptimizeForNetworkUse = YES;
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        NSData *newOutputData = [NSData dataWithContentsOfURL:outputURL];
        NSLog(@"Size of New Video(bytes):%d",[newOutputData length]);
    }];
}

I know that self.movieUrl is not nil. But when I printed the size (in bytes) of the NSData associated with the video, they were the same before and after, both 30,000,000 bytes.

But according to this question, the above code should work.

What am I doing wrong exactly?

Upvotes: 7

Views: 16823

Answers (3)

Chandan
Chandan

Reputation: 349

You can use this method for video compression.

struct ReduceVideoSizeError: Error { }

func reduceVideoSize(inputURL: URL, outputURL: URL, completion: @escaping (Error?) -> Void) {
    let asset = AVAsset(url: inputURL)
    
    asset.loadTracks(withMediaType: .video) { videoTracks, error in
        guard let videoTrack = videoTracks?.first else {
            completion(NSError(domain: "YourAppDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to load video track"]))
            return
        }
        
        asset.loadTracks(withMediaType: .audio) { audioTracks, error in
            guard let audioTrack = audioTracks?.first else {
                completion(NSError(domain: "YourAppDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to load audio track"]))
                return
            }
            
            guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetMediumQuality) else {
                completion(NSError(domain: "YourAppDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create AVAssetExportSession"]))
                return
            }
            
            let videoSettings: [String : AnyObject] = [
                AVVideoCodecKey : AVVideoCodecType.h264 as AnyObject,
                AVVideoWidthKey : videoTrack.naturalSize.width as AnyObject,
                AVVideoHeightKey : videoTrack.naturalSize.height as AnyObject,
                AVVideoCompressionPropertiesKey : [
                    AVVideoAverageBitRateKey: 600000 // Adjust bitrate as needed
                ] as AnyObject
            ]
            
            let audioSettings: [String : AnyObject] = [
                AVFormatIDKey: kAudioFormatMPEG4AAC as AnyObject,
                AVNumberOfChannelsKey: 2 as AnyObject,
                AVSampleRateKey: 44100 as AnyObject,
                AVEncoderBitRateKey: 64000 as AnyObject
            ]
            
            exportSession.outputURL = outputURL
            exportSession.outputFileType = .mp4
            
            exportSession.timeRange = CMTimeRange(start: .zero, duration: asset.duration)
            
            exportSession.videoComposition = AVVideoComposition(asset: asset) { request in
                let source = request.sourceImage.clampedToExtent()
                request.finish(with: source, context: nil)
            }
            
            exportSession.audioMix = AVAudioMix() // Ensure audio settings are respected
            
            exportSession.exportAsynchronously {
                if exportSession.status == .completed {
                    completion(nil)
                } else if let error = exportSession.error {
                    completion(error)
                } else {
                    completion(NSError(domain: "YourAppDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown error"]))
                }
            }
        }
    }
}

private func getVideoWriterSettings(bitrate: Int, width: Int, height: Int) -> [String : AnyObject] {
    let videoWriterCompressionSettings = [
        AVVideoAverageBitRateKey: bitrate
    ]
    
    let videoWriterSettings: [String : AnyObject] = [
        AVVideoCodecKey: AVVideoCodecType.h264 as AnyObject,
        AVVideoCompressionPropertiesKey: videoWriterCompressionSettings as AnyObject,
        AVVideoWidthKey: width as AnyObject,
        AVVideoHeightKey: height as AnyObject
    ]
    
    return videoWriterSettings
}

Upvotes: 0

Himanshu Moradiya
Himanshu Moradiya

Reputation: 4815

In Swift 3.0

Select video from Camera roll

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
     if info[UIImagePickerControllerMediaType] as? String == (kUTTypeMovie as? String) {
        // here your video capture code
        let videoURL = info[UIImagePickerControllerMediaURL] as! NSURL!
        let data = NSData(contentsOf: videoURL! as URL)!
        print("File size before compression: \(Double(data.length / 1048576)) mb")
        let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".m4v")
        compressVideo(inputURL: videoURL as! URL, outputURL: compressedURL) { (exportSession) in
                guard let session = exportSession else {
                    return
                }

                switch session.status {
                case .unknown:
                    break
                case .waiting:
                    break
                case .exporting:
                    break
                case .completed:
                    guard let compressedData = NSData(contentsOf: compressedURL) else {
                        return
                    }
                   print("File size after compression: \(Double(compressedData.length / 1048576)) mb")
                case .failed:
                    break
                case .cancelled:
                    break
                }
            }
       }
       self.dismiss(animated: true, completion: nil)
 }

Compression type :

AVAssetExportPresetLowQuality
AVAssetExportPresetMediumQuality
AVAssetExportPresetHighestQuality
AVAssetExportPreset640x480 
AVAssetExportPreset960x540

Compress video method

 func compressVideo(inputURL: URL, outputURL: URL, handler:@escaping (_ exportSession: AVAssetExportSession?)-> Void) {
        let urlAsset = AVURLAsset(url: inputURL, options: nil)
        guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetLowQuality) else {
            handler(nil)

            return
        }

        exportSession.outputURL = outputURL
        exportSession.outputFileType = AVFileTypeQuickTimeMovie
        exportSession.shouldOptimizeForNetworkUse = true
        exportSession.exportAsynchronously { () -> Void in
            handler(exportSession)
        }
    }

Output :

File size before compression: 25.0 mb
File size after compression: 7.0 mb

Upvotes: 21

jjjjjjjj
jjjjjjjj

Reputation: 4503

I have figured it out, thanks to this question (Swift version): IOS Video Compression Swift iOS 8 corrupt video file

I have the objective C version. Here is the method:

 - (void)compressVideo:(NSURL*)inputURL
        outputURL:(NSURL*)outputURL
          handler:(void (^)(AVAssetExportSession*))completion  {
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:urlAsset presetName:AVAssetExportPresetMediumQuality];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
    completion(exportSession);
  }];
}

And calling the method:

NSURL* uploadURL = [NSURL fileURLWithPath:
                     [NSTemporaryDirectory() stringByAppendingPathComponent:@"temporaryPreview.mov"]];

[self compressVideo:self.movieURL outputURL:uploadURL handler:^(AVAssetExportSession *completion) {
    if (completion.status == AVAssetExportSessionStatusCompleted) {
        NSData *newDataForUpload = [NSData dataWithContentsOfURL:uploadURL];
        NSLog(@"Size of new Video after compression is (bytes):%d",[newDataForUpload length]);
    }
}];

This reduced the file size of my videos from 32 MB to 1.5 MB.

Upvotes: 10

Related Questions