ios swift 自界说压缩压缩视频(清晰,办理了旋转题目) ...

打印 上一主题 下一主题

主题 773|帖子 773|积分 2319

    公司最近有一个需求,需要压缩视频,体积和清晰度都有要求。在网上查了很多资料,终极使用了assetReader和assetWriter,但是网上找到的资料,直接拷贝到项目中,都或多或少有些题目,或少了一些参数,所以我把自己这些天查到的,并更改好,能顺利压缩的代码写出来,记录下,也方便各人检察。但这些代码只是适用于我自己的项目,如果各人使用有题目的话,可以留言大概自己网上去探求办理方法。
    之前压缩视频,我使用的是苹果提供的一个基于AVFoundation框架的类,AVAssetExportSession,用它来进行视频压缩和转码,使用也是比较方便的,只需设置输出之类和格式等参数。如下图所示,设置比较简单:
  1. func saveVideo(from videoURL: URL,
  2.                to videoFinalPath: URL,
  3.                handler: ((URL?) -> Void)? = nil) {
  4.    
  5.     let asset = AVAsset(url: videoURL)
  6.     if (deleteIfExists(path: videoFinalPath)) {
  7.             
  8.         guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset960x540) else {
  9.             handler?(nil)
  10.             return
  11.         }
  12.         exporter.outputURL = videoFinalPath
  13.         exporter.outputFileType = .mp4
  14.         exporter.exportAsynchronously { () -> Void in
  15.             switch exporter.status {
  16.             case  .failed:
  17.                 let err = exporter.error
  18.                 print("failed import video: \(exporter.error)")
  19.                 handler?(nil)
  20.             case .cancelled:
  21.                 print("cancelled import video: \(exporter.error)")
  22.                 handler?(nil)
  23.             case .completed:
  24.                 print("completed import video save")
  25.                 handler?(videoFinalPath)
  26.             default:
  27.                 handler?(nil)
  28.                 break
  29.             }
  30.         }
  31.     }
  32. }
复制代码
    此中AVAssetExportPreset960x540就是压缩的质量参数。之前我选择的是AVAssetExportPresetMediumQuality,压缩的体积照旧不错的。压缩的视频是我自己拍摄的41秒的视频,70多MB,能压缩到10多MB,但是视频的清晰度就大打扣头。厥后我选择了AVAssetExportPreset640x480的压缩参数,压缩出来为18.3MB,但是视频照旧不太清晰,但是比之前AVAssetExportPresetMediumQuality的要清晰一点。选 AVAssetExportPreset960x540,压缩出来为27MB,视频这时是比较清晰的,但是体积太大了。参考微信的压缩视频功能,微信中发送视频,默认的压缩出来是13MB多,而且很清晰。所以,上面的参数都达不到这个结果,需要重新找方法去办理。

    之后,我使用了assetReader和assetWriter,这个是可以自界说压缩参数并实现更精细的控制,但使用起来相对复杂一些。在使用的时候需要设置好音视频的参数,如果设置不对,大概压缩失败,直接报错闪退。我反复设置了参数后,终于可以压缩,视频的体积和清晰度都能到达想要的结果,但是这时我发现,压缩出来的视频永远是相当于home键在右侧的方向。比如,我拿手机拍一个视频,手机是竖着的,home键朝下,那么压缩出来的视频,便是会逆时针转90度。查了参数,才知道可以通过AVAssetTrack中的preferredTransform检察视频的方向。默认0度就是手机横着,Home键在右边拍摄的视频。如果手机是竖着,Home键在下方,拍摄的视频,那么他的角度是90度。代码如下:
  1. //获取视频的角度
  2. private func degressFromVideoFileWithURL(videoTrack: AVAssetTrack)->Int {
  3.     var degress = 0
  4.     let t: CGAffineTransform = videoTrack.preferredTransform
  5.     if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
  6.         // Portrait
  7.         degress = 90
  8.     }else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
  9.         // PortraitUpsideDown
  10.         degress = 270
  11.     }else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
  12.         // LandscapeRight
  13.         degress = 0
  14.     }else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
  15.         // LandscapeLeft
  16.         degress = 180
  17.     }
  18.     return degress
  19. }
