AVFoundation 获取视频帧图片以及相关信息

AVFoundation框架是iOS平台多媒体的主要框架之一,它囊括了四个方面的内容:

  • 音视频播放以及编辑
  • 利用麦克风和摄像头进行数据采集,录制成音频或视频
  • 音频处理
  • 将文字转换成音频

本篇主要想记录一下音视频获取信息以及学习一下关于Asset的相关知识。

基本信息获取

从工程中读取视频文件内容:
官方提供了两个种读取的方法分别是有添加参数的和直接添加
直接读取:

let   path = Bundle.main.path(forResource: "IMG_2735", ofType: "mp4")
let url = URL.init(fileURLWithPath: path)
let mainAsset = AVAsset(url: url)

通过添加参数来读取,添加参数时,需要知道以下参数的意义:

  • AVURLAssetPreferPreciseDurationAndTimingKey
    当前是否支持精确的持续时间以及随机访问任何时间点内容
  • AVURLAssetReferenceRestrictionsKey
    是否支持涉及外部引用资源解析,因为存在某些媒体文件是引用了外部的资源的,如果这里限制了,那就会导致这类文件不能正确读取资源
  • AVURLAssetHTTPCookiesKey
    包含一个HTTP cookies的资源访问请求,在默认的情况下,AVURLAsset只有一个缓存的Cookies,这个选项可以帮助你直接使用当前Cookies来进行访问资源,以提速访问
let   path = Bundle.main.path(forResource: "IMG_2735", ofType: "mp4")
let url = URL.init(fileURLWithPath: path)
let options = [AVURLAssetAllowsCellularAccessKey:false,AVURLAssetPreferPreciseDurationAndTimingKey:true]
let mainAsset = AVURLAsset.init(url: url, options: options)

可以读取的基础信息如下:

        let d = CMTimeGetSeconds(mainAsset.duration)
        let p = mainAsset.providesPreciseDurationAndTiming
        print("时长:\(d),可用时间准确性:\(p) \(mainAsset.duration.timescale)")
        
        let formatter = DateFormatter.init()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let cc = mainAsset.creationDate
        let createDate = formatter.string(from: cc?.dateValue ?? Date.init())
        print("创建时间:\(createDate)")
       
        let rate = mainAsset.preferredRate //速率
        print("rate:\(rate)")
        
        let volume = mainAsset.preferredVolume //音量
        print("volume:\(volume)")
        
        let canPlay = mainAsset.isPlayable //是否支持播放
        print("isPlayable:\(canPlay)")
        
        let export = mainAsset.isExportable //是否支持导出
        print("exportable:\(export)")
        
        let reabable = mainAsset.isReadable //是否可读取内容
        print("readable:\(reabable)")
        
        let composable = mainAsset.isComposable //是否可合成
        print("composable:\(composable)")
        
        let hasProtect = mainAsset.hasProtectedContent //是否受保护
        print("hasProtect:\(hasProtect)")
        
        let compatible = mainAsset.isCompatibleWithSavedPhotosAlbum//是否可以写入相册
        print("是否可以写入相册:\(compatible)")

AVAsset里面包含了一个或多个AVAssetTrack可以通过下面几个方法来获取对应的音视频轨道:

    /**
      @property     tracks
      @abstract     获取所有的音视频轨道
    */
    open var tracks: [AVAssetTrack] { get }

    
    /**
      @method       trackWithTrackID:
      @abstract     根据trackID来获取对应的音视频轨道.
      @param        trackID
      @result       返回一个或者零个音视频轨道
    */
    open func track(withTrackID trackID: CMPersistentTrackID) -> AVAssetTrack?

    
    /**
      @method       tracksWithMediaType:
      @abstract 根据media type来获取对应的音/视频轨道组
      @param        mediaType
                     参考 AVMediaFormat.h 类
      @result       音/视频轨道组
    */
    open func tracks(withMediaType mediaType: AVMediaType) -> [AVAssetTrack]

    
    /**
      @method       tracksWithMediaCharacteristic:
      @abstract       根据特殊的描述符来获取对应的音/视频轨道组.
      @param        mediaCharacteristic
                    参考 AVMediaFormat.h 类
      @result       音/视频轨道组
    */
    open func tracks(withMediaCharacteristic mediaCharacteristic: AVMediaCharacteristic) -> [AVAssetTrack]

另外也可以遍历AVAssetTrack的一些基本参数:

    for track in mainAsset.tracks {
         print("trackID:\(track.trackID)")
         print("mediaType:\(track.mediaType.rawValue)")
         print("nominalFrameRate:\(track.nominalFrameRate)")
         print("minFrameDuration:\(track.minFrameDuration.seconds)")
         print("formatDescription:\(track.formatDescriptions)")
    }

获取某一时刻的视频帧

这里可以分为两部分,一个是根据某个间隔获取视频的每一个时刻的视频帧,这里用到了一次获取多个视频帧的方法:

       let imgc = mainAsset.tracks(withMediaType: .video).count
        var track:AVAssetTrack!
        if imgc == 1 {
            track = mainAsset.tracks(withMediaType: .video).first
        }
        let gem = AVAssetImageGenerator.init(asset: mainAsset)
        //缩略图的宽高
        gem.maximumSize = CGSize.init(width: 128, height: 72)
        gem.apertureMode = AVAssetImageGenerator.ApertureMode.cleanAperture
        var array:[NSValue]! = []
        let d = mainAsset.duration.seconds
        let all = d * Double(track.nominalFrameRate)
        
        let numberGet = Int(d*5)//每秒里面取5帧
        let scan = Int(all)/numberGet //取帧间隔
        for item in 0..<numberGet {
            let value = item*scan
            let ctvalue = CMTime.init(value: CMTimeValue(value), timescale: CMTimeScale(track!.nominalFrameRate)) as NSValue
            array.append(ctvalue)
        }
        //这里是获取多个视频帧所用的异步方法,将更高效的去获取视频帧内容而不堵塞UI线程
        gem.generateCGImagesAsynchronously(forTimes: array) { (time1, cgimg, time2, result, error) in
            if let _cgimg = cgimg {
                let img = UIImage.init(cgImage: _cgimg)
               //把获取到的图片加入一个数组内
                self.imagesArray.append(img)
                DispatchQueue.main.async {
                  print("get a img by asset\(img)")
                }
            }
        }

另一个则是相对简单的,直接通过请求某个时间点的时刻来获取对应视频帧信息的帧信息内容:
这里我实现了一个slider,通过这个slider的value值变化来取对应的视频帧

///这里是slider change 的内容
  @objc func changeAction(){
        print("slider.value:\(slider.value)")
       
        let imgc = mainAsset.tracks(withMediaType: .video).count
        var track:AVAssetTrack!
        if imgc == 1 {
            track = mainAsset.tracks(withMediaType: .video).first
        }
        let v = slider.value * Float(mainAsset.duration.seconds) * track.nominalFrameRate
        let t = CMTime.init(value: CMTimeValue(v), timescale: CMTimeScale(track.nominalFrameRate))
        imageGen.cancelAllCGImageGeneration()
        guard let img = try?self.imageGen.copyCGImage(at: t, actualTime: nil) else {
            return
        }
        print("get a img by asset")
        self.thumbImageView.image = UIImage.init(cgImage: img)
}

在获取帧信息的时候,频繁出现的一个东西:CMTime时间值,这里解释一下高浩浩浩浩浩浩

后面或有更多AVFoundation的相关知识记录在此,欢迎关注。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354