iOS MP4轉(zhuǎn)CGImageRef數(shù)組循環(huán)播放(AVAssetReader+AVAssetReaderTrackOutput)

開始實現(xiàn)之前瓜晤,先介紹一下 AVFoundation用到的類仑嗅!

  1. AVAsset
    一個統(tǒng)一多媒體文件類豆拨,不局限于音頻視頻浦旱,我們就可以通過這個類獲取到它的多媒體文件各種屬性比如類型時長每秒幀數(shù)等等

  2. AVURLAsset
    AVAsset子類宇色,用來本地或者遠(yuǎn)程創(chuàng)建AVAsset

  3. AVAssetTrack
    多媒體文件軌道:AVMediaTypeVideo/AVMediaTypeAudio/AVMediaTypeText等等
    一般來說視頻有兩個軌道,一個是播放聲音一個播放畫面颁湖,所以說我們需要取播放畫面的軌道的話:

 self.assetTrack = self.asset?.tracksWithMediaType(AVMediaTypeVideo)[0]
  1. AVAssetReader
    我們可以通過這個類獲取asset的媒體數(shù)據(jù)(會拋出異常宣蠕,所以放在do-catch里面或者直接try!)
self.assetReader = try AVAssetReader(asset: self.asset!)
  1. AVAssetReaderTrackOutput
    能從AVAssetReader對象中讀取同一類型媒體數(shù)據(jù)的樣品的集合,大概就是視頻輸出的意思甥捺,從AVAssetTrack獲取到某一通道的多媒體文件抢蚀,然后通過AVAssetReader.startReading()方法開始獲取視頻的每一幀。
    關(guān)于AVAssetReaderTrackOutput采樣輸出屬性:
let m_pixelFormatType = kCVPixelFormatType_32BGRA //iOS在內(nèi)部進(jìn)行YUV至BGRA格式轉(zhuǎn)換
outputSettings:[String(kCVPixelBufferPixelFormatTypeKey) : Int(m_pixelFormatType)]

這個直接使用網(wǎng)上的這個镰禾,但是看到stackoverflow有說皿曲,除非需要特別的format,不然可以outputSettings為nil吴侦,說是AVFoundation會選擇最優(yōu)效率的format
Query for optimal pixel format when capturing video on iOS?

  1. while循環(huán)處理視頻幀樣本
assetReader?.startReading()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // 確保大于0幀
    while self.assetReader?.status == .Reading  && self.assetTrack?.nominalFrameRate > 0 {
        // 讀取視頻
        autoreleasepool({
            let videoBuffer = self.videoReaderOutput?.copyNextSampleBuffer()
            if videoBuffer != nil {
                let cgImage = self.cgImageFromSampleBufferRef(videoBuffer!)
                guard self.delegate != nil else {
                    print("代理沒設(shè)置")
                    return
                }
                self.delegate?.movieDecoderCallBack(self, cgImage: cgImage.takeRetainedValue())
                //                        cgImage.release()
                // 根據(jù)需要休眠一段時間谷饿;比如上層播放視頻時每幀之間是有間隔的
                NSThread.sleepForTimeInterval(0.001)
            }
        })
    }
    // 完成回調(diào)
    guard self.delegate != nil else {
        print("代理沒設(shè)置")
        return
    }
    self.delegate?.movieDecoderFinishCallBack(self)
}
  1. CMSampleBuffer--> CGImageRef的方法我是在Objective-C里面處理的,因為swift的autoreleasepool里面不能return
