天天看點

01.視訊播放器架構介紹視訊播放器介紹文檔

視訊播放器介紹文檔

目錄介紹

  • 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視圖層

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

  • 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 鎖定螢幕功能,這個參考大部分播放器,隻有在全屏模式下才會有

  • 視訊常見的布局視圖
    • 視訊底圖(用于顯示初始化視訊時的封面圖),視訊狀态視圖【加載loading,播放異常,加載視訊失敗,播放完成等】
    • 改變亮度和聲音【改變聲音視圖,改變亮度視圖】,改變視訊快進和快退,左右滑動快進和快退視圖(手勢滑動的快進快退提示框)
    • 頂部控制區視圖(包含傳回健,title等),底部控制區視圖(包含進度條,播放暫停,時間,切換全屏等)
    • 鎖屏布局視圖(全屏時展示,其他隐藏),底部播放進度條視圖(很多點傳播放器都有這個),清晰度清單視圖(切換清晰度彈窗)
  • 後期可能涉及的布局視圖
    • 手勢指導頁面(有些播放器有新手指導功能),離線下載下傳的界面(該界面中包含下載下傳清單, 清單的item編輯(全選, 删除))
    • 使用者從wifi切換到4g網絡,提示網絡切換彈窗界面(當網絡由wifi變為4g的時候會顯示)
    • 圖檔廣告視圖(帶有倒計時消失),開始視訊廣告視圖,非會員試看視圖
    • 彈幕視圖(這個很重要),水印顯示視圖,倍速播放界面(用于控制倍速),底部視訊清單縮略圖視圖
    • 投屏視訊視圖界面,視訊直播間刷禮物界面,老師開課界面,展示更多視圖(下載下傳,分享,切換音頻等)
  • 視訊播放器的痛點
    • 播放器核心難以切換
      • 不同的視訊播放器核心,由于api不一樣,是以難以切換操作。要是想相容核心切換,就必須自己制定一個視訊接口+實作類的播放器
    • 播放器核心和UI層耦合
      • 也就是說視訊player和ui操作柔和到了一起,尤其是兩者之間的互動。比如播放中需要更新UI進度條,播放異常需要顯示異常UI,都比較難處理播放器狀态變化更新UI操作
    • UI難以自定義或者修改麻煩
      • 比如常見的視訊播放器,會把視訊各種視圖寫到xml中,這種方式在後期代碼會很大,而且改動一個小的布局,則會影響大。這樣到後期往往隻敢加代碼,而不敢删除代碼……
      • 有時候難以适應新的場景,比如添加一個播放廣告,老師開課,或者視訊引導業務需求,則需要到播放器中寫一堆業務代碼。疊代到後期,違背了開閉原則,視訊播放器需要做到和業務分離
    • 視訊播放器結構不清晰
      • 這個是指該視訊播放器能否看了文檔後快速上手,知道封裝的大概流程。友善後期他人修改和維護,是以需要将視訊播放器功能分離。比如切換核心+視訊播放器(player+controller+view)
  • 需要達到的目的和效果
    • 針對視訊播放,視訊投屏,音頻播放,播放回放,以及視訊直播的功能
  • 通用視訊架構特點
    • 一定要解耦合
      • 播放器核心與播放器解耦: 支援更多的播放場景、以及新的播放業務快速接入,并且不影響其他播放業務,比如後期添加阿裡雲播放器核心,或者騰訊播放器核心
      • 播放器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接口)實作

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);           

  • 01.最簡單的播放
  • 02.如何切換視訊核心
  • 03.切換視訊模式
  • 04.切換視訊清晰度
  • 05.視訊播放監聽
  • 06.清單中播放處理
  • 07.懸浮視窗播放
  • 08.其他重要功能Api
  • 09.播放多個視訊
  • 10.VideoPlayer相關Api
  • 11.Controller相關Api
  • 12.仿快手播放視訊
  • 具體看這篇文檔:[視訊播放器Api說明]()

6.1視訊層級示例圖

01.視訊播放器架構介紹視訊播放器介紹文檔

6.2 視訊播放器流程圖

  • 待完善

6.3 視訊播放器lib庫

01.視訊播放器架構介紹視訊播放器介紹文檔

6.4 視訊核心lib庫介紹

01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔

6.5視訊播放器UI庫介紹

01.視訊播放器架構介紹視訊播放器介紹文檔

01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔
01.視訊播放器架構介紹視訊播放器介紹文檔

  • 比如,現在有個業務需求,需要在視訊播放器剛開始添加一個廣告視圖,等待廣告倒計時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();           

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抽取封裝優化

  • 發展中遇到的問題
    • 播放器可支援多種場景下的播放,多個産品會用到同一個播放器,這樣就會帶來一個問題,一個播放業務播放器狀态發生變化,其他播放業務必須同步更新播放狀态,各個播放業務之間互相交叉,随着播放業務的增多,開發和維護成本會急劇增加, 導緻後續開發不可持續。
  • 視訊播放器結構需要清晰
    • 一定要解耦合,播放器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();
        }
    }           

  • 網絡上比較好的項目: 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緩沖子產品

  • 統一管理視訊播放器封裝庫日志,友善後期排查問題
    • 比如,視訊核心,日志過濾則是:aaa
    • 比如,視訊player,日志過濾則是:bbb
    • 比如,緩存子產品,日志過濾則是:VideoCache

  • 針對視訊封裝庫,統一處理抛出的異常,為了友善開發者快速知道異常的來由,則可以查詢約定的code碼。
    • 這個在sdk中特别常見,是以該庫一定程度是借鑒騰訊播放器……

視訊架構: https://github.com/yangchong211/YCVideoPlayer

繼續閱讀