帶緩存功能的視頻播放器

世界上早就有一些優(yōu)秀的 app 視頻播放器,如優(yōu)酷、愛奇藝等,能續(xù)播和下載視頻。想用用不了,于是琢磨著自己實現(xiàn)一款類似功能的播放器。僅僅做一款不帶緩存功能播放器,使用 AVPlayerViewController 即可滿足,如若還要自定義界面,使用 AVFoundation 也是分分鐘的事,想做緩存功能,由于系統(tǒng)庫沒有直接支持,則需要翻倍工作。

方案探索

  • FFmpeg

功能強大,能滿足復雜需求,也意味著復雜,需要掌握視頻編解碼知識以及和 C 語言打交道。

  • HttpServer

在應用內(nèi)搭建一個 HttpServer,Server 再請求視頻資源。說白了是一個 agent,雖說比 FFmpeg 簡單些,可是工作量也是不小。

  • ResourceLoader

AVURLAsset 有個 AVAssetResourceLoaderDelegate,是資源加載的 delgate,輕量級,適用。關于該方案,可以參考這篇博文,這博文附帶的 demo 有坑(少了一行代碼)。

既然選好了方案,最好是了解一下相關背景知識,包括 AVFoundation、視頻斷點下載的最佳實現(xiàn)方式以及加載視頻。

AVFoundation

AVFoundation stack on iOS
AVFoundation stack on iOS

AVFoundation 是 iOS 自帶的庫,從圖可以看出,支持播放音樂、視頻和動畫效果。往細里看,關注 AVAsset、AVPlayerItem、AVPlayer、AVPlayerLayer

  • AVAsset
    一般來說,更加常用的是其子類 AVURLAsset,也可以自定義 AV****Asset。該類管理音視頻軌道、格式類型,加載視頻等等。
  • AVPlayerItem
    正如其名,代表著控制播放,包括播放暫停、快進快退等。總得來說,管理視頻播放的狀態(tài)。
  • AVPlayer
    player 負責解碼視頻,可以設置播放速率,可以控制播放暫停,快進快退等,建議把這個職責交給 AVPlayerItem。
  • AVPlayerLayer
    layer 負責渲染視頻,如果不設置,只播放語音。
    //Demo
    let videoURL = NSURL(string: "your video url here")!
    let videoAsset = AVURLAsset(URL: videoURL)
    let playerItem = AVPlayerItem(asset: videoAsset)
    let player = AVPlayer(playerItem: playerItem)
    let layer = AVPlayerLayer(player: player)
    // add layer to your view

視頻斷點下載

斷點下載方案有有幾套,需要了解各套方案,從而得出最佳方式。

  • NSURLSessionDownloadTask
    通過 NSURLSession 生成 Task,執(zhí)行下載任務。中途可以取消下載,只需要保存上下文,即可恢復下載任務。
  • Stream
    使用文件流來完成下載任務,在配置的時候跳過部分字節(jié),也算是簡單的一種方案。
  • Http 頭的 Range 請求頭
    在構(gòu)造 Request 時設置Range即可從某字節(jié)開始下載資源。比起前面兩種方法,不用生產(chǎn) Task,不用打開關閉流,更加方便簡單,也不需要存著下載進度,繼續(xù)下載只需要讀取文件,取得長度,設置 Range 即可,因此使用 Range 是最佳斷點下在發(fā)方式。

Range 請求頭

發(fā)起一個 Http 請求后,會收到返回,一般來說有以下內(nèi)容。
HTTP/1.0 200 OK
Content-Type: image/png
Content-Length: 36907
Connection: keep-alive
Server: nginx
Accept-Ranges: bytes
看到Accept-Ranges: bytes,表明服務器支持Range 請求,支持的單位是字節(jié);如果Accept-Ranges: none,表明服務器不支持,要用其他方案。值得開心的是,大部分 web 服Range頭域可以請求實體的一個或者多個子范圍。
設置 Range 的值也是非常簡單,key 是 Range,value 是 bytes=XXX,其中 bytes=0-499表示頭500個字節(jié),bytes=-500表示最后500字節(jié),bytes=2000-表示第2000之后的所有字節(jié),同時也可以指定多個范圍,如 bytes=500-1000,1200-1800。

ResourceLoader

使用 AVFoundation 播放音視頻,給AVURLAsset的屬性resourceLoader 指定 delegate 后,在資源的 URL 不能被系統(tǒng)識別時可以自定義視頻加載,如 Lemur://www.lemur.work/player.mov
let offset: UInt64 = xxx
let url = NSURL(string: urlString)!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let request = NSMutableURLRequest(URL: url)
request.setValue("bytes=(offset)-", forHTTPHeaderField: "Range")
let task = session.dataTaskWithRequest(request)
task.resume()
接下來重點關注 AVAssetResourceLoaderDelegate 的實現(xiàn)。

AVAssetResourceLoaderDelegate

該 delegate 是連接視頻播放和視頻斷點下載的橋梁。
optional func resourceLoader(
_ resourceLoader: AVAssetResourceLoader,
shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest
) -> Bool
當視頻播放器要加載視頻,通過這個方法發(fā)起一個請求,只要給請求提供返回,就實現(xiàn)了視頻播放。該接口會被調(diào)用多次,請求不同片段的視頻數(shù)據(jù),應當保存這些請求,在請求的數(shù)據(jù)全部響應完畢才銷毀該請求。
optional func resourceLoader(
_ resourceLoader:AVAssetResourceLoader,
didCancelLoadingRequest loadingRequest: AVAssetResourceLoadingRequest
)
當視頻播放器要取消請求時,相應的,也應該停止下載這部分數(shù)據(jù)。通常在拖拽視頻進度時調(diào)這方法。
optional func resourceLoader(
_ resourceLoader: AVAssetResourceLoader,
shouldWaitForRenewalOfRequestedResource renewalRequest: AVAssetResourceRenewalRequest
) -> Bool
當視頻播放器播放新的視頻時,需要把之前發(fā)起的請求全部請求,并發(fā)起新的視頻請求。

整套方案

  1. 搭好視頻播放器 UI
    提醒的一點是監(jiān)聽播放器的狀態(tài),如視頻可以開始播放等等。
  2. 斷點下載
    下載的數(shù)據(jù)保存在文件系統(tǒng),用 URL 的 MD5 后值為文件名,下次再下載時檢查是否已經(jīng)下載過,并讀取進度,在向服務器發(fā)起下載請求。
  3. ResourceLoader
    使用自定義的 scheme 播放視頻,保存所有的請求,并在下載數(shù)據(jù)后響應請求,保證每個請求都有合適的響應。

寫在最后

當初決定寫一個帶緩存功能的播放器時,覺得是困難的,在那之前,沒有學習過視頻相關的知識。既然決定要做了,開始翻閱大量資料,感觸最深的是,逐個擊破。將任務分解成多個小任務,每天只專注于一個,很快就完成任務。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容