如何建立一個媒體回應更即時的 App
這篇文章想跟你分享:
在 App 開發的過程中,許多畫面中都需要透過網路取得媒體資源,此時若使用者開啟 App 而媒體資源尚未就緒的話,就會造成畫面呈現不如預期。這篇文章整理了 iOS App 開發時,可以使用的一系列
AVFoundation
新引入的異步處理 API 及用法。
為了節省使用者的等待時間,在開發 iOS App 可以注意以下幾點,減少媒體回應的時間:
- 使用 WWDC22 發表的一系列新 API,異步加載網路內容
- 設定影片縮圖的寬限範圍
- 避免不必要的 Cache,減少載入資源的時間
【 參考資料:https://developer.apple.com/videos/play/wwdc2022/110379/】
使用 AVAssetImageGenerator
產生影片縮圖
【 API 】
func image(at time: CMTime) async throws -> (image: CGImage, actualTime: CMTime)
【 使用方法 】
上圖圈起來的 .image(at: time)
會回傳 CGImage
及 CMTime
的 Tuple,分別為影片縮圖,以及產生縮圖的時間點。
設定取得縮圖的時間寬限值
一般影片中有些時間點的影格需要由前後的影格才能計算出來,因此需要花費額外計算時間。如下圖,若想取得黃色標示 Time
時間點的影格 P
,則需要額外計算。此時可透過設定時間寬限值 requestedTimeTolerance
,將附近的 I
影格當作縮圖,減少縮圖取得時間
可透過 AVAssetImageGenerator
的屬性:.requestedTimeToleranceBefore
和 .requestedTimeToleranceAfter
,實作時間的寬限值。
當寬限值設為 0 時:
當寬限值非 0 時:
使用 AVAssetImageGenerator
產生多個縮圖
【 API 】
func images(for times: [CMTime]) -> AVAssetImageGenerator.Images
【 使用方法 】
上圖新的方法不僅提供異步處理,更在寫法上進行大量改良。
在過去需要產生多個縮圖需如下圖:
由新、舊 API 的使用方法可以發現以下差異:
- 可直接放
CMTimes
參數,不需再轉成NSValue
。 result
是 Async Sequence。- 成功時的回傳值,除了圖片,還有該圖片位於影片的確切時間點。
- 失敗時會回傳詳細 Error Message。
- 如果只需要縮圖,不需要其他資訊的話,可以直接使用
result.image
取得:
使用 insertTimeRange
異步插入 AVAsset
【 API 】
func insertTimeRange(
_ timeRange: CMTimeRange,
of asset: AVAsset,
at startTime: CMTime,
) async throws
【 使用方法 】
過去沒有新 API 時,要插入 AVMutableComposition
前,需要先加載原本的AVAsset
,才能做成一個新的composition
:
異步製作 AVVideoComposition
【 API 】
let videoComposition = try await AVVideoComposition.videoComposition(withPropertiesOf: asset)
【 使用方法 】
過去沒有新 API 時,會需要先把 AVAsset
加載完畢,才能製作 AVVideoComposition
:
監看 AVAsset
資訊的異步方法
【 API 】
let (duration, tracks) = try await asset.load(.duration, .track)
【 使用方法】
可以看到新 API 的改良:
- 不再用字串當 key,避免打字錯誤而找不到值的情形。
- 只回傳已經加載完的屬性,避免取得尚未加載完畢的屬性。
並非所有狀況都需要使用異步的方法
如果 AVAsset
的資訊已經存在記憶體中,便不需要進行異步處理取得。
假設目標要做一個新的 AVMutableComposition
,該物件的前半段是VideoTrack1
,後半段是 VideoTrack2
。因為VideoTrack1
與VideoTrack2
都已存在記憶體,新的 AVMutableComposition
都指向它們,因此可以用同步的方法在記憶體中查找資訊。
避免不必要的 Cache
【 API 】
convenience init(url URL: URL)
【 使用方法 】
當使用 URL
製作 AVAsset
時,如果URL
為網路位置的話,會需要在 App 內建立 Cache,當儲存到達一定容量時才會播放。相反的,如果 URL
在本地端就儲存 Cache。然而有些情況,系統無法自動辨別是否為網路位置。例如:存取專案中的 MP4 原始檔時,需要使用 AVAssetResourceLoader
,又因為它只有加載沒有讀取的功能,所以會像存取網路資源一樣,當 Cache 儲存夠了之後才播放。
現在有新的 .entireLengthAvailableOnDemand
可以設置,當檔案位於本地端時,可透過此 Flag 控制避免載入不必要的 Cache。此功能具備以下特點:
- 播放時節省記憶體,因為不需要額外載入快取
- 節省播放前的等待時間,因為不需要等待快取存滿才進行下一步
- 有任何網路相關的執行的話,都可能影響播放行為,因此當檔案完全在本地端才可以使用此 Flag
小結
透過新的 API 可以做到異步加載網路內容、設影片縮圖取得時間寬限範圍、避免不必要快取,一切都是為了要節省使用者等待的時間,開發出媒體回應更即時的 App。如果對更多細節有興趣的話,歡迎到下方連結研究更多,也歡迎有任何問題可以留言或者寄 Email 給我們!我們下篇文章見!
資料來源: