1. 程式人生 > >Android基礎學習總結(十六)——基於ijkplayer封裝支援簡單介面UI定製的視訊播放器

Android基礎學習總結(十六)——基於ijkplayer封裝支援簡單介面UI定製的視訊播放器

前言

專案開發中遇到需要解析播放m3u8視訊流的情況,但是原生的PlayerView非常慢,使用起來複雜,不適合上手,這裡找到一款ijkplayer是Bilibili基於ffmpeg開發並開源的輕量級視訊播放器,支援播放本地網路視訊,也支援流媒體播放。支援Android&iOS。
這裡感謝jjdxmashl基於ijkplayer封裝了支援簡單介面UI定製的視訊播放器,操作簡單實用,推薦大家使用。

簡介

當前專案是基於ijkplayer專案進行的播放器介面UI封裝。
是一個適用於 Android 的 RTMP 直播推流 SDK,可高度定製化和二次開發。特色是同時支援 H.264 軟編/硬編和 AAC 軟編/硬編。主要是支援RIMP、HLS、MP4、M4A等視訊格式的播放。
作者專案地址:

http://www.github.com/jjdxmashl/jjdxm_ijkplayer
作者簡書地址:http://www.jianshu.com/p/6c938df18413

特性

  • 基於ijkplayer封裝的視訊播放器介面,支援 RTMP , HLS (http & https) , MP4,M4A 等;
  • 可根據需求去定製部分介面樣式;
  • 常用的手勢操作左邊上下亮度,右邊上下聲音,左右滑動播放進度調整;
  • 支援多種解析度流的切換播放;
  • 播放出錯嘗試重連;
  • 介面裁剪顯示樣式;

快速開始

step1:匯入依賴

該專案已經打包到jcenter中心了,可以通過compile命令直接依賴,在主程式目錄build.gradle中,新增以下程式碼:

compile ‘com.dou361.ijkplayer:jjdxm-ijkplayer:1.0.0

step2:簡單的播放器實現

setContentView(R.layout.simple_player_view_player);
String url = "http://9890.vod.myqcloud.com/9890_9c1fa3e2aea011e59fc841df10c92278.f20.mp4";
player = new PlayerView(this)
        .setTitle("什麼")
        .setScaleType(PlayStateParams.fitparent
) .hideMenu(true) .forbidTouch(false) .showThumbnail(new OnShowThumbnailListener() { @Override public void onShowThumbnail(ImageView ivThumbnail) { Glide.with(mContext) .load("http://pic2.nipic.com/20090413/406638_125424003_2.jpg") .placeholder(R.color.cl_default) .error(R.color.cl_error) .into(ivThumbnail); } }) .setPlaySource(url) .startPlay();

step3:多種不同的解析度流的播放器實現

在佈局中使用simple_player_view_player.xml佈局

<include
    layout="@layout/simple_player_view_player"
    android:layout_width="match_parent"
    android:layout_height="180dp"/>

程式碼中建立一個播放器物件

/**播放資源*/
ist<VideoijkBean> list = new ArrayList<VideoijkBean>();
String url1 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4";
String url2 = "http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f30.mp4";
VideoijkBean m1 = new VideoijkBean();
m1.setStream("標清");
m1.setUrl(url1);
VideoijkBean m2 = new VideoijkBean();
m2.setStream("高清");
m2.setUrl(url2);
list.add(m1);
list.add(m2);
/**播放器*/
player = new PlayerView(this)
            .setTitle("什麼")
            .setScaleType(PlayStateParams.fitparent)
            .hideMenu(true)
            .forbidTouch(false)
            .showThumbnail(new OnShowThumbnailListener() {
                @Override
                public void onShowThumbnail(ImageView ivThumbnail) {
                    /**載入前顯示的縮圖*/
                    Glide.with(mContext)
                            .load("http://pic2.nipic.com/20090413/406638_125424003_2.jpg")
                            .placeholder(R.color.cl_default)
                            .error(R.color.cl_error)
                            .into(ivThumbnail);
                }
            })
            .setPlaySource(list)
            .startPlay();

配置生命週期方法

為了讓播放器同步Activity生命週期,建議以下方法都去配置,註釋的程式碼,主要作用是播放時螢幕常亮和暫停其它媒體的播放。

