减少 swift/iOS 中的视频大小以上传到服务器

reduce video size in swift/iOS to upload to server

我正在使用 UIImagePickerController 从我的 swift iOS 应用中挑选视频。我正在保存此 URL,现在想将其转换为数据以发送到我的服务器进行存储,使用:

let messageVideoData = NSData(contentsOfURL: chosenVideoURL)

问题是文件太大了。对于在我的 iPhone 6s 上拍摄的 7 秒视频,分辨率为 1280、720,帧速率为 30,文件大小超过 4 MB。我注意到用 whatsapp 和其他聊天应用程序发送的相同图像减少到几百 KB。

减小外部存储文件大小的最佳方法是什么?该视频主要针对手机,因此可以将分辨率降低到 800 或更低。

我尝试将 UIImagePickerController 质量设置为:

picker.videoQuality = UIImagePickerControllerQualityType.Type640x480

但这仅将文件大小减小到 3.5 MB。

使用:

picker.videoQuality = UIImagePickerControllerQualityType.TypeLow

将分辨率降低到远低于理想值的值。

我应该采取另一种方法来减小我的视频文件大小以便存储在我的服务器上吗?

试试这个压缩视频的答案。根据jojaba's answer:

If you are wanting to compress the video for remote sharing and to keep the original quality for local storage on the iPhone, you should look into AVAssetExportSession or AVAssetWriter.

Compress Video Without Low Quality

虽然这种方法是按照 Objective-C。

您还应该考虑阅读 iOS 如何管理 Assets

//use SDAVAssetExportSession library with sprcifica bitrate as per requirement
// video file size ~10-15 MB apporox

