压缩AVAsset(主要是AVMutableComposition)

时间:2017-02-23 10:32:51

标签: ios video compression avfoundation avasset

我有一个包含这些规格的视频

  
      
  • 格式:H.264,1280x544
  •   
  • FPS:25
  •   
  • 数据大小:26MB
  •   
  • 时长:3:00
  •   
  • 数据速率:1.17 Mbit / s
  •   

在试验期间,我在每个其他帧上执行了removeTimeRange(range : CMTimeRange)总帧数= 4225 )。这导致视频速度提高2倍,因此持续时间变为 1:30

然而,当我导出视频时,视频的大小增加了12倍,即 325MB 。这是有道理的,因为这项技术将视频分解为约 2112 块并拼接它回到了一起。显然,在这样做时,各个帧之间的压缩会丢失,从而导致巨大的尺寸。

当使用AVPlayer进行播放时,会导致视频中出现口吃,因此效果不佳。

问题:如何在拼接框架时应用某种压缩方式,以便视频可以流畅播放并且尺寸更小?

我只想要一个正确的方向。谢谢!

CODE

1)从Asset&创建AVMutableComposition配置

func configureAssets(){

let options =    [AVURLAssetPreferPreciseDurationAndTimingKey : "true"]
let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "Push", withExtension: "mp4")! , options : options)

let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack

let comp = AVMutableComposition()
let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

do {

    try videoCompositionTrack.insertTimeRange(
        CMTimeRangeMake(kCMTimeZero, videoAsset.duration),
        of: videoAssetSourceTrack,
        at: kCMTimeZero)


    deleteSomeFrames(from: comp)

    videoCompositionTrack.preferredTransform = videoAssetSourceTrack.preferredTransform

}catch { print(error) }

asset = comp   }

2)删除每隔一帧。

   func deleteSomeFrames(from asset : AVMutableComposition){

let fps =               Int32(asset.tracks(withMediaType: AVMediaTypeVideo).first!.nominalFrameRate)
let sumTime =           Int32(asset.duration.value)  /  asset.duration.timescale;
let totalFrames =       sumTime * fps
let totalTime =         Float(CMTimeGetSeconds(asset.duration))
let frameDuration =     Double(totalTime / Float(totalFrames))
let frameTime =         CMTime(seconds: frameDuration, preferredTimescale: 1000)


for frame in Swift.stride(from: 0, to: totalFrames, by: 2){

    let timing =    CMTimeMultiplyByFloat64(frameTime, Float64(frame))

    print("Asset Duration = \(CMTimeGetSeconds(asset.duration))")
    print("")

    let timeRange = CMTimeRange(start: timing, duration : frameTime)
    asset.removeTimeRange(timeRange)
}

print("duration after time removed = \(CMTimeGetSeconds(asset.duration))")
}

3)保存文件

  func createFileFromAsset(_ asset: AVAsset){

let documentsDirectory =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath =            documentsDirectory.appendingPathComponent("rendered-vid.mp4")

if let exportSession =    AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality){

    exportSession.outputURL = filePath
    exportSession.shouldOptimizeForNetworkUse = true
    exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
    exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession.exportAsynchronously {
        print("finished: \(filePath) :  \(exportSession.status.rawValue) ")

        if exportSession.status.rawValue == 4{

            print("Export failed -> Reason: \(exportSession.error!.localizedDescription))")
            print(exportSession.error!)

        }
 }}}

4)最后更新ViewController以播放新的Composition!

override func viewDidLoad() {
super.viewDidLoad()

//  Create the AVPlayer and play the composition

assetConfig.configureAssets()
let snapshot : AVComposition =  assetConfig.asset  as! AVComposition
let playerItem =                AVPlayerItem(asset : snapshot)
player =                        AVPlayer(playerItem: playerItem)
let playerLayer =               AVPlayerLayer(player: player)
playerLayer.frame =             CGRect(x : 0, y : 0, width : self.view.frame.width , height : self.view.frame.height)

self.view.layer.addSublayer(playerLayer)
player?.play()

  }

1 个答案:

答案 0 :(得分:0)

如果您使用AVMutableComposition,您会注意到每个合成可能包含一个或多个AVCompositionTrack(or AVMutableCompositionTrack),编辑合成的最佳方法是操作每个曲目,但不是整个曲目。

但如果您的目的是加快视频的速度,则无需编辑曲目。

所以我会尽力告诉你我对你的问题的了解

关于视频在播放时口吃

口吃的可能原因

请注意您使用的是方法removeTimeRange(range: CMTimeRange),此方法会删除构成时的timeRange,但 NOT 会自动填充每个时间范围的空白

Visualize Example
[F stand for Frame,E stand for Empty]

org_video    -->  F-F-F-F-F-F-F-F...
after remove time range, the composition will be like this
after_video  -->  F-E-F-E-F-E-F-E...
and you might think that the video will be like this
target_video -->  F-F-F-F...

这是播放过程中出现口吃的最可能原因。

建议的解决方案

因此,如果您想缩短视频,让速度更快/更慢,则可能需要使用方法scaleTimeRange:(CMTimeRange) toDuration:(CMTime)

Example
AVMutableComposition * project;//if this video is 200s

//Scale
project.scaleTimeRange:CMTimeRangeMake(kCMTimeZero, project.duration) toDuration:CMTimeMakeWithSeconds(100,kCMTimeMaxTimescale)

这种方法是让视频更快/更慢。

关于文件大小

视频文件的大小可能会受到比特率格式类型的影响,如果您使用H.264,导致尺寸放大的最可能原因将是比特率

在您的代码中,您正在使用AVAssetExportSession

AVAssetExportSession(asset: asset, presetName:     AVAssetExportPresetHighestQuality

你给出的预设是AVAssetExportPresetHighestQuality 在我自己的应用程序项目中,使用此预设后,无论您的源视频比特率如何,视频的比特率都将为20~30Mbps。并且,使用apple的预设将不允许您手动设置比特率,所以。

可能的解决方案

还有一个名为SDAVAssetExportSession的第三方工具,此会话将允许您完全配置导出会话,您可能希望尝试研究有关自定义导出会话预设的此代码。

这是我现在可以告诉你的。希望可以提供帮助:>