AVFoundation系列学习第一篇: AVAsset的使用方法
AVAsset 是AVFoundation的操作模型,其中包含音频,视频,字幕,元数据。
GPUImage系列专栏
参考:AVFoundation Programming Guide
本文将从以下几个方面介绍AVAsset
Demo 地址
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
}
}