@Override
protected void onPause() {
    super.onPause();
    if (player != null) {
        player.onPause();
    }
    /**demo的內容,恢復系統其它媒體的狀態*/
    //MediaUtils.muteAudioFocus(mContext, true);
}

@Override
protected void onResume() {
    super.onResume();
    if (player != null) {
        player.onResume();
    }
    /**demo的內容,暫停系統其它媒體的狀態*/
    MediaUtils.muteAudioFocus(mContext, false);
    /**demo的內容,啟用裝置常亮狀態*/
    //if (wakeLock != null) {
    //    wakeLock.acquire();
    //}
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (player != null) {
        player.onDestroy();
    }
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (player != null) {
        player.onConfigurationChanged(newConfig);
    }
}

@Override
public void onBackPressed() {
    if (player != null && player.onBackPressed()) {
        return;
    }
    super.onBackPressed();
    /**demo的內容,恢復裝置亮度狀態*/
    //if (wakeLock != null) {
    //    wakeLock.release();
    //}
}

更多UI樣式的設定

1.視訊介面裁剪設定

可通過方法setScaleType(int type)去設定

  • PlayStateParams.fitParent:可能會剪裁,保持原視訊的大小,顯示在中心,當原視訊的大小超過view的大小超過部分裁剪處理
  • PlayStateParams.fillParent:可能會剪裁,等比例放大視訊,直到填滿View為止,超過View的部分作裁剪處理
  • PlayStateParams.wrapcontent:將視訊的內容完整居中顯示,如果視訊大於view,則按比例縮視訊直到完全顯示在view中
  • PlayStateParams.fitXY:不剪裁,非等比例拉伸畫面填滿整個View
  • PlayStateParams.f16_9:不剪裁,非等比例拉伸畫面到16:9,並完全顯示在View中
  • PlayStateParams.f4_3:不剪裁,非等比例拉伸畫面到4:3,並完全顯示在View中

2.播放器底部bar播放進度條樣式定製

預設的進度樣式是豎屏為上下樣式,即進度條在播放時長的上面,橫屏為左右樣式,即進度條在播放時長的中間。樣式定製主要是兩個方法搭配使用toggleProcessDurationOrientation方法和setProcessDurationOrientation方法,橫豎屏切換2中情況,和3種進度條樣式

/**上下樣式*/
PlayStateParams.PROCESS_PORTRAIT
/**左右樣式*/
PlayStateParams.PROCESS_LANDSCAPE
/**中間兩邊樣式*/
PlayStateParams.PROCESS_CENTER

總共有2的3次方中樣式,下面只羅列幾種樣式
(1).橫豎屏都為上下樣式

player = new PlayerView(this) {
        @Override
        public PlayerView toggleProcessDurationOrientation() {
            return setProcessDurationOrientation(PlayStateParams.PROCESS_PORTRAIT);
        }
    }
            .setTitle("什麼")
            .setProcessDurationOrientation(PlayStateParams.PROCESS_PORTRAIT)
            .setScaleType(PlayStateParams.fitparent)
            .forbidTouch(false)
            .hideCenterPlayer(true)
            .setPlaySource(list)
            .startPlay();

(2).橫豎屏都為左右樣式

player = new PlayerView(this) {
        @Override
        public PlayerView toggleProcessDurationOrientation() {
            return setProcessDurationOrientation(PlayStateParams.PROCESS_LANDSCAPE);
        }
    }
            .setTitle("什麼")
            .setProcessDurationOrientation(PlayStateParams.PROCESS_LANDSCAPE)
            .setScaleType(PlayStateParams.fitparent)
            .forbidTouch(false)
            .hideCenterPlayer(true)
            .setPlaySource(list)
            .startPlay();

(3).橫屏為上下樣式豎屏為左右樣式

player = new PlayerView(this) {
        @Override
        public PlayerView toggleProcessDurationOrientation() {
            return setProcessDurationOrientation(getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE?PlayStateParams.PROCESS_LANDSCAPE:PlayStateParams.PROCESS_PORTRAIT);
        }
    }
            .setTitle("什麼")
            .setProcessDurationOrientation(PlayStateParams.PROCESS_LANDSCAPE)
            .setScaleType(PlayStateParams.fitparent)
            .forbidTouch(false)
            .hideCenterPlayer(true)
            .setPlaySource(list)
            .startPlay();