-(CGImageRef)cgImageFromSampleBufferRef:(CMSampleBufferRef)sampleBufferRef {
     @autoreleasepool {
        // 為媒體數(shù)據(jù)設(shè)置一個CMSampleBufferRef
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBufferRef);
        // 鎖定 pixel buffer 的基地址妈倔,保證在內(nèi)存中可用
        CVPixelBufferLockBaseAddress(imageBuffer, 0);
        // 得到 pixel buffer 的基地址
        void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
        // 得到 pixel buffer 的行字節(jié)數(shù)
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
        // 得到 pixel buffer 的寬和高
        size_t width = CVPixelBufferGetWidth(imageBuffer);
        size_t height = CVPixelBufferGetHeight(imageBuffer);
        // 創(chuàng)建一個依賴于設(shè)備的 RGB 顏色空間
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        // 用抽樣緩存的數(shù)據(jù)創(chuàng)建一個位圖格式的圖形上下文(graphic context)對象
        CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
        //根據(jù)這個位圖 context 中的像素創(chuàng)建一個 Quartz image 對象
        CGImageRef quartzImage = CGBitmapContextCreateImage(context);
        // 解鎖 pixel buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
        // CVPixelBufferRelease(imageBuffer);
        // Free up the context and color space
        CGContextRelease(context);
        CGColorSpaceRelease(colorSpace);
        return quartzImage;
     }
}
  1. CMSampleBufferRef
    AVAssetReaderTrackOutput.copyNextSampleBuffer()能同步copy下一個CMSampleBuffer博投。每一個CMSampleBuffer在緩沖區(qū)有一個單獨的樣本幀(視頻樣本幀)相關(guān)聯(lián)。

  2. CVImageBufferRef
    有了CMSampleBuffer盯蝴,我們就可以在圖像緩沖區(qū)創(chuàng)建CVImageBufferRef(Pixel buffers 像素緩沖區(qū)屬于CVImageBufferRef派生類)毅哗,有了這個圖像緩沖區(qū),我們就可以對它做像素級別的操作捧挺。

  3. CGBitmapContextCreate創(chuàng)建位圖對象

  • data: 指向要渲染的繪制內(nèi)存的地址虑绵。這個內(nèi)存塊的大小至少是(bytesPerRow*height)個字節(jié)
  • width: bitmap的寬度,單位為像素
  • height: bitmap的高度,單位為像素
  • bitsPerComponent: 內(nèi)存中像素的每個組件的位數(shù).例如,對于32位像素格式和RGB 顏色空間闽烙,你應(yīng)該將這個值設(shè)為8.
  • bytesPerRow: bitmap的每一行在內(nèi)存所占的比特數(shù)
  • colorspace: bitmap上下文使用的顏色空間翅睛。
  • bitmapInfo: 指定bitmap是否包含alpha通道声搁,像素中alpha通道的相對位置,像素組件是整形還是浮點型等信息的字符串捕发。

第六步和第七步都使用了autoreleasepool為了及時釋放掉內(nèi)存疏旨,網(wǎng)上看到很多網(wǎng)友說用這個方法都內(nèi)存爆掉了,我加了這兩個之后雖然還是在Leaks看到有內(nèi)存泄露扎酷,但是沒有出現(xiàn)過內(nèi)存爆掉的情況

    @nonobjc private var images: Array<CGImageRef> = []
    var animation: CAKeyframeAnimation?

    override func prepareForReuse() {
        animation = nil
        images.removeAll()
        videoView.layer.removeAllAnimations()
    }

    func movieDecoderFinishCallBack(movieDecoder: MovieDecoder) {
        dispatch_async(dispatch_get_main_queue()) {    
            let videoPath = "\(CacheDirectory)/\(self.cellModel.mp4Id).mp4"
            let fileUrl = NSURL(fileURLWithPath: videoPath)
            let asset = AVURLAsset(URL: fileUrl)
            self.animation = CAKeyframeAnimation.init(keyPath: "contents")
            self.animation!.duration = Double(asset.duration.value)/Double(asset.duration.timescale);
            self.animation!.values = self.images;
            self.animation!.repeatCount = MAXFLOAT;
            self.startAnimation()
            // 清除緩存
            self.images.removeAll()
        }
    }

除了在movieDecoderFinishCallBack回調(diào)執(zhí)行self.images.removeAll()之外檐涝,我還在prepareForReuse方法里面將animation置成nil,然后發(fā)現(xiàn)無論怎么滾動法挨,內(nèi)存都穩(wěn)定在50M以下谁榜,比以前動輒幾百M的好多了,不會出現(xiàn)crash的情況凡纳,不過依然會出現(xiàn)內(nèi)存泄露的問題窃植。


