【问题标题】:Swift - Compressing video filesSwift - 压缩视频文件
【发布时间】:2017-03-21 02:50:57
【问题描述】:

所以,目前我正在使用它来压缩视频:

func compressVideo(inputURL: NSURL, outputURL: NSURL, handler:(session: AVAssetExportSession)-> Void)
    {
        let urlAsset = AVURLAsset(URL: inputURL, options: nil)

        let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetMediumQuality)

        exportSession!.outputURL = outputURL

        exportSession!.outputFileType = AVFileTypeQuickTimeMovie

        exportSession!.shouldOptimizeForNetworkUse = true

        exportSession!.exportAsynchronouslyWithCompletionHandler { () -> Void in

            handler(session: exportSession!)
        }

    }

当我在 2 秒内录制视频时,大小为 4,3 MB,当我在 6 秒内录制视频时,文件大小为 9,3 MB .

有什么减小尺寸的小窍门吗?

【问题讨论】:

标签: ios swift xcode video avassetexportsession


【解决方案1】:

此扩展专注于将其导出到较低质量的设置(在本例中为 Medium),并使用 mp4 容器,而不是 iOS 青睐的 mov 容器。这可能会导致质量下降,但您可以在微调输出时尝试更高的输出设置和不同的格式。

extension PreviewVideoViewController: AVCaptureFileOutputRecordingDelegate {
    func fileOutput(_ output: AVCaptureFileOutput,
                    didFinishRecordingTo outputFileURL: URL,
                    from connections: [AVCaptureConnection],
                    error: Error?) {
        guard let data = try? Data(contentsOf: outputFileURL) else {
            return
        }

        print("File size before compression: \(Double(data.count / 1048576)) mb")

        let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + UUID().uuidString + ".mp4")
        compressVideo(inputURL: outputFileURL 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 = try? Data(contentsOf: compressedURL) else {
                    return
                }

                print("File size after compression: \(Double(compressedData.count / 1048576)) mb")
            case .failed:
                break
            case .cancelled:
                break
            }
        }
    }


    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: AVAssetExportPresetMediumQuality) else {
            handler(nil)

            return
        }

        exportSession.outputURL = outputURL
        exportSession.outputFileType = .mp4
        exportSession.exportAsynchronously {
            handler(exportSession)
        }
    }
}

【讨论】:

  • 我想既然我的班级名称是class PreviewVideoViewController: UIViewController {,我应该用PreviewVideoViewController 替换你的MyViewController?但我确实收到 3 条错误消息:s22.postimg.org/p8ps48lm9/…
  • @RoduckNickes 你的应用是 Swift 2.2 的吗?
  • 当我在终端运行 xcrun swift -version 时,它会显示 Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) Target: x86_64-apple-macosx10.9
  • @RoduckNickes 更新了 2.2 的答案,因为您的错误表明您不是为 3.0 编写的。如果这对您不起作用,请告诉我。
  • 当我使用此代码压缩视频时,我发现视频中没有声音..?
【解决方案2】:
   func convertVideo(phAsset : PHAsset){

    PHImageManager.default().requestAVAsset(forVideo: phAsset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in
        if let asset = asset as? AVURLAsset {
            do {
                let videoData = try  Data.init(contentsOf: asset.url)
                print(asset.url)
                self.orginalVideo = asset.url
                print("File size before compression: \(Double(videoData.count / 1048576)) mb")
            let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + NSUUID().uuidString + ".MP4")
                print(compressedURL)
                self.compressVideo(inputURL: asset.url , outputURL: compressedURL) { (exportSession) in
                    guard let session = exportSession else {
                        return
                    }
                    switch session.status {
                    case .unknown:
                        print("unknown")
                        break
                    case .waiting:
                        print("waiting")
                        break
                    case .exporting:
                        print("exporting")
                        break
                    case .completed:
                        do {
                        let compressedData = try  Data.init(contentsOf: compressedURL)
                            self.compressVideo = compressedURL
                            print(compressedData)
                             print("File size AFTER compression: \(Double(compressedData.count / 1048576)) mb")
                        }
                        catch{
                           print(error)
                        }


                    case .failed:
                        print("failed")
                        break
                    case .cancelled:
                        print("cancelled")
                        break
                    }
                }
            } catch {
                print(error)
                //return
            }
        }
    })


}

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: AVAssetExportPresetMediumQuality) else {
        handler(nil)

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

【讨论】:

    【解决方案3】:
    //use SDAVAssetExportSession library with dynamic bitrate
    
    func aVodzLatestVideoCompressor(inputURL: URL, aOutputURL: URL, aStartTime:Float, aEndTime:Float)   {
                let sizeVideo:Float = inputURL.verboseFileSizeInMB()
                if  sizeVideo < 5.0 {
                    DispatchQueue.main.async {
                       //self.directsendvideowithoutcomressor()
                    }
                    return
                }
                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
                }
            }
        }
        
        
        //Extra 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];
            }
    

    【讨论】:

    猜你喜欢
    • 2018-02-27
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 2017-11-29
    • 1970-01-01
    相关资源
    最近更新 更多