帶緩存功能的視頻播放器

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

方案探索

  • FFmpeg

功能強(qiáng)大做瞪,能滿足復(fù)雜需求对粪,也意味著復(fù)雜,需要掌握視頻編解碼知識以及和 C 語言打交道装蓬。

  • HttpServer

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

  • ResourceLoader

AVURLAsset 有個 AVAssetResourceLoaderDelegate暗赶,是資源加載的 delgate鄙币,輕量級,適用蹂随。關(guān)于該方案十嘿,可以參考這篇博文,這博文附帶的 demo 有坑(少了一行代碼)。

既然選好了方案岳锁,最好是了解一下相關(guān)背景知識绩衷,包括 AVFoundation、視頻斷點(diǎn)下載的最佳實(shí)現(xiàn)方式以及加載視頻激率。

AVFoundation

AVFoundation stack on iOS
AVFoundation stack on iOS

AVFoundation 是 iOS 自帶的庫咳燕,從圖可以看出,支持播放音樂乒躺、視頻和動畫效果招盲。往細(xì)里看,關(guān)注 AVAsset聪蘸、AVPlayerItem宪肖、AVPlayer表制、AVPlayerLayer

  • AVAsset
    一般來說,更加常用的是其子類 AVURLAsset控乾,也可以自定義 AV****Asset么介。該類管理音視頻軌道、格式類型蜕衡,加載視頻等等壤短。
  • AVPlayerItem
    正如其名,代表著控制播放慨仿,包括播放暫停久脯、快進(jìn)快退等×海總得來說帘撰,管理視頻播放的狀態(tài)。
  • AVPlayer
    player 負(fù)責(zé)解碼視頻万皿,可以設(shè)置播放速率摧找,可以控制播放暫停,快進(jìn)快退等牢硅,建議把這個職責(zé)交給 AVPlayerItem蹬耘。
  • AVPlayerLayer
    layer 負(fù)責(zé)渲染視頻,如果不設(shè)置减余,只播放語音综苔。
    //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

視頻斷點(diǎn)下載

斷點(diǎn)下載方案有有幾套,需要了解各套方案位岔,從而得出最佳方式如筛。

  • NSURLSessionDownloadTask
    通過 NSURLSession 生成 Task,執(zhí)行下載任務(wù)赃承。中途可以取消下載妙黍,只需要保存上下文,即可恢復(fù)下載任務(wù)瞧剖。
  • Stream
    使用文件流來完成下載任務(wù)拭嫁,在配置的時候跳過部分字節(jié),也算是簡單的一種方案抓于。
  • Http 頭的 Range 請求頭
    在構(gòu)造 Request 時設(shè)置Range即可從某字節(jié)開始下載資源做粤。比起前面兩種方法,不用生產(chǎn) Task捉撮,不用打開關(guān)閉流怕品,更加方便簡單,也不需要存著下載進(jìn)度巾遭,繼續(xù)下載只需要讀取文件肉康,取得長度闯估,設(shè)置 Range 即可,因此使用 Range 是最佳斷點(diǎn)下在發(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刚夺,表明服務(wù)器支持Range 請求,支持的單位是字節(jié)末捣;如果Accept-Ranges: none侠姑,表明服務(wù)器不支持,要用其他方案箩做。值得開心的是莽红,大部分 web 服Range頭域可以請求實(shí)體的一個或者多個子范圍。
設(shè)置 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()
接下來重點(diǎn)關(guān)注 AVAssetResourceLoaderDelegate 的實(shí)現(xiàn)观谦。

AVAssetResourceLoaderDelegate

該 delegate 是連接視頻播放和視頻斷點(diǎn)下載的橋梁。
optional func resourceLoader(
_ resourceLoader: AVAssetResourceLoader,
shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest
) -> Bool
當(dāng)視頻播放器要加載視頻桨菜,通過這個方法發(fā)起一個請求豁状,只要給請求提供返回,就實(shí)現(xiàn)了視頻播放倒得。該接口會被調(diào)用多次泻红,請求不同片段的視頻數(shù)據(jù),應(yīng)當(dāng)保存這些請求霞掺,在請求的數(shù)據(jù)全部響應(yīng)完畢才銷毀該請求谊路。
optional func resourceLoader(
_ resourceLoader:AVAssetResourceLoader,
didCancelLoadingRequest loadingRequest: AVAssetResourceLoadingRequest
)
當(dāng)視頻播放器要取消請求時,相應(yīng)的菩彬,也應(yīng)該停止下載這部分?jǐn)?shù)據(jù)缠劝。通常在拖拽視頻進(jìn)度時調(diào)這方法潮梯。
optional func resourceLoader(
_ resourceLoader: AVAssetResourceLoader,
shouldWaitForRenewalOfRequestedResource renewalRequest: AVAssetResourceRenewalRequest
) -> Bool
當(dāng)視頻播放器播放新的視頻時,需要把之前發(fā)起的請求全部請求惨恭,并發(fā)起新的視頻請求酷麦。

整套方案

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

寫在最后

當(dāng)初決定寫一個帶緩存功能的播放器時升酣,覺得是困難的,在那之前态罪,沒有學(xué)習(xí)過視頻相關(guān)的知識噩茄。既然決定要做了,開始翻閱大量資料复颈,感觸最深的是绩聘,逐個擊破。將任務(wù)分解成多個小任務(wù)耗啦,每天只專注于一個凿菩,很快就完成任務(wù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帜讲,一起剝皮案震驚了整個濱河市衅谷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌似将,老刑警劉巖获黔,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異玩郊,居然都是意外死亡肢执,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門译红,熙熙樓的掌柜王于貴愁眉苦臉地迎上來预茄,“玉大人,你說我怎么就攤上這事〕苌拢” “怎么了拙徽?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诗宣。 經(jīng)常有香客問我膘怕,道長,這世上最難降的妖魔是什么召庞? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任岛心,我火速辦了婚禮,結(jié)果婚禮上篮灼,老公的妹妹穿的比我還像新娘忘古。我一直安慰自己,他們只是感情好诅诱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布髓堪。 她就那樣靜靜地躺著,像睡著了一般娘荡。 火紅的嫁衣襯著肌膚如雪干旁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天炮沐,我揣著相機(jī)與錄音争群,去河邊找鬼。 笑死央拖,一個胖子當(dāng)著我的面吹牛祭阀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鲜戒,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抹凳!你這毒婦竟也來了遏餐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤赢底,失蹤者是張志新(化名)和其女友劉穎失都,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幸冻,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粹庞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年坷随,在試婚紗的時候發(fā)現(xiàn)自己被綠了弧腥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡砂蔽,死狀恐怖碑定,靈堂內(nèi)的尸體忽然破棺而出流码,到底是詐尸還是另有隱情又官,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布漫试,位于F島的核電站六敬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驾荣。R本人自食惡果不足惜外构,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望播掷。 院中可真熱鬧典勇,春花似錦、人聲如沸叮趴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眯亦。三九已至伤溉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妻率,已是汗流浹背乱顾。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宫静,地道東北人走净。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像孤里,于是被迫代替她去往敵國和親伏伯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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