覽的完整原理)
SAVideoRangeSlider源碼剖析用AVAssetImageGenerator高效生成視頻縮略圖預(yù)覽的完整原理【免費(fèi)下載鏈接】SAVideoRangeSlideriOS Video trim control項(xiàng)目地址: https://gitcode.com/gh_mirrors/sa/SAVideoRangeSliderSAVideoRangeSlider 是一款面向 iOS 的開源視頻裁剪Video Trim控件用 iMovie 風(fēng)格的左右拖動(dòng)手柄實(shí)現(xiàn)視頻片段截取。本文完整剖析其源碼中最核心的部分——如何利用 AVAssetImageGenerator 高效生成視頻縮略圖預(yù)覽帶你從像素位置到視頻時(shí)間徹底理解其實(shí)現(xiàn)原理。一、SAVideoRangeSlider 視頻裁剪控件是什么想象一下 iMovie 里的剪輯時(shí)間軸底部有一排連續(xù)的視頻畫面兩端各有一個(gè)手柄拖動(dòng)手柄即可選定要保留的片段上方還會(huì)彈出氣泡顯示00:12 - 00:35這樣的時(shí)間區(qū)間。SAVideoRangeSlider 就是這個(gè)體驗(yàn)的輕量實(shí)現(xiàn)。它的目錄結(jié)構(gòu)非常清晰目錄作用SAVideoRangeSlider/核心控件源碼約 6 個(gè)文件輕量無(wú)依賴SAVideoRangeSliderExample/完整示例工程含裁剪導(dǎo)出演示PaintCodeFiles/5 套配色主題Yellow / Gray / Green / Purple / Star核心邏輯集中在 SAVideoRangeSlider.m對(duì)外接口定義在 SAVideoRangeSlider.h只有兩個(gè) Delegate 回調(diào)和 minGap / maxGap限制裁剪最短/最長(zhǎng)時(shí)長(zhǎng)單位秒等少量屬性新手上手門檻很低。?二、為什么視頻縮略圖預(yù)覽這么難圖片天生就是一幀但視頻文件里并不存放現(xiàn)成的圖片序列。要讓時(shí)間軸上出現(xiàn)一排看得見的畫面必須解碼按指定時(shí)間點(diǎn)從視頻流中抽出畫面CPU 密集型操作映射把控件上的像素位置換算成視頻的秒數(shù)控制成本縮略圖太多會(huì)卡頓太少則看不清這正是 AVAssetImageGenerator 出場(chǎng)的地方——它是 AVFoundation 框架中專為按時(shí)間抽取視頻幀設(shè)計(jì)的工具類。三、AVAssetImageGenerator 生成視頻縮略圖的 5 個(gè)關(guān)鍵 API在進(jìn)入源碼前先建立概念地圖API作用一句話理解AVURLAsset包裝視頻文件打開視頻AVAssetImageGenerator抽幀器把視頻變成可拍照的機(jī)器maximumSize限制輸出尺寸別解碼超大的幀省 CPUcopyCGImageAtTime:actualTime:error:同步抽取單幀要哪一幀給哪一幀generateCGImagesAsynchronouslyForTimes:異步批量抽幀給一批時(shí)間點(diǎn)回來(lái)一批圖配套的時(shí)間類型是CMTimeCoreMedia 的時(shí)間表示換算用CMTimeMakeWithSeconds和CMTimeGetSeconds完成。四、核心源碼解讀getMovieFrame 的 6 步抽幀流程全部縮略圖邏輯都在這一個(gè)方法里SAVideoRangeSlider.m#L329-L485。按執(zhí)行順序拆解第 1 步打開視頻創(chuàng)建抽幀器用AVURLAsset包裝傳入的videoUrl再通過(guò)類方法一行代碼創(chuàng)建抽幀器見 SAVideoRangeSlider.m#L331-L332。第 2 步按屏幕密度設(shè)置輸出尺寸判斷是否 Retina 屏是則把maximumSize設(shè)為背景寬高的 2 倍否則 1 倍SAVideoRangeSlider.m#L334-L338。這是第一個(gè)性能關(guān)鍵——解碼尺寸貼著顯示尺寸走避免解碼出全屏大圖再縮小。第 3 步同步生成第一幀用kCMTimeZero在時(shí)間 0 處同步抽取首幀并立即顯示SAVideoRangeSlider.m#L345。用戶體驗(yàn)上打開控件就立刻能看到畫面而不是一空白條。第 4 步計(jì)算縮略圖數(shù)量int picWidth 20; // 每張縮略圖 20pt 寬 int picsCnt ceil(_bgView.frame.size.width / picWidth);縮略圖數(shù)量 軌道寬度 ÷ 單張寬度SAVideoRangeSlider.m#L340-L365。數(shù)量隨寬度自適應(yīng)是控制解碼成本的關(guān)鍵設(shè)計(jì)。第 5 步像素位置 → 視頻時(shí)間 的換算公式CMTime timeFrame CMTimeMakeWithSeconds( _durationSeconds * time4Pic / _bgView.frame.size.width, 600);含義一目了然某張圖放在像素位置time4Pic對(duì)應(yīng)視頻時(shí)間就是總時(shí)長(zhǎng) × 像素位置 ÷ 軌道總寬SAVideoRangeSlider.m#L377。這就是整條縮略圖軌道的比例尺。第 6 步iOS 7 的特殊分支 異步批量抽幀源碼做了一個(gè)系統(tǒng)版本判斷SAVideoRangeSlider.m#L371-L429iOS 7由于generateCGImagesAsynchronouslyForTimes當(dāng)時(shí)存在 Bug改用同步循環(huán)逐幀抽取再用dispatch_async把添加子視圖丟回主線程保證 UI 不崩線程規(guī)則其他版本走正統(tǒng)路線——把所有時(shí)間點(diǎn)打包一次性調(diào)用generateCGImagesAsynchronouslyForTimes:completionHandler:異步批量抽幀SAVideoRangeSlider.m#L443-L484每回調(diào)一幀就鋪一張到軌道上最后統(tǒng)一回主線程添加。五、同步 vs 異步兩種抽幀策略如何配合策略代碼位置優(yōu)點(diǎn)代價(jià)同步單幀copyCGImageAtTime:首幀 L345立等可取首幀秒顯幀多時(shí)阻塞調(diào)用線程異步批量generateCGImages...其余幀 L443后臺(tái)解碼逐幀上屏不卡 UI有回調(diào)時(shí)機(jī)需自行處理順序值得學(xué)習(xí)的組合拳首幀同步保底 其余異步填充再配合兩個(gè)衛(wèi)生動(dòng)作——每張CGImageRef用完立即CGImageRelease防止內(nèi)存泄漏SAVideoRangeSlider.m#L423所有添加 UIImageView都dispatch_async到主隊(duì)列嚴(yán)格遵守 UIKit 主線程規(guī)則。六、像素與時(shí)間的雙向換算拖動(dòng)手柄如何變成秒數(shù)縮略圖軌道負(fù)責(zé)顯示拖動(dòng)手柄負(fù)責(zé)選擇。兩者靠同一把比例尺互轉(zhuǎn)像素 → 秒SAVideoRangeSlider.m#L492-L501 中l(wèi)eftPosition/rightPosition的 getter 把像素值換算成像素 × 總時(shí)長(zhǎng) ÷ 軌道寬拖動(dòng) → 回調(diào)三個(gè)手勢(shì)左手柄、右手柄、中間整體平移在 handleLeftPan: / handleRightPan: / handleCenterPan: 中實(shí)時(shí)修正位置并通過(guò)didChangeLeftPosition:rightPosition:把秒數(shù)推給外界氣泡顯示trimIntervalStr 把秒數(shù)格式化成MM:SS - MM:SS顯示在拖拽氣泡里手勢(shì)結(jié)束觸發(fā)didGestureStateEnded...回調(diào)示例工程此時(shí)拿到startTime / stopTime交給AVAssetExportSession設(shè)置timeRange完成無(wú)損裁剪導(dǎo)出ViewController.m#L112-L163。七、快速上手3 步集成 SAVideoRangeSlider放入源碼把 SAVideoRangeSlider/ 目錄下的 6 個(gè)文件拖進(jìn)項(xiàng)目參見 SAVideoRangeSlider.podspec 的框架依賴QuartzCore、AVFoundation、CoreMedia創(chuàng)建控件一個(gè)構(gòu)造方法initWithFrame:videoUrl:傳入視頻路徑即可自動(dòng)抽幀生成縮略圖軌道ViewController.m#L59-L69監(jiān)聽回調(diào)設(shè)置delegate按需配置minGap/maxGap和氣泡大小即可在回調(diào)里拿到起止秒數(shù)。想要換主題PaintCodeFiles/ 下每套配色如 Yellow/SASliderLeft.m都是 PaintCode 導(dǎo)出的矢量繪制代碼替換手柄視圖即可換膚。八、實(shí)踐建議4 個(gè)容易踩的坑縮略圖數(shù)量寧少勿多——解碼是 CPU 密集操作按寬度 ÷ 20pt這種密度封頂即可務(wù)必設(shè)置maximumSize——貼著顯示尺寸解碼能省下數(shù)倍內(nèi)存與耗時(shí)CF 資源記得釋放——CGImageRef必須CGImageRelease批量場(chǎng)景下泄漏會(huì)非常明顯批量抽幀用異步 API——單幀同步、多幀異步別讓解碼占住主線程??偨Y(jié)SAVideoRangeSlider 用一個(gè) 500 來(lái)行的控件講透了 iOS 視頻裁剪的完整鏈路AVAssetImageGenerator 負(fù)責(zé)把視頻變成圖像素-時(shí)間比例尺負(fù)責(zé)把位置變成秒Delegate 回調(diào)負(fù)責(zé)把結(jié)果交給業(yè)務(wù)。理解了getMovieFrame的 6 步流程和同步/異步的取舍你不僅能讀懂這個(gè)控件也能在自己的 App 里復(fù)刻出同樣絲滑的縮略圖時(shí)間軸?!久赓M(fèi)下載鏈接】SAVideoRangeSlideriOS Video trim control項(xiàng)目地址: https://gitcode.com/gh_mirrors/sa/SAVideoRangeSlider創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考