AVFoundation系列一: AVAsset的使用方法

版权声明:转载请标注原文地址。邮箱[email protected] https://blog.csdn.net/Xoxo_x/article/details/83317965

AVAsset 是AVFoundation的操作模型,其中包含音频,视频,字幕,元数据。

GPUImage系列专栏
参考:AVFoundation Programming Guide

AVFoundation系列五:关于音视频的导出
AVFoundation系列四:如何配置一个合格的Camera
AVFoundation系列三:音视频编辑
AVFoundation系列二:用AVPlayer播放视频

本文将从以下几个方面介绍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可以一次性异步加载多个
videoCompositionappliesPreferredTrackTransform冲突,会导致彼此失效

七、通过一个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
    }

其中:videoExportaudioExportAVAssetExportSession,在接下来的文章会介绍到。
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
   }
   
}

猜你喜欢

转载自blog.csdn.net/Xoxo_x/article/details/83317965