3.隱藏部分不想要的介面

//隱藏返回鍵,true隱藏,false為顯示
PlayerView hideBack(boolean isHide)
//隱藏選單鍵,true隱藏,false為顯示
PlayerView hideMenu(boolean isHide)
//隱藏解析度按鈕,true隱藏,false為顯示
PlayerView hideSteam(boolean isHide)
//隱藏旋轉按鈕,true隱藏,false為顯示
PlayerView hideRotation(boolean isHide)
//隱藏全屏按鈕,true隱藏,false為顯示
PlayerView hideFullscreen(boolean isHide)
//隱藏中間播放按鈕,ture為隱藏,false為不做隱藏處理,但不是顯示
PlayerView hideCenterPlayer(boolean isHide)

4.視訊移動流量是播放提醒

//設定2/3/4/5G和WiFi網路型別提示 true為進行2/3/4/5G網路型別提示 false 不進行網路型別提示
PlayerView setNetWorkTypeTie(boolean isGNetWork)

5.視訊載入前顯示縮圖

player.showThumbnail(new OnShowThumbnailListener() {
                @Override
                public void onShowThumbnail(ImageView ivThumbnail) {
                    /**載入前顯示的縮圖*/
                    Glide.with(mContext)
                            .load("http://pic2.nipic.com/20090413/406638_125424003_2.jpg")
                            .placeholder(R.color.cl_default)
                            .error(R.color.cl_error)
                            .into(ivThumbnail);
                }
            })

6.預設顯示上下操作欄bar

//設定是否禁止隱藏bar,true為一直顯示,false為點選可以隱藏或顯示
PlayerView setForbidHideControlPanl(boolean flag)

7.設定播放出錯後嘗試重連的方式和重連的時間

//設定自動重連的模式或者重連時間,isAuto true 出錯重連,false出錯不重連,connectTime重連的時間
setAutoReConnect(boolean isAuto, int connectTime)

8.視訊介面的旋轉

當前預設使用setPlayerRotation方法為90、270、0輪詢切換,如果需要指定角度旋轉可以使用setPlayerRotation方法

//旋轉角度
PlayerView setPlayerRotation()
//旋轉指定角度
PlayerView setPlayerRotation(int rotation)

自定義視訊介面