复制代码
    然后,我也找到相识决方法。如果视频的角度不为0,我们在压缩时,直接把视频手动旋转到之前的角度就行了。各人切记,不要再用AVAssetExportSession去旋转方向,网上很多如许的文章,我也试了,转向是成功了,但是之前压缩好的体积,经过AVAssetExportSession再一压缩又变大了……
        现在把已经测试过能顺利运行的代码贴出来,供各人参考:
  1. func exportVideo(from videoURL: URL,
  2.                  to videoFinalPath: URL,
  3.                  completeHandler: @escaping (URL) -> ()) {
  4.     // 创建音视频输入asset
  5.     let asset = AVAsset(url: videoURL)
  6.     // 原视频大小,用于测试压缩效果
  7.     let oldfileItem = try? FileManager.default.attributesOfItem(atPath: videoURL.path)
  8.     print(oldfileItem?[FileAttributeKey.size]) //打印之前视频体积
  9.     // 创建音视频Reader和Writer
  10.     guard let reader = try? AVAssetReader(asset: asset),
  11.           let writer = try? AVAssetWriter.init(outputURL: videoFinalPath, fileType: AVFileType.mp4) else { return}
  12.    
  13.     //视频输出配置
  14.     let configVideoOutput: [String : Any] = [kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_422YpCbCr8)] as! [String: Any]
  15.     //音频输出配置
  16.     let configAudioOutput: [String : Any] = [AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM)] as! [String: Any]
  17.    
  18.     let compressionProperties: [String: Any] = [AVVideoAverageBitRateKey: 1600 * 1024,  //码率
  19.                                        AVVideoExpectedSourceFrameRateKey: 25,           //帧率
  20.                                                   AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel]
  21.     let videoCodeec: String = AVVideoCodecType.h264.rawValue //视频编码
  22.     var videoSettings: [String: Any] = [AVVideoCodecKey: videoCodeec, //视频编码
  23.                                         AVVideoWidthKey: 960,           //视频宽(必须填写正确,否则压缩后有问题)
  24.                                        AVVideoHeightKey: 540,           //视频高(必须填写正确,否则压缩后有问题)
  25.                         AVVideoCompressionPropertiesKey: compressionProperties,
  26.                                   AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill] //设置视频缩放方式
  27.    
  28.     let stereoChannelLayout: AudioChannelLayout = AudioChannelLayout(mChannelLayoutTag: kAudioChannelLayoutTag_Stereo,
  29.                                                                      mChannelBitmap: AudioChannelBitmap.init(rawValue: 0),
  30.                                                                      mNumberChannelDescriptions: 0,
  31.                                                                      mChannelDescriptions: AudioChannelDescription())
  32.     let channelLayoutAsData: NSData = NSData(bytes: (stereoChannelLayout as? UnsafeRawPointer), length: MemoryLayout.size(ofValue: AudioChannelLayout.self))
  33.     let audioSettings: [String: Any] = [AVFormatIDKey         : kAudioFormatMPEG4AAC,
  34.                                         AVEncoderBitRateKey   : 96000, // 码率
  35.                                         AVSampleRateKey       : 44100, // 采样率
  36.                                         AVChannelLayoutKey    : channelLayoutAsData,
  37.                                         AVNumberOfChannelsKey : 2]
  38.     // video part
  39.     guard let videoTrack: AVAssetTrack = (asset.tracks(withMediaType: .video)).first else {return}
  40.    
  41.     //获取原视频的角度
  42.     let degree = self.degressFromVideoFileWithURL(videoTrack: videoTrack)
  43.     //获取原视频的宽高,如果是手机拍摄,一般是宽大,高小,如果是手机自带录屏,那么是高大,宽小
  44.     let naturalSize = videoTrack.naturalSize
  45.     if naturalSize.width < naturalSize.height {
  46.         videoSettings[AVVideoWidthKey] = 540
  47.         videoSettings[AVVideoHeightKey] = 960
  48.     }
  49.    
  50.    
  51.     let videoOutput: AVAssetReaderTrackOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: configVideoOutput)
  52.     let videoInput: AVAssetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
  53.     //视频写入的旋转(这句很重要)
  54.     if let transform = self.getAffineTransform(degree: degree, videoTrack: videoTrack) {
  55.         videoInput.transform = transform
  56.     }
  57.    
  58.     if reader.canAdd(videoOutput) {
  59.         reader.add(videoOutput)
  60.     }
  61.     if writer.canAdd(videoInput) {
  62.         writer.add(videoInput)
  63.     }
  64.     // audio part
  65.     guard let audioTrack: AVAssetTrack = (asset.tracks(withMediaType: .audio)).first else {return}
  66.     let audioOutput: AVAssetReaderTrackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: configAudioOutput)
  67.     let audioInput: AVAssetWriterInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
  68.     if reader.canAdd(audioOutput) {
  69.         reader.add(audioOutput)
  70.     }
  71.     if writer.canAdd(audioInput) {
  72.         writer.add(audioInput)
  73.     }
  74.     // 开始读写
  75.     reader.startReading()
  76.     writer.startWriting()
  77.     writer.startSession(atSourceTime: .zero)
  78.    
  79.     let group = DispatchGroup()
  80.    
  81.     group.enter()
  82.     videoInput.requestMediaDataWhenReady(on: DispatchQueue(label: "videoOutQueue"), using: {
  83.        var completedOrFailed = false
  84.         while (videoInput.isReadyForMoreMediaData) && !completedOrFailed {
  85.             let sampleBuffer: CMSampleBuffer? = videoOutput.copyNextSampleBuffer()
  86.             if sampleBuffer != nil {//}&& reader.status == .reading {
  87.                 let result = videoInput.append(sampleBuffer!)
  88. //                    if (!result) {
  89. //                        reader.cancelReading()
  90. //                        break
  91. //                    }
  92.             } else {
  93.                 completedOrFailed = true
  94.                 videoInput.markAsFinished()
  95.                 group.leave()
  96.                 break
  97.             }
  98.         }
  99.     })
  100.    
  101.     group.enter()
  102.     audioInput.requestMediaDataWhenReady(on: DispatchQueue(label: "audioOutQueue"), using: {
  103.         var completedOrFailed = false
  104.          while (audioInput.isReadyForMoreMediaData) && !completedOrFailed {
  105.              let sampleBuffer: CMSampleBuffer? = audioOutput.copyNextSampleBuffer()
  106.              if sampleBuffer != nil {//}&& reader.status == .reading {
  107.                  let result = audioInput.append(sampleBuffer!)
  108. //                    if (!result) {
  109. //                        reader.cancelReading()
  110. //                        break
  111. //                    }
  112.              } else {
  113.                  completedOrFailed = true
  114.                  audioInput.markAsFinished()
  115.                  group.leave()
  116.                  break
  117.              }
  118.          }
  119.      })
  120.     group.notify(queue: DispatchQueue.main) {
  121.         if reader.status == .reading {
  122.             reader.cancelReading()
  123.         }
  124.         switch writer.status {
  125.         case .writing:
  126.             writer.finishWriting(completionHandler: {
  127.                 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3, execute: {
  128.                     let newfileSize = try? FileManager.default.attributesOfItem(atPath: videoFinalPath.path)
  129.                     print(newfileSize?[FileAttributeKey.size]) //打印压缩后视频的体积
  130.                     completeHandler(videoFinalPath)
  131.                 })
  132.             })
  133.         case .cancelled:
  134.             print("$$$ compress cancelled")
  135.         case .failed:
  136.             print("$$$ compress failed",writer.error)
  137.         case .completed:
  138.             print("$$$ compress completed")
  139.         case .unknown:
  140.             print("$$$ compress unknown")
  141.         }
  142.     }
  143. }
