GPUImage系列专栏 参考:AVFoundation Programming Guide
AVFoundation系列五:关于音视频的导出 AVFoundation系列四:如何配置一个合格的Camera AVFoundation系列三:音视频编辑 AVFoundation系列二:用AVPlayer播放视频
1. AVAsset的加载方式 2. 播放一个AVAsset 3. 获取一个asset的相关属性 4. 从相册加载AVAsset 5. loadValuesAsynchronously的使用 6. 从视频中获取视频帧 图像 7. 通过一个AVAsset导出音频,设置时间裁剪 8. 通过一个AVAsset导出视频,设置时间裁剪
一、AVAsset的加载方式 第一种加载方式
let path = Bundle.main.path(forResource: "3", ofType: "mp4") let asset = AVAsset.init(url: URL.init(fileURLWithPath: path!, isDirectory: true))第二种加载方式
let options = [AVURLAssetPreferPreciseDurationAndTimingKey:true] let asset = AVURLAsset.init(url: URL.init(fileURLWithPath: path!, isDirectory: true), options: options)其中:AVURLAssetPreferPreciseDurationAndTimingKey 获取精确时间 通常不会再播放时使用 URL.init(fileURLWithPath: path!, isDirectory: true)如果URL是一个文件路径,苹果建议加上isDirectory
二、播放一个AVAsset 由于生命周期的原因,我们添加两个成员变量:
var player:AVPlayer? var playerItem:AVPlayerItem?播放:
func plackTrack(track:AVAssetTrack?){ guard let asset = track?.asset else{ return } playerItem = AVPlayerItem.init(asset: asset) player = AVPlayer.init(playerItem: playerItem!) let playerlayer = AVPlayerLayer.init(player: player!) playerlayer.frame = view.bounds self.view.layer.addSublayer(playerlayer) player?.play() }AVAssetTrack 如: let audioTrack = asset.tracks(withMediaType: .audio) let videoTrack = asset.tracks(withMediaType: .video)
三、获取一个asset的相关属性
func getAssetAttribute(){ let path = Bundle.main.path(forResource: "3", ofType: "mp4") let asset = AVAsset.init(url: URL.init(fileURLWithPath: path!, isDirectory: true)) //时长 let duration = asset.duration.seconds //歌词 let lyrics = asset.lyrics //创建时间 通常从相册加载时会有数据 let creatDate = asset.creationDate?.dataValue //相关信息 如 iso let metadata = asset.metadata(forFormat: .isoUserData) print(duration,lyrics ?? "",creatDate ?? "",metadata) }四、从相册加载AVAsset 这里没有写相关代码,请参考PhotoKit
五、loadValuesAsynchronously的使用
func asyncLoadInfo(){ let path = Bundle.main.path(forResource: "3", ofType: "mp4") let asset = AVAsset.init(url: URL.init(fileURLWithPath: path!, isDirectory: true)) ///当一个asset加载时,它的部分属性是未知的,因此需要 asset来完成指定的加载 asset.loadValuesAsynchronously(forKeys: ["playable"]) { var error: NSError? let keyStatus = asset.statusOfValue(forKey: "playable", error: &error) } }六、从视频中获取视频帧 图像
func getImageByAsset(){ let path = Bundle.main.path(forResource: "3", ofType: "mp4") let asset = AVAsset.init(url: URL.init(fileURLWithPath: path!, isDirectory: true)) if asset.tracks(withMediaType: .video).count > 0 { let imgGen = AVAssetImageGenerator.init(asset: asset) //最大尺寸 imgGen.maximumSize = CGSize.init(width: 100, height: 100) //光圈 imgGen.apertureMode = AVAssetImageGenerator.ApertureMode.cleanAperture //异步加载多个 imgGen.generateCGImagesAsynchronously(forTimes: [CMTime.zero as NSValue]) { (time1, cgimg, time2, result, error) in if let _cgimg = cgimg { let img = UIImage.init(cgImage: _cgimg) print("async get a img by asset") } } var actrueTime:CMTime = CMTime.zero //同步加载一个 if let cgimg = try? imgGen.copyCGImage(at: CMTime.zero, actualTime: &actrueTime){ let img = UIImage.init(cgImage: cgimg) print("sync get a img by asset") } } }generateCGImagesAsynchronously可以一次性异步加载多个 videoComposition 与 appliesPreferredTrackTransform冲突,会导致彼此失效
七、通过一个AVAsset导出音频、视频,设置时间裁剪
func exportAsset(){ guard let path = Bundle.main.path(forResource: "3", ofType: "mp4") else{ return } let url = URL.init(fileURLWithPath: path) let asset = AVAsset.init(url: url) let videoTracks = asset.tracks(withMediaType: .video) let audioTracks = asset.tracks(withMediaType: .audio) let firstVideoTrack = videoTracks.first let firstAudioTrack = audioTracks.first //视频 let videocomposition = AVMutableComposition.init() if let compositionVideoTrack = videocomposition.addMutableTrack(withMediaType: .video, preferredTrackID: 0){ if firstVideoTrack != nil{ try? compositionVideoTrack.insertTimeRange(firstVideoTrack!.timeRange, of: firstVideoTrack!, at: .zero) videoExportSession(asset: videocomposition) } } //音频 let audiocomposition = AVMutableComposition.init() if let compositionAudioTrack = audiocomposition.addMutableTrack(withMediaType: .audio, preferredTrackID: 0){ if firstAudioTrack != nil{ try? compositionAudioTrack.insertTimeRange(firstAudioTrack!.timeRange, of: firstAudioTrack!, at: .zero) audioExportSession(asset: audiocomposition) } } }可以调用 cancelExport 来取消 导出 audioExport?.cancelExport() AVMutableComposition 用于控制组件成分,本身是AVAsset的子类,意味着它可以像,AVAsset一样被用于播放。
//导出 func audioExportSession(asset:AVAsset){ let presetNames = AVAssetExportSession.exportPresets(compatibleWith: asset) if presetNames.contains(AVAssetExportPresetAppleM4A) { audioExport = AVAssetExportSession.init(asset: asset, presetName: AVAssetExportPresetAppleM4A) }else{ audioExport = AVAssetExportSession.init(asset: asset, presetName: AVAssetExportPresetPassthrough) } audioExport?.outputURL = getAudioExportURL() audioExport?.outputFileType = AVFileType.m4a audioExport?.shouldOptimizeForNetworkUse = true audioExport?.exportAsynchronously(completionHandler: {[weak self]in print(self?.audioExport?.error) }) } //获取url func getAudioExportURL()->URL{ let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileName = "Quinn_export" + ".m4a" let url = path.appendingPathComponent(fileName) if FileManager.default.fileExists(atPath: url.path){ try? FileManager.default.removeItem(at: url) } print("quinn",url) return url }其中:videoExport、audioExport 为 AVAssetExportSession,在接下来的文章会介绍到。 presetNames 本视频支持导出的格式 AVAssetExportPresetPassthrough 为模拟器支持格式. 如果想要裁剪 设置相关参数 audioExport?.timeRange = getTimeRange(asset:asset) 时间裁剪方法:
//公共函数 //设置裁剪参数 func getTimeRange(asset:AVAsset)->CMTimeRange{ print(asset.duration.timescale) let start = CMTimeMake(value: Int64(asset.duration.timescale * 10), timescale: asset.duration.timescale) let end = CMTimeMake(value: Int64(asset.duration.timescale * 30), timescale: asset.duration.timescale) let range = CMTimeRange.init(start: start, end: end) return range }同理 ,视频导出代码如下:
/// 视频相关 extension ViewController{ //导出 func videoExportSession(asset:AVAsset){ // presetNames 本视频支持导出的格式 AVAssetExportPresetPassthrough 为模拟器支持格式 let presetNames = AVAssetExportSession.exportPresets(compatibleWith: asset) //设置 AVAssetExportPreset640x480等可选参数,会导致视频压缩,但还需研究VideoTool,进一步做压缩处理 videoExport = AVAssetExportSession.init(asset: asset, presetName: presetNames.first ?? AVAssetExportPresetPassthrough) videoExport?.outputURL = getVideoExportURL() videoExport?.outputFileType = AVFileType.mp4 ///如果想要裁剪 设置相关参数 // videoExport?.timeRange = getTimeRange(asset:asset) videoExport?.exportAsynchronously(completionHandler: {[weak self]in print(self?.audioExport?.error) }) } //获取url func getVideoExportURL()->URL{ let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileName = "Quinn_export" + ".mp4" let url = path.appendingPathComponent(fileName) if FileManager.default.fileExists(atPath: url.path){ try? FileManager.default.removeItem(at: url) } print("quinn",url) return url } }