可以複製以下佈局內容到自己的專案中,注意已有的id不能修改或刪除,可以增加view,可以對以下佈局內容調整顯示位置或者自行隱藏

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/app_video_box"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    android:orientation="vertical">


    <com.dou361.ijkplayer.widget.IjkVideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/ll_bg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        android:orientation="vertical">

        <!-- 封面顯示-->
        <ImageView
            android:id="@+id/iv_trumb"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:visibility="visible"/>
    </LinearLayout>

    <!--重新播放-->
    <LinearLayout
        android:id="@+id/app_video_replay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#33000000"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">
        <!-- 播放狀態-->
        <TextView
            android:id="@+id/app_video_status_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/small_problem"
            android:textColor="@android:color/white"
            android:textSize="14dp"/>

        <ImageView
            android:id="@+id/app_video_replay_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="8dp"
            android:src="@drawable/simple_player_circle_outline_white_36dp"/>
    </LinearLayout>
    <!-- 網路提示-->
    <LinearLayout
        android:id="@+id/app_video_netTie"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#33000000"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:gravity="center"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            android:text="您正在使用行動網路播放視訊\n可能產生較高流量費用"
            android:textColor="@android:color/white"/>

        <TextView
            android:id="@+id/app_video_netTie_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/simple_player_btn"
            android:gravity="center"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            android:text="繼續"
            android:textColor="@android:color/white"/>
    </LinearLayout>

    <!--載入中-->
    <LinearLayout
        android:id="@+id/app_video_loading"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">

        <ProgressBar
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:indeterminateBehavior="repeat"
            android:indeterminateOnly="true"/>
        <TextView
            android:id="@+id/app_video_speed"
            android:layout_width="wrap_content"
            android:layout_marginTop="4dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:visibility="gone"
            android:text="188Kb/s"
            android:textColor="@android:color/white"/>
    </LinearLayout>

    <!-- 中間觸控提示-->
    <include
        layout="@layout/simple_player_touch_gestures"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

    <!-- 頂部欄-->
    <include layout="@layout/simple_player_topbar"/>
    <!-- 底部欄-->
    <include
        layout="@layout/simple_player_controlbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"/>

    <!--聲音亮度控制-->
    <LinearLayout
        android:id="@+id/simple_player_settings_container"
        android:layout_width="250dp"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:background="#80000000"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:visibility="visible">

        <LinearLayout
            android:id="@+id/simple_player_volume_controller_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/qcloud_player_icon_audio_vol_mute"/>

            <SeekBar
                android:id="@+id/simple_player_volume_controller"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="150dp"
                android:layout_height="wrap_content"/>

            <ImageView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/qcloud_player_icon_audio_vol"/>
        </LinearLayout>

        <LinearLayout
            android:id="@+id/simple_player_brightness_controller_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:padding="5dp"
                android:src="@drawable/qcloud_player_icon_brightness"/>

            <SeekBar
                android:id="@+id/simple_player_brightness_controller"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="150dp"
                android:layout_height="wrap_content"/>

            <ImageView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/qcloud_player_icon_brightness"/>
        </LinearLayout>

    </LinearLayout>


    <!--解析度選擇-->
    <LinearLayout
        android:id="@+id/simple_player_select_stream_container"
        android:layout_width="150dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:background="#80000000"
        android:gravity="center_vertical"
        android:visibility="gone">

        <ListView
            android:id="@+id/simple_player_select_streams_list"
            android:layout_width="150dp"
            android:layout_height="wrap_content"/>
    </LinearLayout>


    <ImageView
        android:id="@+id/play_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginTop="8dp"
        android:src="@drawable/simple_player_center_play"/>

</RelativeLayout>

播放器PlayerView物件

PlayerView(Activity activity)