复制代码
  1. private func getAffineTransform(degree: Int, videoTrack: AVAssetTrack)-> CGAffineTransform? {
  2.     var translateToCenter: CGAffineTransform?
  3.     var mixedTransform: CGAffineTransform?
  4.     if degree == 90 { //视频旋转90度,home按键在左"
  5.         translateToCenter = CGAffineTransform(translationX: videoTrack.naturalSize.height, y: 0.0)
  6.         mixedTransform = translateToCenter!.rotated(by: Double.pi / 2)
  7.     } else if degree == 180 { //视频旋转180度,home按键在上"
  8.         translateToCenter = CGAffineTransform(translationX: videoTrack.naturalSize.width, y: videoTrack.naturalSize.height)
  9.         mixedTransform = translateToCenter!.rotated(by: Double.pi)
  10.     } else if degree == 270 { //视频旋转270度,home按键在右"
  11.         translateToCenter = CGAffineTransform(translationX: 0.0, y: videoTrack.naturalSize.width)
  12.         mixedTransform = translateToCenter!.rotated(by: Double.pi / 2 * 3)
  13.     }
  14.     return mixedTransform
  15. }
复制代码
    以上便是所有代码,渴望对各人有帮助!另外关于压缩视频的参数,有篇文章写得非常详细,链接为:手把手教你iOS自界说视频压缩 - 知乎
    各人可以在视频压缩出来后,参照文章的参数,进行调整,以便到达最佳结果。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表