視頻播放器介紹文檔
目錄介紹
- 01.該視頻播放器介紹
- 02.視頻播放器功能
- 03.視頻播放器架構說明
- 04.視頻播放器如何使用
- 05.播放器詳細Api文檔
- 06.播放器封裝思路
- 07.播放器示例展示圖
- 08.添加自定義視圖
- 09.視頻播放器優化處理
- 10.播放器問題記錄說明
- 11.性能優化和庫大小
- 12.視頻緩存原理介紹
- 13.查看視頻播放器日誌
- 14.該庫異常code說明
- 15.該庫系列wiki文檔
- 16.版本更新文檔記錄
00.視頻播放器通用框架
- 基礎封裝視頻播放器player,可以在ExoPlayer、MediaPlayer,聲網RTC視頻播放器內核,原生MediaPlayer可以自由切換
- 對於視圖狀態切換和後期維護拓展,避免功能和業務出現耦合。比如需要支持播放器UI高度定製,而不是該lib庫中UI代碼
- 針對視頻播放,音頻播放,播放回放,以及視頻直播的功能。使用簡單,代碼拓展性強,封裝性好,主要是和業務徹底解耦,暴露接口監聽給開發者處理業務具體邏輯
- 該播放器整體架構:播放器內核(自由切換) + 視頻播放器 + 邊播邊緩存 + 高度定製播放器UI視圖層
01.該視頻播放器介紹
1.1 該庫說明
播放器功能 | MediaPlayer | ExoPlayer | IjkPlayer | RTC | TXPlayer |
---|---|---|---|---|---|
UI/Player/業務解耦 | 支持 | 支持 | 支持 | ||
切換視頻播放模式 | 支持 | 支持 | 支持 | ||
視頻無縫切換 | 支持 | 支持 | 支持 | ||
調節播放進度 | 支持 | 支持 | 支持 | ||
網絡環境監聽 | 支持 | 支持 | 支持 | ||
滑動改變亮度/聲音 | 支持 | 支持 | 支持 | ||
設置視頻播放比例 | 支持 | 支持 | 支持 | ||
自由切換視頻內核 | 支持 | 支持 | 支持 | ||
記錄播放位置 | 支持 | 支持 | 支持 | ||
清晰度模式切換 | 支持 | 支持 | 支持 | ||
重力感應自動進入 | 支持 | 支持 | 支持 | ||
鎖定屏幕功能 | 支持 | 支持 | 支持 | ||
倍速播放 | 不支持 | 支持 | 支持 | ||
視頻小窗口播放 | 支持 | 支持 | 支持 | ||
列表小窗口播放 | 支持 | 支持 | 支持 | ||
邊播邊緩存 | 支持 | 支持 | 支持 | ||
同時播放多個視頻 | 支持 | 支持 | 支持 | ||
仿快手預加載 | 支持 | 支持 | 支持 | ||
基於內核無UI | 支持 | 支持 | 支持 | ||
添加彈幕 | 支持 | 支持 | 支持 | ||
全屏顯示電量 | 支持 | 支持 | 支持 |
1.2 該庫功能說明
類型 | 功能說明 |
---|---|
項目結構 | VideoCache緩存lib,VideoKernel視頻內核lib,VideoPlayer視頻UIlib |
內核 | MediaPlayer、ExoPlayer、IjkPlayer,後期接入Rtc和TXPlayer |
協議/格式 | http/https、concat、rtsp、hls、rtmp、file、m3u8、mkv、webm、mp3、mp4等 |
畫面 | 調整顯示比例:默認、16:9、4:3、填充;播放時旋轉畫面角度(0,90,180,270);鏡像旋轉 |
佈局 | 內核和UI分離,和市面GitHub上大多數播放器不一樣,方便定製,通過addView添加 |
播放 | 正常播放,小窗播放,列表播放,仿抖音播放 |
自定義 | 可以自定義添加視頻UI層,可以說UI和Player高度分離,支持自定義渲染層SurfaceView |
02.視頻播放器功能
-
A基礎功能
- A.1.1 能夠自定義視頻加載loading類型,設置視頻標題,設置視頻底部圖片,設置播放時長等基礎功能
- A.1.2 可以切換播放器的視頻播放狀態,播放錯誤,播放未開始,播放開始,播放準備中,正在播放,暫停播放,正在緩衝等等狀態
- A.1.3 可以自由設置播放器的播放模式,比如,正常播放,全屏播放,和小屏幕播放。其中全屏播放支持旋轉屏幕。
- A.1.4 可以支持多種視頻播放類型,比如,原生封裝視頻播放器,還有基於ijkPlayer封裝的播放器。
- A.1.5 可以設置是否隱藏播放音量,播放進度,播放亮度等,可以通過拖動seekBar改變視頻進度。還支持設置n秒後不操作則隱藏頭部和頂部佈局功能
- A.1.6 可以設置豎屏模式下全屏模式和橫屏模式下的全屏模式,方便多種使用場景
- A.1.7 top和bottom面版消失和顯示:點擊視頻畫面會顯示、隱藏操作面板;顯示後不操作會5秒後自動消失【也可以設置n秒消失時間】
-
B高級功能
- B.1.1 支持一遍播放一遍緩衝的功能,其中緩衝包括兩部分,第一種是播放過程中緩衝,第二種是暫停過程中緩衝
- B.1.2 基於ijkPlayer,ExoPlayer,Rtc,原生MediaPlayer等的封裝播放器,支持多種格式視頻播放
- B.1.3 可以設置是否記錄播放位置,設置播放速度,設置屏幕比例
- B.1.4 支持滑動改變音量【屏幕右邊】,改變屏幕亮度【屏幕左邊】,屏幕底測左右滑動調節進度
- B.1.5 支持list頁面中視頻播放,滾動後暫停播放,播放可以自由設置是否記錄狀態。並且還支持刪除視頻播放位置狀態。
- B.1.6 切換橫豎屏:切換全屏時,隱藏狀態欄,顯示自定義top(顯示電量);豎屏時恢復原有狀態
- B.1.7 支持切換視頻清晰度模式
- B.1.8 添加鎖屏功能,豎屏不提供鎖屏按鈕,橫屏全屏時顯示,並且鎖屏時,屏蔽手勢處理
-
C拓展功能【這塊根據實際情況選擇是否需要使用,一般視頻付費App會有這個工鞥】
- C1產品需求:類似優酷,愛奇藝視頻播放器部分邏輯。比如如果用戶沒有登錄也沒有看視頻權限,則提示試看視頻[自定義佈局];如果用戶沒有登錄但是有看視頻權限,則正常觀看;如果用戶登錄,但是沒有充值會員,部分需要權限視頻則進入試看模式,試看結束後彈出充值會員界面;如果用戶餘額不足,比如餘額只有99元,但是視頻觀看要199元,則又有其他提示。
- C2自身需求:比如封裝好了視頻播放庫,那麼點擊視頻上登錄按鈕則跳到登錄頁面;點擊充值會員頁面也跳到充值頁面。這個通過定義接口,可以讓使用者通過方法調用,靈活處理點擊事件。
- C.1.1 可以設置試看模式,設置試看時長。試看結束後就提示登錄或者充值……
- C.1.2 對於設置視頻的寬高,建議設置成4:3或者16:9或者常用比例,如果不是常用比例,則可能會有黑邊。其中黑邊的背景可以設置
- C.1.3 可以設置播放有權限的視頻時的各種文字描述,而沒有把它寫在封裝庫中,使用者自己設定
- C.1.4 鎖定屏幕功能,這個參考大部分播放器,只有在全屏模式下才會有
03.視頻播放器架構說明
-
視頻常見的佈局視圖
- 視頻底圖(用於顯示初始化視頻時的封面圖),視頻狀態視圖【加載loading,播放異常,加載視頻失敗,播放完成等】
- 改變亮度和聲音【改變聲音視圖,改變亮度視圖】,改變視頻快進和快退,左右滑動快進和快退視圖(手勢滑動的快進快退提示框)
- 頂部控制區視圖(包含返回健,title等),底部控制區視圖(包含進度條,播放暫停,時間,切換全屏等)
- 鎖屏佈局視圖(全屏時展示,其他隱藏),底部播放進度條視圖(很多播放器都有這個),清晰度列表視圖(切換清晰度彈窗)
-
後期可能涉及的佈局視圖
- 手勢指導頁面(有些播放器有新手指導功能),離線下載的界面(該界面中包含下載列表, 列表的item編輯(全選, 刪除))
- 用戶從wifi切換到4g網絡,提示網絡切換彈窗界面(當網絡由wifi變為4g的時候會顯示)
- 圖片廣告視圖(帶有倒計時消失),開始視頻廣告視圖,非會員試看視圖
- 彈幕視圖(這個很重要),水印顯示視圖,倍速播放界面(用於控制倍速),底部視頻列表縮略圖視圖
- 投屏視頻視圖界面,視頻直播間刷禮物界面,老師開課界面,展示更多視圖(下載,分享,切換音頻等)
-
視頻播放器的痛點
-
播放器內核難以切換
- 不同的視頻播放器內核,由於api不一樣,所以難以切換操作。要是想兼容內核切換,就必須自己制定一個視頻接口+實現類的播放器
-
播放器內核和UI層耦合
- 也就是說視頻player和ui操作柔和到了一起,尤其是兩者之間的交互。比如播放中需要更新UI進度條,播放異常需要顯示異常UI,都比較難處理播放器狀態變化更新UI操作
-
UI難以自定義或者修改麻煩
- 比如常見的視頻播放器,會把視頻各種視圖寫到xml中,這種方式在後期代碼會很大,而且改動一個小的佈局,則會影響大。這樣到後期往往只敢加代碼,而不敢刪除代碼……
- 有時候難以適應新的場景,比如添加一個播放廣告,老師開課,或者視頻引導業務需求,則需要到播放器中寫一堆業務代碼。迭代到後期,違背了開閉原則,視頻播放器需要做到和業務分離
-
視頻播放器結構不清晰
- 這個是指該視頻播放器能否看了文檔後快速上手,知道封裝的大概流程。方便後期他人修改和維護,因此需要將視頻播放器功能分離。比如切換內核+視頻播放器(player+controller+view)
-
-
需要達到的目的和效果
- 基礎封裝視頻播放器player,可以在ExoPlayer、MediaPlayer,聲網RTC視頻播放器內核,原生MediaPlayer可以自由切換
- 對於視圖狀態切換和後期維護拓展,避免功能和業務出現耦合。比如需要支持播放器UI高度定製,而不是該lib庫中UI代碼
- 針對視頻播放,視頻投屏,音頻播放,播放回放,以及視頻直播的功能
-
通用視頻框架特點
-
一定要解耦合
- 播放器內核與播放器解耦: 支持更多的播放場景、以及新的播放業務快速接入,並且不影響其他播放業務,比如後期添加阿里雲播放器內核,或者騰訊播放器內核
- 播放器player與視頻UI解耦:支持添加自定義視頻視圖,比如支持添加自定義廣告,新手引導,或者視頻播放異常等視圖,這個需要較強的拓展性
-
適合多種業務場景
- 比如適合播放單個視頻,多個視頻,以及列表視頻,或者類似抖音那種一個頁面一個視頻,還有小窗口播放視頻。也就是適合大多數業務場景
-
-
視頻分層
-
播放器內核
- 可以切換ExoPlayer、MediaPlayer,IjkPlayer,聲網視頻播放器,這裡使用工廠模式Factory + AbstractVideoPlayer + 各個實現AbstractVideoPlayer抽象類的播放器類
- 定義抽象的播放器,主要包含視頻初始化,設置,狀態設置,以及播放監聽。由於每個內核播放器api可能不一樣,所以這裡需要實現AbstractVideoPlayer抽象類的播放器類,方便後期統一調用
- 為了方便創建不同內核player,所以需要創建一個PlayerFactory,定義一個createPlayer創建播放器的抽象方法,然後各個內核都實現它,各自創建自己的播放器
-
VideoPlayer播放器
- 可以自由切換視頻內核,Player+Controller。player負責播放的邏輯,Controller負責視圖相關的邏輯,兩者之間用接口進行通信
- 針對Controller,需要定義一個接口,主要負責視圖UI處理邏輯,支持添加各種自定義視圖View【統一實現自定義接口Control】,每個view儘量保證功能單一性,最後通過addView形式添加進來
- 針對Player,需要定義一個接口,主要負責視頻播放處理邏輯,比如視頻播放,暫停,設置播放進度,設置視頻鏈接,切換播放模式等操作。需要注意把Controller設置到Player裡面,兩者之間通過接口交互
-
UI控制器視圖
- 定義一個BaseVideoController類,這個主要是集成各種事件的處理邏輯,比如播放器狀態改變,控制視圖隱藏和顯示,播放進度改變,鎖定狀態改變,設備方向監聽等等操作
- 定義一個view的接口InterControlView,在這裡類裡定義綁定視圖,視圖隱藏和顯示,播放狀態,播放模式,播放進度,鎖屏等操作。這個每個實現類則都可以拿到這些屬性呢
- 在BaseVideoController中使用LinkedHashMap保存每個自定義view視圖,添加則put進來後然後通過addView將視圖添加到該控制器中,這樣非常方便添加自定義視圖
- 播放器切換狀態需要改變Controller視圖,比如視頻異常則需要顯示異常視圖view,則它們之間的交互是通過ControlWrapper(同時實現Controller接口和Player接口)實現
-
04.視頻播放器如何使用
4.1 關於gradle引用說明
-
如下所示
//視頻UI層,必須要有 implementation 'cn.yc:VideoPlayer:3.0.1' //視頻緩存,如果不需要則可以不依賴 implementation 'cn.yc:VideoCache:3.0.0' //視頻內核層,必須有 implementation 'cn.yc:VideoKernel:3.0.1'
4.2 在xml中添加布局
- 注意,在實際開發中,由於Android手機碎片化比較嚴重,分辨率太多了,建議靈活設置佈局的寬高比為4:3或者16:9或者你認為合適的,可以用代碼設置。
-
如果寬高比變形,則會有黑邊
<org.yczbj.ycvideoplayerlib.player.VideoPlayer android:id="@+id/video_player" android:layout_width="match_parent" android:layout_height="240dp"/>
4.3 最簡單的視頻播放器參數設定
-
如下所示
//創建基礎視頻播放器,一般播放器的功能 BasisVideoController controller = new BasisVideoController(this); //設置控制器 mVideoPlayer.setVideoController(controller); //設置視頻播放鏈接地址 mVideoPlayer.setUrl(url); //開始播放 mVideoPlayer.start();
4.4 注意問題
-
如果是全屏播放,則需要在清單文件中設置當前activity的屬性值
- android:configChanges 保證了在全屏的時候橫豎屏切換不會執行Activity的相關生命週期,打斷視頻的播放
- android:screenOrientation 固定了屏幕的初始方向
-
這兩個變量控制全屏後和退出全屏的屏幕方向
<activity android:name=".VideoActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait"/>
-
如何一進入頁面就開始播放視頻,稍微延時一下即可
-
代碼如下所示,注意避免直接start(),因為有可能視頻還沒有初始化完成……
mVideoPlayer.postDelayed(new Runnable() { @Override public void run() { mVideoPlayer.start(); } },300);
-
05.播放器詳細Api文檔
- 01.最簡單的播放
- 02.如何切換視頻內核
- 03.切換視頻模式
- 04.切換視頻清晰度
- 05.視頻播放監聽
- 06.列表中播放處理
- 07.懸浮窗口播放
- 08.其他重要功能Api
- 09.播放多個視頻
- 10.VideoPlayer相關Api
- 11.Controller相關Api
- 12.仿快手播放視頻
- 具體看這篇文檔:[視頻播放器Api說明]()
06.播放器封裝思路
6.1視頻層級示例圖
6.2 視頻播放器流程圖
- 待完善
6.3 視頻播放器lib庫
6.4 視頻內核lib庫介紹
6.5視頻播放器UI庫介紹
07.播放器示例展示圖
08.添加自定義視圖
- 比如,現在有個業務需求,需要在視頻播放器剛開始添加一個廣告視圖,等待廣告倒計時120秒後,直接進入播放視頻邏輯。相信這個業務場景很常見,大家都碰到過,使用該播放器就特別簡單,代碼如下所示:
-
首先創建一個自定義view,需要實現InterControlView接口,重寫該接口中所有抽象方法,這裡省略了很多代碼,具體看demo。
public class AdControlView extends FrameLayout implements InterControlView, View.OnClickListener { private ControlWrapper mControlWrapper; public AdControlView(@NonNull Context context) { super(context); init(context); } private void init(Context context){ LayoutInflater.from(getContext()).inflate(R.layout.layout_ad_control_view, this, true); } /** * 播放狀態 * -1 播放錯誤 * 0 播放未開始 * 1 播放準備中 * 2 播放準備就緒 * 3 正在播放 * 4 暫停播放 * 5 正在緩衝(播放器正在播放時,緩衝區數據不足,進行緩衝,緩衝區數據足夠後恢復播放) * 6 暫停緩衝(播放器正在播放時,緩衝區數據不足,進行緩衝,此時暫停播放器,繼續緩衝,緩衝區數據足夠後恢復暫停 * 7 播放完成 * 8 開始播放中止 * @param playState 播放狀態,主要是指播放器的各種狀態 */ @Override public void onPlayStateChanged(int playState) { switch (playState) { case ConstantKeys.CurrentState.STATE_PLAYING: mControlWrapper.startProgress(); mPlayButton.setSelected(true); break; case ConstantKeys.CurrentState.STATE_PAUSED: mPlayButton.setSelected(false); break; } } /** * 播放模式 * 普通模式,小窗口模式,正常模式三種其中一種 * MODE_NORMAL 普通模式 * MODE_FULL_SCREEN 全屏模式 * MODE_TINY_WINDOW 小屏模式 * @param playerState 播放模式 */ @Override public void onPlayerStateChanged(int playerState) { switch (playerState) { case ConstantKeys.PlayMode.MODE_NORMAL: mBack.setVisibility(GONE); mFullScreen.setSelected(false); break; case ConstantKeys.PlayMode.MODE_FULL_SCREEN: mBack.setVisibility(VISIBLE); mFullScreen.setSelected(true); break; } //暫未實現全面屏適配邏輯,需要你自己補全 } }
-
然後該怎麼使用這個自定義view呢?很簡單,在之前基礎上,通過控制器對象add進來即可,代碼如下所示
controller = new BasisVideoController(this); AdControlView adControlView = new AdControlView(this); adControlView.setListener(new AdControlView.AdControlListener() { @Override public void onAdClick() { BaseToast.showRoundRectToast( "廣告點擊跳轉"); } @Override public void onSkipAd() { playVideo(); } }); controller.addControlComponent(adControlView); //設置控制器 mVideoPlayer.setController(controller); mVideoPlayer.setUrl(proxyUrl); mVideoPlayer.start();
09.視頻播放器優化處理
9.1 如何兼容不同內核播放器
-
提問:針對不同內核播放器,比如谷歌的ExoPlayer,B站的IjkPlayer,還有原生的MediaPlayer,有些api不一樣,那使用的時候如何統一api呢?
- 比如說,ijk和exo的視頻播放listener監聽api就完全不同,這個時候需要做兼容處理
- 定義接口,然後各個不同內核播放器實現接口,重寫抽象方法。調用的時候,獲取接口對象調用api,這樣就可以統一Api
-
定義一個接口,這個接口有什麼呢?這個接口定義通用視頻播放器方法,比如常見的有:視頻初始化,設置url,加載,以及播放狀態,簡單來說可以分為三個部分。
- 第一部分:視頻初始化實例對象方法,主要包括:initPlayer初始化視頻,setDataSource設置視頻播放器地址,setSurface設置視頻播放器渲染view,prepareAsync開始準備播放操作
- 第二部分:視頻播放器狀態方法,主要包括:播放,暫停,恢復,重製,設置進度,釋放資源,獲取進度,設置速度,設置音量
- 第三部分:player綁定view後,需要監聽播放狀態,比如播放異常,播放完成,播放準備,播放size變化,還有播放準備
-
首先定義一個工廠抽象類,然後不同的內核播放器分別創建其具體的工廠實現具體類
- PlayerFactory:抽象工廠,擔任這個角色的是工廠方法模式的核心,任何在模式中創建對象的工廠類必須實現這個接口
- ExoPlayerFactory:具體工廠,具體工廠角色含有與業務密切相關的邏輯,並且受到使用者的調用以創建具體產品對象。
-
如何使用,分為三步,具體操作如下所示
- 1.先調用具體工廠對象中的方法createPlayer方法;2.根據傳入產品類型參數獲得具體的產品對象;3.返回產品對象並使用。
- 簡而言之,創建對象的時候只需要傳遞類型type,而不需要對應的工廠,即可創建具體的產品對象
-
這種創建對象最大優點
- 工廠方法用來創建所需要的產品,同時隱藏了哪種具體產品類將被實例化這一細節,用戶只需要關心所需產品對應的工廠,無須關心創建細節,甚至無須知道具體產品類的類名。
- 加入新的產品時,比如後期新加一個阿里播放器內核,這個時候就只需要添加一個具體工廠和具體產品就可以。系統的可擴展性也就變得非常好,完全符合“開閉原則”
9.2 播放器UI抽取封裝優化
-
發展中遇到的問題
- 播放器可支持多種場景下的播放,多個產品會用到同一個播放器,這樣就會帶來一個問題,一個播放業務播放器狀態發生變化,其他播放業務必須同步更新播放狀態,各個播放業務之間互相交叉,隨著播放業務的增多,開發和維護成本會急劇增加, 導致後續開發不可持續。
-
UI難以自定義或者修改麻煩
- 比如常見的視頻播放器,會把視頻各種視圖寫到xml中,這種方式在後期代碼會很大,而且改動一個小的佈局,則會影響大。這樣到後期往往只敢加代碼,而不敢刪除代碼……
- 有時候難以適應新的場景,比如添加一個播放廣告,老師開課,或者視頻引導業務需求,則需要到播放器中寫一堆業務代碼。迭代到後期,違背了開閉原則,視頻播放器需要做到和業務分離
-
視頻播放器結構需要清晰
- 也就是說視頻player和ui操作柔和到了一起,尤其是兩者之間的交互。比如播放中需要更新UI進度條,播放異常需要顯示異常UI,都比較難處理播放器狀態變化更新UI操作
- 這個是指該視頻播放器能否看了文檔後快速上手,知道封裝的大概流程。方便後期他人修改和維護,因此需要將視頻播放器功能分離。比如切換內核+視頻播放器(player+controller+view)
- 一定要解耦合,播放器player與視頻UI解耦:支持添加自定義視頻視圖,比如支持添加自定義廣告,新手引導,或者視頻播放異常等視圖,這個需要較強的拓展性
-
適合多種業務場景
- 比如適合播放單個視頻,多個視頻,以及列表視頻,或者類似抖音那種一個頁面一個視頻,還有小窗口播放視頻。也就是適合大多數業務場景
-
方便播放業務發生變化
- 播放狀態變化是導致不同播放業務場景之間交叉同步,解除播放業務對播放器的直接操控,採用接口監聽進行解耦。比如:player+controller+interface
-
關於視頻播放器
- 定義一個視頻播放器InterVideoPlayer接口,操作視頻播放,暫停,緩衝,進度設置,設置播放模式等多種操作。
- 然後寫一個播放器接口的具體實現類,在這個裡面拿到內核播放器player,然後做相關的實現操作。
-
關於視頻視圖View
- 定義一個視圖InterVideoController接口,主要負責視圖顯示/隱藏,播放進度,鎖屏,狀態欄等操作。
- 然後寫一個播放器視圖接口的具體實現類,在這裡裡面inflate視圖操作,然後接口方法實現,為了方便後期開發者自定義view,因此需要addView操作,將添加進來的視圖用map集合裝起來。
-
播放器player和controller交互
- 在player中創建BaseVideoController對象,這個時候需要把controller添加到播放器中,這個時候有兩個要點特別重要,需要把播放器狀態監聽,和播放模式監聽傳遞給控制器
- setPlayState設置視頻播放器播放邏輯狀態,主要是播放緩衝,加載,播放中,暫停,錯誤,完成,異常,播放進度等多個狀態,方便控制器做UI更新操作
- setPlayerState設置視頻播放切換模式狀態,主要是普通模式,小窗口模式,正常模式三種其中一種,方便控制器做UI更新
-
播放器player和view交互
- 這塊非常關鍵,舉個例子,視頻播放失敗需要顯示控制層的異常視圖View;播放視頻初始化需要顯示loading,然後更新UI播放進度條等。都是播放器和視圖層交互
- 可以定義一個類,同時實現InterVideoPlayer接口和InterVideoController接口,這個時候會重新這兩個接口所有的方法。此類的目的是為了在InterControlView接口實現類中既能調用VideoPlayer的api又能調用BaseVideoController的api
-
如何添加自定義播放器視圖
- 添加了自定義播放器視圖,比如添加視頻廣告,可以選擇跳過,選擇播放暫停。那這個視圖view,肯定是需要操作player或者獲取player的狀態的。這個時候就需要暴露監聽視頻播放的狀態接口監聽
- 首先定義一個InterControlView接口,也就是說所有自定義視頻視圖view需要實現這個接口,該接口中的核心方法有:綁定視圖到播放器,視圖顯示隱藏變化監聽,播放狀態監聽,播放模式監聽,進度監聽,鎖屏監聽等
- 在BaseVideoController中的狀態監聽中,通過InterControlView接口對象就可以把播放器的狀態傳遞到子類中
9.4 代碼方面優化措施
-
如果是在Activity中的話,建議設置下面這段代碼
@Override protected void onResume() { super.onResume(); if (mVideoPlayer != null) { //從後臺切換到前臺,當視頻暫停時或者緩衝暫停時,調用該方法重新開啟視頻播放 mVideoPlayer.resume(); } } @Override protected void onPause() { super.onPause(); if (mVideoPlayer != null) { //從前臺切到後臺,當視頻正在播放或者正在緩衝時,調用該方法暫停視頻 mVideoPlayer.pause(); } } @Override protected void onDestroy() { super.onDestroy(); if (mVideoPlayer != null) { //銷燬頁面,釋放,內部的播放器被釋放掉,同時如果在全屏、小窗口模式下都會退出 mVideoPlayer.release(); } } @Override public void onBackPressed() { //處理返回鍵邏輯;如果是全屏,則退出全屏;如果是小窗口,則退出小窗口 if (mVideoPlayer == null || !mVideoPlayer.onBackPressed()) { super.onBackPressed(); } }
10.播放器問題記錄說明
11.性能優化和庫大小
12.視頻緩存原理介紹
-
網絡上比較好的項目:https://github.com/danikula/AndroidVideoCache
- 網絡用的HttpURLConnection,文件緩存處理,文件最大限度策略,回調監聽處理,斷點續傳,代理服務等。
-
但是存在一些問題,比如如下所示
- 文件的緩存超過限制後沒有按照lru算法刪除,
- 處理返回給播放器的http響應頭消息,響應頭消息的獲取處理改為head請求(需服務器支持)
- 替換網絡庫為okHttp(因為大部分的項目都是以okHttp為網絡請求庫的),但是這個改動性比較大
-
然後看一下怎麼使用,超級簡單。傳入視頻url鏈接,返回一個代理鏈接,然後就可以呢
HttpProxyCacheServer cacheServer = ProxyVideoCacheManager.getProxy(this); String proxyUrl = cacheServer.getProxyUrl(URL_AD); mVideoPlayer.setUrl(proxyUrl); public static HttpProxyCacheServer getProxy(Context context) { return sharedProxy == null ? (sharedProxy = newProxy(context)) : sharedProxy; } private static HttpProxyCacheServer newProxy(Context context) { return new HttpProxyCacheServer.Builder(context) .maxCacheSize(512 * 1024 * 1024) // 512MB for cache //緩存路徑,不設置默認在sd_card/Android/data/[app_package_name]/cache中 //.cacheDirectory() .build(); }
-
大概的原理
- 原始的方式是直接塞播放地址給播放器,它就可以直接播放。現在我們要在中間加一層本地代理,播放器播放的時候(獲取數據)是通過我們的本地代理的地址來播放的,這樣我們就可以很好的在中間層(本地代理層)做一些處理,比如:文件緩存,預緩存(秒開處理),監控等。
-
原理詳細一點來說
- 1.採用了本地代理服務的方式,通過原始url給播放器返回一個本地代理的一個url ,代理URL類似:http://127.0.0.1:port/視頻url;(port端口為系統隨機分配的有效端口,真實url是為了真正的下載),然後播放器播放的時候請求到了你本地的代理上了。
- 2.本地代理採用ServerSocket監聽127.0.0.1的有效端口,這個時候手機就是一個服務器了,客戶端就是socket,也就是播放器。
- 3.讀取客戶端就是socket來讀取數據(http協議請求)解析http協議。
- 4.根據url檢查視頻文件是否存在,讀取文件數據給播放器,也就是往socket裡寫入數據(socket通信)。同時如果沒有下載完成會進行斷點下載,當然弱網的話數據需要生產消費同步處理。
-
如何實現預加載
- 其實預加載的思路很簡單,在進行一個播放視頻後,再返回接下來需要預加載的視頻url,啟用線程去請求下載數據
- 開啟一個線程去請求並預加載一部分的數據,可能需要預加載的數據大於>1,利用隊列先進入的先進行加載,因此可以採用LinkedHashMap保存正在預加載的task。
- 在開始預加載的時候,判斷該播放地址是否已經預加載,如果不是那麼創建一個線程task,並且把它放到map集合中。然後執行預加載邏輯,也就是執行HttpURLConnection請求
- 提供取消對應url加載的任務,因為有可能該url不需要再進行預加載了,比如參考抖音,當用戶瞬間下滑幾個視頻,那麼很多視頻就需要跳過了不需要再進行預加載
- 具體直接看項目代碼:VideoCache緩衝模塊
13.查看視頻播放器日誌
-
統一管理視頻播放器封裝庫日誌,方便後期排查問題
- 比如,視頻內核,日誌過濾則是:aaa
- 比如,視頻player,日誌過濾則是:bbb
- 比如,緩存模塊,日誌過濾則是:VideoCache
14.該庫異常code說明
-
針對視頻封裝庫,統一處理拋出的異常,為了方便開發者快速知道異常的來由,則可以查詢約定的code碼。
- 這個在sdk中特別常見,因此該庫一定程度是借鑑騰訊播放器……