//生命週期方法回撥
PlayerView onPause()
PlayerView onResume()
PlayerView onDestroy()
PlayerView onConfigurationChanged(final Configuration newConfig)
boolean onBackPressed()
//顯示縮圖
PlayerView showThumbnail(OnShowThumbnailListener onShowThumbnailListener)
//設定播放資訊監聽回撥
PlayerView setOnInfoListener(IMediaPlayer.OnInfoListener onInfoListener)
//設定播放器中的返回鍵監聽
PlayerView setPlayerBackListener(OnPlayerBackListener listener)
//設定控制面板顯示隱藏監聽
PlayerView setOnControlPanelVisibilityChangListenter(OnControlPanelVisibilityChangeListener listener)
//百分比顯示切換
PlayerView toggleAspectRatio()
//設定播放區域拉伸型別
PlayerView setScaleType(int showType)
//旋轉角度
PlayerView setPlayerRotation()
//旋轉指定角度
PlayerView setPlayerRotation(int rotation)
//設定播放地址包括視訊清晰度列表對應地址列表
PlayerView setPlaySource(List<VideoijkBean> list)
//設定播放地址單個視訊VideoijkBean
PlayerView setPlaySource(VideoijkBean videoijkBean)
//設定播放地址單個視訊地址時帶流名稱
PlayerView setPlaySource(String stream, String url)
//設定播放地址單個視訊地址時
PlayerView setPlaySource(String url)
//自動播放
PlayerView autoPlay(String path)
//開始播放
PlayerView startPlay()
//設定視訊名稱
PlayerView setTitle(String title)
//選擇要播放的流
PlayerView switchStream(int index)
//暫停播放
PlayerView pausePlay()
//停止播放
PlayerView stopPlay()
//設定播放位置
PlayerView seekTo(int playtime)
//獲取當前播放位置
int getCurrentPosition()
//獲取視訊播放總時長
long getDuration()
//設定2/3/4/5G和WiFi網路型別提示 true為進行2/3/4/5G網路型別提示 false 不進行網路型別提示
PlayerView setNetWorkTypeTie(boolean isGNetWork)
//是否僅僅為全屏
PlayerView setOnlyFullScreen(boolean isFull)
//設定是否禁止雙擊
PlayerView setForbidDoulbeUp(boolean flag)
//設定是否禁止隱藏bar
PlayerView setForbidHideControlPanl(boolean flag)
//當前播放的是否是直播
boolean isLive()
//是否禁止觸控
PlayerView forbidTouch(boolean forbidTouch)
//隱藏所有狀態介面
PlayerView hideAllUI()
獲取頂部控制barview
View getTopBarView()
//獲取底部控制barview
View getBottonBarView()
//獲取旋轉view
ImageView getRationView()
//獲取返回view
ImageView getBackView()
//獲取選單view
ImageView getMenuView()
//獲取全屏按鈕view
ImageView getFullScreenView()
//獲取底部bar的播放view
ImageView getBarPlayerView()
//獲取中間的播放view
ImageView getPlayerView()
//隱藏返回鍵,true隱藏,false為顯示
PlayerView hideBack(boolean isHide)
//隱藏選單鍵,true隱藏,false為顯示
PlayerView hideMenu(boolean isHide)
//隱藏解析度按鈕,true隱藏,false為顯示
PlayerView hideSteam(boolean isHide)
//隱藏旋轉按鈕,true隱藏,false為顯示
PlayerView hideRotation(boolean isHide)
//隱藏全屏按鈕,true隱藏,false為顯示
PlayerView hideFullscreen(boolean isHide)
//隱藏中間播放按鈕,ture為隱藏,false為不做隱藏處理,但不是顯示
PlayerView hideCenterPlayer(boolean isHide)
//顯示或隱藏操作面板
PlayerView operatorPanl()
//全屏切換
PlayerView toggleFullScreen()
//設定自動重連的模式或者重連時間,isAuto true 出錯重連,false出錯不重連,connectTime重連的時間
setAutoReConnect(boolean isAuto, int connectTime)
//進度條和時長顯示的方向切換
PlayerView toggleProcessDurationOrientation()
//設定進度條和時長顯示的方向,預設為上下顯示,PlayStateParams.PROCESS_PORTRAIT為上下顯示PlayStateParams.PROCESS_LANDSCAPE為左右顯示PlayStateParams.PROCESS_CENTER為中間兩邊樣式
setProcessDurationOrientation(int portrait)
//顯示選單設定
showMenu()
//獲取介面方向
int getScreenOrientation()
//顯示載入網速
PlayerView setShowSpeed(boolean isShow)

ijkplayer封裝的視訊播放資訊返回碼監聽

通過setOnInfoListener去監聽

/*
 * Do not change these values without updating their counterparts in native
 */
int MEDIA_INFO_UNKNOWN = 1;//未知資訊
int MEDIA_INFO_STARTED_AS_NEXT = 2;//播放下一條
int MEDIA_INFO_VIDEO_RENDERING_START = 3;//視訊開始整備中
int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;//視訊日誌跟蹤
int MEDIA_INFO_BUFFERING_START = 701;//開始緩衝中
int MEDIA_INFO_BUFFERING_END = 702;//緩衝結束
int MEDIA_INFO_NETWORK_BANDWIDTH = 703;//網路頻寬,網速方面
int MEDIA_INFO_BAD_INTERLEAVING = 800;//
int MEDIA_INFO_NOT_SEEKABLE = 801;//不可設定播放位置,直播方面
int MEDIA_INFO_METADATA_UPDATE = 802;//
int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;//不支援字幕
int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;//字幕超時

int MEDIA_INFO_VIDEO_INTERRUPT= -10000;//資料連線中斷
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//視訊方向改變
int MEDIA_INFO_AUDIO_RENDERING_START = 10002;//音訊開始整備中

int MEDIA_ERROR_UNKNOWN = 1;//未知錯誤
int MEDIA_ERROR_SERVER_DIED = 100;//服務掛掉
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//資料錯誤沒有有效的回收
int MEDIA_ERROR_IO = -1004;//IO錯誤
int MEDIA_ERROR_MALFORMED = -1007;
int MEDIA_ERROR_UNSUPPORTED = -1010;//資料不支援
int MEDIA_ERROR_TIMED_OUT = -110;//資料超時