func aVodzLatestVideoCompressor(inputURL: URL, aOutputURL: URL, aStartTime:Float, aEndTime:Float)   {
          
            let startTime = CMTime(seconds: Double(aStartTime), preferredTimescale: 1000)
            let endTime = CMTime(seconds: Double(aEndTime), preferredTimescale: 1000)
            let timeRange = CMTimeRange(start: startTime, end: endTime)
            
            let anAsset = AVURLAsset(url: inputURL, options: nil)
            guard let videoTrack = anAsset.tracks(withMediaType: AVMediaType.video).first else { return }
            var aQuality:Float = 0.0
            var duration = anAsset.duration
            let totalSeconds = Int(CMTimeGetSeconds(duration))
            print("duration -\(duration) - totalSeconds -\(totalSeconds)")
            
            var bitrate = min(aQuality, videoTrack.estimatedDataRate)
            let landscap = self.isLandScapVideo(afileURL: inputURL )
            var originalWidth = videoTrack.naturalSize.width
            var originalHeight  = videoTrack.naturalSize.height
            print("originalWidth -\(originalWidth) originalHeight- \(originalHeight) ")
            while (originalWidth >= 1920 || originalHeight >= 1920) {
                originalWidth = originalWidth / 2
                originalHeight = originalHeight / 2
            }
    
            var setWidth = Int(originalWidth)
            var setlHeight = Int(originalHeight)
            
            if  sizeVideo < 10.0 {
                // COMPRESS_QUALITY_HIGH:
                setWidth = Int(originalWidth)
                setlHeight = Int(originalHeight)
                aQuality = Float(setWidth * setlHeight *  20)
                bitrate = min(aQuality, videoTrack.estimatedDataRate)
            }else if sizeVideo < 20.0 {
                //COMPRESS_QUALITY_MEDIUM:
                if totalSeconds > 35{
                    setWidth = Int(originalWidth /  2.7)
                    setlHeight = Int(originalHeight / 2.7)
                }else if totalSeconds > 25 {
                    setWidth = Int(originalWidth / 2.3)
                    setlHeight = Int(originalHeight / 2.3)
                }else{
                    setWidth = Int(originalWidth / 2.0)
                    setlHeight = Int(originalHeight / 2.0)
                }
                aQuality = Float(setWidth * setlHeight *  10)
                bitrate = min(aQuality, videoTrack.estimatedDataRate)
            }else if sizeVideo < 30.0 {
                //COMPRESS_QUALITY_MEDIUM:
                if totalSeconds > 35{
                    setWidth = Int(originalWidth / 3)
                    setlHeight = Int(originalHeight / 3)
                }else if totalSeconds > 20 {
                    setWidth = Int(originalWidth / 2.5)
                    setlHeight = Int(originalHeight / 2.5)
                }else{
                    setWidth = Int(originalWidth / 2.0)
                    setlHeight = Int(originalHeight / 2.0)
                }
                aQuality = Float(setWidth * setlHeight * 10)
                bitrate = min(aQuality, videoTrack.estimatedDataRate)
            }else{
                if totalSeconds > 35{
                    setWidth = Int(originalWidth / 3.0)
                    setlHeight = Int(originalHeight / 3.0)
                }else if totalSeconds > 25 {
                    setWidth = Int(originalWidth / 2.5)
                    setlHeight = Int(originalHeight / 2.5)
                }else{
                    setWidth = Int(originalWidth / 2.0)
                    setlHeight = Int(originalHeight / 2.0)
                }
                aQuality = Float(setWidth * setlHeight * 10)
                bitrate = min(aQuality, videoTrack.estimatedDataRate)
            }
    
            print("aQuality")
            print(Float(aQuality))
            print("bitrate")
            print(Float(bitrate))
            let encoder = SDAVAssetExportSession(asset: anAsset)
            encoder?.shouldOptimizeForNetworkUse = true
     
            encoder?.timeRange = timeRange
            encoder?.outputFileType = AVFileType.mp4.rawValue
            encoder?.outputURL = aOutputURL
            //960 X 540 , 1280 * 720 , 1920*1080 // size reduce parameter
            encoder?.videoSettings = [
                AVVideoCodecKey: AVVideoCodecType.h264,
                AVVideoWidthKey:  landscap ? NSNumber(value:1280) : NSNumber(value:720) ,
                AVVideoHeightKey:  landscap ? NSNumber(value:720) : NSNumber(value:1280),
                AVVideoCompressionPropertiesKey: [
                    AVVideoAverageBitRateKey: NSNumber(value: bitrate),
                    AVVideoProfileLevelKey: AVVideoProfileLevelH264High40
                ]
            ]
            encoder?.audioSettings = [
                AVFormatIDKey: NSNumber(value: kAudioFormatMPEG4AAC),
                AVNumberOfChannelsKey: NSNumber(value: 2),
                AVSampleRateKey: NSNumber(value: 44100),
                AVEncoderBitRateKey: NSNumber(value: 128000)
            ]
            
            encoder?.exportAsynchronously(completionHandler: {
                if encoder?.status == .completed {
                    print("Video export succeeded")
                    DispatchQueue.main.async {
                        appDelegate.hideLoader()
                        //NotificationCenter.default.post(name: Notification.Name("getMediaEffect"), object: "3")
                        //self.sendCompletion?(UIImage(), aOutputURL)
                        let text = "Original video-  \(inputURL.verboseFileSizeInMB()) \n and Compressed video \(aOutputURL.verboseFileSizeInMB()) "
                        let alertController = UIAlertController.init(title: "Compressed!!", message: text , preferredStyle: .alert)
                        alertController.addAction(UIAlertAction.init(title: "share to server!", style: .default, handler: { (action) in
                            // Completion block
                            NotificationCenter.default.post(name: Notification.Name("getMediaEffect"), object: "3")
                            self.sendCompletion?(UIImage(), aOutputURL)
                        }))
                        alertController.addAction(UIAlertAction.init(title: "Save", style: .default, handler: { (action) in
                            // Completion block
                            DispatchQueue.main.async {
                                appDelegate.hideLoader()
                                if let videoURL = aOutputURL as? URL{
                                    self.shareVideo(aUrl:videoURL )
                                }
                            }
                        }))
                        alertController.addAction(UIAlertAction.init(title: "cancel!", style: .default, handler: { (action) in
                        }))
                        self.present(alertController, animated: true, completion: nil)
                    }
                    
                } else if encoder?.status == .cancelled {
                    print("Video export cancelled")
                    DispatchQueue.main.async {
                        appDelegate.hideLoader()
                        self.view.makeToast("error_something_went_wrong".localized)
                    }
                } else {
                    print("Video export failed with error: \(encoder!.error.localizedDescription) ")
                    DispatchQueue.main.async {
                        appDelegate.hideLoader()
                        self.view.makeToast("error_something_went_wrong".localized)
                    }
                }
            })
        }
    
     func isLandScapVideo(afileURL: URL) -> Bool{
            let resolution = self.resolutionForLocalVideo(url: afileURL)
            guard let width = resolution?.width, let height = resolution?.height else {
                return false
            }
            if abs(width) > abs(height){
                //landscap
                return true
            }else{
                //potrait
                return false
            }
        }
    extension URL {
        func verboseFileSizeInMB() -> Float{
            let p = self.path
            
            let attr = try? FileManager.default.attributesOfItem(atPath: p)
            
            if let attr = attr {
                let fileSize = Float(attr[FileAttributeKey.size] as! UInt64) / (1024.0 * 1024.0)
                print(String(format: "FILE SIZE: %.2f MB", fileSize))
                return fileSize
            } else {
                return Float.zero
            }
        }
    }
    
    
    //Below update if any issue in library at SDAVAssetExportSession library changes below at m file:(changes as per your requirement)
         ——   CGAffineTransform matrix = CGAffineTransformMakeTranslation(transx / xratio, transy / yratio - transform.ty);
    
     ——//fix Orientation - 1
        UIImageOrientation videoAssetOrientation = UIImageOrientationUp;
        BOOL isVideoAssetPortrait = NO;
        CGAffineTransform videoTransform = videoTrack.preferredTransform;
        if (videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0) {
            videoAssetOrientation = UIImageOrientationRight;
            isVideoAssetPortrait = YES;
        }
        if (videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0) {
            videoAssetOrientation =  UIImageOrientationLeft;
            isVideoAssetPortrait = YES;
        }
        if (videoTransform.a == 1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == 1.0) {
            videoAssetOrientation =  UIImageOrientationUp;
        }
        if (videoTransform.a == -1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == -1.0) {
            videoAssetOrientation = UIImageOrientationDown;
        }
       // [passThroughLayer setTransform:transform atTime:kCMTimeZero];
        if ((videoAssetOrientation = UIImageOrientationDown) || (videoAssetOrientation = UIImageOrientationLeft)){
            [passThroughLayer setTransform:videoTrack.preferredTransform atTime:kCMTimeZero];
        }else{
            [passThroughLayer setTransform:transform atTime:kCMTimeZero];
        }