最后在網(wǎng)上找到一個感覺像是處理內(nèi)存泄露的方法,具體我沒考究荐糜,不過應(yīng)該可以提供些許思路:
把一個視頻拆分成多個AVAssetTrack撕瞧,這樣做的原因是因為,使用AVAssetReader讀取每一幀SampleBuffer的數(shù)據(jù)是需要把數(shù)據(jù)加載到內(nèi)存里面去的狞尔,如果直接把整個視頻的SampleBuffer加載到內(nèi)存丛版,會造成閃退
鏈接:GitHub:KayWong/VideoReverse 使用AVFoundation實現(xiàn)視頻倒序

以上代碼參考鏈接:
IOS 微信聊天發(fā)送小視頻的秘密(AVAssetReader+AVAssetReaderTrackOutput播放視頻)

國慶期間我會寫一個demo出來,現(xiàn)在暫時沒時間偏序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末页畦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子研儒,更是在濱河造成了極大的恐慌豫缨,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件端朵,死亡現(xiàn)場離奇詭異好芭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)冲呢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門舍败,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人敬拓,你說我怎么就攤上這事邻薯。” “怎么了乘凸?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵厕诡,是天一觀的道長。 經(jīng)常有香客問我营勤,道長灵嫌,這世上最難降的妖魔是什么壹罚? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮寿羞,結(jié)果婚禮上猖凛,老公的妹妹穿的比我還像新娘。我一直安慰自己稠曼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布客年。 她就那樣靜靜地躺著霞幅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪量瓜。 梳的紋絲不亂的頭發(fā)上司恳,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音绍傲,去河邊找鬼扔傅。 笑死,一個胖子當(dāng)著我的面吹牛烫饼,可吹牛的內(nèi)容都是我干的猎塞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼杠纵,長吁一口氣:“原來是場噩夢啊……” “哼荠耽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起比藻,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤铝量,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后银亲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慢叨,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年务蝠,在試婚紗的時候發(fā)現(xiàn)自己被綠了拍谐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡馏段,死狀恐怖赠尾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毅弧,我是刑警寧澤气嫁,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站够坐,受9級特大地震影響寸宵,放射性物質(zhì)發(fā)生泄漏崖面。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一梯影、第九天 我趴在偏房一處隱蔽的房頂上張望巫员。 院中可真熱鬧,春花似錦甲棍、人聲如沸简识。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽七扰。三九已至,卻和暖如春陪白,著一層夾襖步出監(jiān)牢的瞬間颈走,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工咱士, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留立由,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓序厉,卻偏偏與公主長得像锐膜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弛房,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開發(fā)出絢麗的界面效果枣耀,一方面得益于成功系統(tǒng)的設(shè)計,另一方面得益...
    韓七夏閱讀 2,715評論 2 10
  • 本篇文章是基于谷歌有關(guān)Graphic的一篇概覽文章的翻譯:http://source.android.com/de...
    lee_3do閱讀 7,109評論 2 21
  • 轉(zhuǎn)載請帶上出處, 謝謝. 一個 Graphics Context 代表一個繪制目標(biāo), 它包含繪制系統(tǒng)用于完成繪制指...
    Falme丶閱讀 1,784評論 0 2
  • 難得在夜深人靜之時心也是靜的庭再,無執(zhí)于早起捞奕,便莫名的在臺燈前思考起這個問題來。 “人是什么”是哲學(xué)拄轻、宗教颅围、科學(xué)都關(guān)注...
    一只學(xué)術(shù)小狐貍閱讀 1,132評論 0 3
  • 真的有好久好久不寫東西了啊。最近比較懶惰恨搓。有趣的事情發(fā)生了不少院促,但就是懶得記下來,明明并不是沒有時間~ ...
    畫樓西閱讀 170評論 0 1