完整視頻播放器封裝庫,仿優酷

目錄介紹

  • 1.關於此視頻封裝庫介紹
  • 1.1 可以知足那些業務需求
  • 1.2 對比同類型的庫有哪些優點
  • 2.關於使用方法說明
  • 2.1 關於gradle引用說明
  • 2.2 添加布局
  • 2.3 最簡單的視頻播放器參數設定
  • 2.4 注意的問題
  • 2.5 關於開源庫中的類說明
  • 3.關於播放類型說明
  • 3.1 普通視頻播放
  • 3.2 list頁面視頻播放
  • 3.3 小窗口視頻播放
  • 3.4 相似愛奇藝,優酷會員試看視頻播放
  • 3.5 關於封裝庫中日誌打印
  • 4.關於相關方法說明
  • 4.1 關於VideoPlayer類[播放器]中方法說明
  • 4.2 關於VideoPlayerController類[控制器]中方法說明
  • 4.3 關於對象的銷燬
  • 5.關於封裝的思路
  • 5.1 參考的案例思路
  • 5.2 封裝的基本思路
  • 5.3 關於窗口切換分析
  • 5.4 關於VideoPlayerManager視頻播放器管理器分析
  • 5.5 關於VideoPlayerController視頻控制器分析
  • 5.6 關於InterVideoPlayer接口分析
  • 6.關於如何自定義你想要的視頻播放模式
  • 6.1 自定義視頻播放器
  • 7.關於效果圖的展現
  • 7.1 效果圖以下所示
  • 8.關於遇到的問題說明
  • 8.1 視頻難點
  • 8.2 遇到的bug
  • 8.3 後期須要實現的功能
  • 9.關於版本更新說明
  • 9.1 V1.0.0 更新於2017年9月4日
  • 9.2 V1.0.1 更新於2017年11月18日
  • 9.3 v1.1.0 更新於2018年1月15日
  • 10.關於參考文檔說明
  • 10.1 參考的項目
  • 10.2 參考的博客
  • 11.關其餘說明
  • 11.1 目前市場流行的視頻框架
  • 11.2 如何選擇合適的框架
  • 11.3 關於個人我的博客和站點

0.備註

  • 仿照愛奇藝,優酷播放器寫的,十分感謝GitHub上大神前輩們的開源案例和思路。
  • 支持插入廣告,設置視頻觀看權限,觀看完後登陸或者購買會員。我看到在star較多的項目issues中,有些人正好須要這個案例,庫集成後直接經過代碼調用便可,靈活且拓展性強。
  • 因爲調到作視頻的部門,所以此部分代碼會持續更新,也歡迎同行提bug或者問題
  • 若是你以爲還能夠,給個star吧!我也在持續學習中!!!
  • 項目地址:https://github.com/yangchong211/YCVideoPlayer

1.關於此視頻封裝庫介紹

1.1 可以知足那些業務需求

A基礎功能php

  • 1.1.1 可以自定義視頻加載loading類型,設置視頻標題,設置視頻底部圖片,設置播放時長等基礎功能
  • 1.1.2 能夠切換播放器的視頻播放狀態,播放錯誤,播放未開始,播放開始,播放準備中,正在播放,暫停播放,正在緩衝等等狀態
  • 1.1.3 能夠自由設置播放器的播放模式,好比,正常播放,全屏播放,和小屏幕播放。其中全屏播放支持旋轉屏幕。
  • 1.1.4 能夠支持多種視頻播放類型,好比,原生封裝視頻播放器,還有基於ijkplayer封裝的播放器。
  • 1.1.5 能夠設置是否隱藏播放音量,播放進度,播放亮度等,能夠經過拖動seekBar改變視頻進度。還支持設置n秒後不操做則隱藏頭部和頂部佈局功能

-html

B高級功能android

  • 1.1.6 支持一遍播放一遍緩衝的功能,其中緩衝包括兩部分,第一種是播放過程當中緩衝,第二種是暫停過程當中緩衝
  • 1.1.7 基於ijkplayer的封裝播放器,支持多種格式視頻播放
  • 1.1.8 能夠設置是否記錄播放位置,設置播放速度,設置屏幕比例
  • 1.1.9 支持滑動改變音量【屏幕右邊】,改變屏幕亮度【屏幕左邊】,支持切換視頻清晰度模式
  • 1.1.0 支持list頁面中視頻播放,滾動後暫停播放,播放能夠自由設置是否記錄狀態。而且還支持刪除視頻播放位置狀態。

-git

C拓展功能github

  • C1產品需求:相似優酷,愛奇藝視頻播放器部分邏輯。好比若是用戶沒有登陸也沒有看視頻權限,則提示試看視頻[自定義佈局];若是用戶沒有登陸可是有看視頻權限,則正常觀看;若是用戶登陸,可是沒有充值會員,部分須要權限視頻則進入試看模式,試看結束後彈出充值會員界面;若是用戶餘額不足,好比餘額只有99元,可是視頻觀看要199元,則又有其餘提示。
  • C2自身需求:好比封裝好了視頻播放庫,那麼點擊視頻上登陸按鈕則跳到登陸頁面;點擊充值會員頁面也跳到充值頁面。這個經過定義接口,可讓使用者經過方法調用,靈活處理點擊事件。
  • C.1.1 實現了上面兩個需求,靈活可拓展性強。
  • C.1.2 對於設置視頻的寬高,建議設置成4:3或者16:9或者經常使用比例,若是不是經常使用比例,則可能會有黑邊。其中黑邊的背景能夠設置
  • C.1.3 能夠設置播放有權限的視頻時的各類文字描述,而沒有把它寫在封裝庫中,使用者本身設定
  • C.1.4 鎖定屏幕功能

-segmentfault

D待添加功能緩存

  • D.1.1 能夠支持屏幕截圖功能,視頻添加水印效果
  • D.1.2 支持彈幕功能
  • D.1.3 後期待定

1.2 對比同類型的庫有哪些優點

1.2.1目前僅僅查了下GitHub上項目網絡

  • 目前GitHub上比較流行的庫
  • 至於官方庫就不說了,jiecao的庫是基於ijkplayer視頻框架,目前封裝庫有許多,下面幾個只是star比較多,其中jiecao庫比較相似。
ijkplayer官方庫
https://github.com/Bilibili/ijkplayer
Vitamio官方庫
https://github.com/yixia/VitamioBundle
以jiecao爲例的封裝庫
https://github.com/JasonChow1989/JieCaoVideoPlayer-develop          2年前
https://github.com/open-android/JieCaoVideoPlayer                   1年前
https://github.com/lipangit/JiaoZiVideoPlayer                       4個月前
https://github.com/CarGuo/GSYVideoPlayer
其餘庫
https://github.com/danylovolokh/VideoPlayerManager

-app

1.2.2 具備的優點框架

  • A.代碼佈局更加簡潔,並且無多餘代碼
  • B.幾乎沒有多少淡黃色警告,關於註釋,經過使用阿里編碼插件檢測後更加規範,我對代碼有潔癖
  • C.視頻播放器[負責播放],視頻控制器[負責視頻播放各類點擊或者屬性設置操做],控制器抽象類[定義屬性抽象類,供子類實現],其餘能夠看代碼。結構分層上比較清晰
  • D.幾乎全部的方法或者重要的成員或者局部變量都有相關的註釋,註釋的內容很是詳細
  • E.關於視頻屬性設置或者按鈕點擊事件,均可以經過設置相關方法靈活實現。
  • 首先這些庫封裝的思路和代碼都不錯,我也是借鑑他們的思路,在他們的思路上改進而封裝的。
  • 相比來講代碼結構更加清晰,舉幾個例子
  • 針對視頻播放頁面佈局,因爲視頻播放狀態衆多,我封裝這庫不一樣狀態佈局有十幾種,許多庫的視圖佈局沒註釋,顯示比較臃腫,若是修改或者定位,不熟悉或者很久不操做,都要花時間找。展現個人佈局代碼
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--https://github.com/yangchong211-->
    <!--若是你以爲好,請給個star,讓更多人使用,避免重複造輪子-->
    <!--底圖,主要是顯示視頻縮略圖-->
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:visibility="visible"/>
    <!--加載動畫view-->
    <include layout="@layout/custom_video_player_loading"/>
    <!--改變播放位置-->
    <include layout="@layout/custom_video_player_change_position"/>
    <!--改變亮度-->
    <include layout="@layout/custom_video_player_change_brightness"/>
    <!--改變聲音-->
    <include layout="@layout/custom_video_player_change_volume"/>
    <!--播放完成,你也能夠自定義-->
    <include layout="@layout/custom_video_player_completed"/>
    <!--播放錯誤-->
    <include layout="@layout/custom_video_player_error"/>
    <!--頂部控制區-->
    <include layout="@layout/custom_video_player_top"/>
    <!--底部控制區-->
    <include layout="@layout/custom_video_player_bottom"/>
    <!--右下角初始顯示的總時長-->
    <TextView
        android:id="@+id/length"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_marginBottom="12dp"
        android:layout_marginEnd="8dp"
        android:padding="4dp"
        android:visibility="visible"
        android:text="00:00"
        android:textColor="@android:color/white"
        android:textSize="12sp"/>
    <!--中間開始播放按鈕-->
    <ImageView
        android:id="@+id/center_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_player_center_start"
        android:visibility="visible"/>
    <!--試看按鈕-->
    <ImageView
        android:id="@+id/iv_try_see"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/selector_try_see"
        android:visibility="gone"/>
    <!--試看佈局,非會員顯示該佈局-->
    <include layout="@layout/custom_video_player_try_see"/>
</RelativeLayout>

2.關於使用方法說明

2.1 關於gradle引用說明

  • 2.1.1直接引用這段代碼就能夠
compile 'cn.yc:YCVideoPlayerLib:2.2'

2.2 添加布局

  • 注意,在實際開發中,因爲Android手機碎片化比較嚴重,分辨率太多了,建議靈活設置佈局的寬高比爲4:3或者16:9或者你認爲合適的,能夠用代碼設置。
  • 若是寬高比變形,則會有黑邊
<org.yczbj.ycvideoplayerlib.VideoPlayer
    android:id="@+id/video_player"
    android:layout_width="match_parent"
    android:layout_height="240dp"/>

2.3 最簡單的視頻播放器參數設定

  • 2.3.1 這個是最簡單視頻播放器的設置參數代碼
//設置播放類型
    // IjkPlayer or MediaPlayer
    videoPlayer1.setPlayerType(VideoPlayer.TYPE_NATIVE);
    //網絡視頻地址
    String videoUrl = DataUtil.getVideoListData().get(0).getVideoUrl();
    //設置視頻地址和請求頭部
    videoPlayer1.setUp(videoUrl, null);
    //是否從上一次的位置繼續播放
    videoPlayer1.continueFromLastPosition(true);
    //設置播放速度
    videoPlayer1.setSpeed(1.0f);
    //建立視頻控制器
    VideoPlayerController controller = new VideoPlayerController(this);
    controller.setTitle("辦快來圍觀拉,自定義視頻播放器能夠播放視頻拉");
    //設置視頻時長
    controller.setLength(98000);
    //設置5秒不操做後則隱藏頭部和底部佈局視圖
    controller.setHideTime(5000);
    //controller.setImage(R.drawable.image_default);
    ImageUtil.loadImgByPicasso(this, R.drawable.image_default, R.drawable.image_default, controller.imageView());
    //設置視頻控制器
    videoPlayer1.setController(controller);
  • 2.3.2 關於模仿愛奇藝登陸會員權限功能代碼
//設置視頻加載緩衝時加載窗的類型,多種類型
    controller.setLoadingType(2);
    ArrayList<String> content = new ArrayList<>();
    content.add("試看結束,yc觀看所有內容請開通會員1111。");
    content.add("試看結束,yc觀看所有內容請開通會員2222。");
    content.add("試看結束,yc觀看所有內容請開通會員3333。");
    content.add("試看結束,yc觀看所有內容請開通會員4444。");
    controller.setMemberContent(content);
    controller.setHideTime(5000);
    //設置設置會員權限類型,第一個參數是否登陸,第二個參數是否有權限看,第三個參數試看完後展現的文字內容,第四個參數是否保存進度位置
    controller.setMemberType(false,false,3,true);
    controller.imageView().setBackgroundResource(R.color.blackText);
    //ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
    //設置試看結束後,登陸或者充值會員按鈕的點擊事件
    controller.setOnMemberClickListener(new OnMemberClickListener() {
        @Override
        public void onClick(int type) {
            switch (type){
                case ConstantKeys.Gender.LOGIN:
                    //調到用戶登陸也米娜
                    startActivity(MeLoginActivity.class);
                    break;
                case ConstantKeys.Gender.MEMBER:
                    //調到用戶充值會員頁面
                    startActivity(MeMemberActivity.class);
                    break;
                default:
                    break;
            }
        }
    });
  • 2.3.3其餘設置,讓體驗更好
  • 若是是在Activity中的話,建議設置下面這段代碼
@Override
    protected void onStop() {
        super.onStop();
        VideoPlayerManager.instance().releaseVideoPlayer();
    }

    @Override
    public void onBackPressed() {
        if (VideoPlayerManager.instance().onBackPressed()) return;
        super.onBackPressed();
    }
  • 若是是在Fragment中的話,建議設置下面這段代碼
//在宿主Activity中設置代碼以下
    @Override
    protected void onStop() {
        super.onStop();
        VideoPlayerManager.instance().releaseVideoPlayer();
    }

    @Override
    public void onBackPressed() {
        if (VideoPlayerManager.instance().onBackPressed()) return;
        super.onBackPressed();
    }

    //--------------------------------------------------

    //在此Fragment中設置代碼以下
    @Override
    public void onStop() {
        super.onStop();
        VideoPlayerManager.instance().releaseVideoPlayer();
    }

2.4 注意的問題

  • 2.4.1若是是全屏播放,則須要在清單文件中設置當前activity的屬性值
  • android:configChanges 保證了在全屏的時候橫豎屏切換不會執行Activity的相關生命週期,打斷視頻的播放
  • android:screenOrientation 固定了屏幕的初始方向
  • 這兩個變量控制全屏後和退出全屏的屏幕方向
<activity android:name=".ui.test2.TestMyActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:screenOrientation="portrait"/>

2.5 關於開源庫中的類說明

  • image

3.關於播放類型說明

3.1 普通視頻播放

  • 3.1.1 這一步操做能夠直接看第二部份內容——關於使用方法說明

3.2 list頁面視頻播放

  • 3.2.1如何在list頁面設置視頻
  • 第一步:在activity或者fragment中
recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);
    VideoAdapter adapter = new VideoAdapter(this, DataUtil.getVideoListData());
    recyclerView.setAdapter(adapter);
    //注意:下面這個方法不能漏掉
    recyclerView.setRecyclerListener(new RecyclerView.RecyclerListener() {
        @Override
        public void onViewRecycled(RecyclerView.ViewHolder holder) {
            VideoPlayer videoPlayer = ((VideoAdapter.VideoViewHolder) holder).mVideoPlayer;
            if (videoPlayer == VideoPlayerManager.instance().getCurrentVideoPlayer()) {
                VideoPlayerManager.instance().releaseVideoPlayer();
            }
        }
    });
  • 第二步:在RecyclerView的適配器Adapter中
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoViewHolder> {

    private Context mContext;
    private List<Video> mVideoList;

    VideoAdapter(Context context, List<Video> videoList) {
        mContext = context;
        mVideoList = videoList;
    }

    @Override
    public VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_test_my_video, parent, false);
        VideoViewHolder holder = new VideoViewHolder(itemView);
        //建立視頻播放控制器,主要只要建立一次就能夠呢
        VideoPlayerController controller = new VideoPlayerController(mContext);
        holder.setController(controller);
        return holder;
    }

    @Override
    public void onBindViewHolder(VideoViewHolder holder, int position) {
        Video video = mVideoList.get(position);
        holder.bindData(video);
    }

    @Override
    public int getItemCount() {
        return mVideoList==null ? 0 : mVideoList.size();
    }

    class VideoViewHolder extends RecyclerView.ViewHolder {

        VideoPlayerController mController;
        VideoPlayer mVideoPlayer;

        VideoViewHolder(View itemView) {
            super(itemView);
            mVideoPlayer = (VideoPlayer) itemView.findViewById(R.id.nice_video_player);
            // 將列表中的每一個視頻設置爲默認16:9的比例
            ViewGroup.LayoutParams params = mVideoPlayer.getLayoutParams();
            // 寬度爲屏幕寬度
            params.width = itemView.getResources().getDisplayMetrics().widthPixels;
            // 高度爲寬度的9/16
            params.height = (int) (params.width * 9f / 16f);
            mVideoPlayer.setLayoutParams(params);
        }

        /**
         * 設置視頻控制器參數
         * @param controller            控制器對象
         */
        void setController(VideoPlayerController controller) {
            mController = controller;
            mVideoPlayer.setController(mController);
        }

        void bindData(Video video) {
            mController.setTitle(video.getTitle());
            mController.setLength(video.getLength());
            Glide.with(itemView.getContext())
                    .load(video.getImageUrl())
                    .placeholder(R.drawable.image_default)
                    .crossFade()
                    .into(mController.imageView());
            mVideoPlayer.setUp(video.getVideoUrl(), null);
        }
    }
}

3.3 小窗口視頻播放

  • 3.3.1建議在設置小窗口先先判斷視頻播放器是否開始播放
if (videoPlayer.isIdle()) {
        Toast.makeText(this, "要點擊播放後才能進入小窗口", Toast.LENGTH_SHORT).show();
    } else {
        videoPlayer.enterTinyWindow();
    }

3.4 相似愛奇藝,優酷會員試看視頻播放

  • 3.4.1 能夠參考——2.3.2 關於模仿愛奇藝登陸會員權限功能代碼

3.5 關於封裝庫中日誌打印

  • 3.5.1關於封裝庫中日誌打印設置
  • 若是上線產品後不想打印日誌,能夠在初始化時設置,注意須要在初始化播放器以前設置
//若是不想打印庫中的日誌,能夠設置
    VideoLogUtil.isLog = false;
  • 3.5.3關於日誌工具類代碼
public class VideoLogUtil {
    
        private static final String TAG = "YCVideoPlayer";
        public static boolean isLog = true;
    
        static void d(String message) {
            if(isLog){
                Log.d(TAG, message);
            }
    
        }
    
        static void i(String message) {
            if(isLog){
                Log.i(TAG, message);
            }
    
        }
    
        static void e(String message, Throwable throwable) {
            if(isLog){
                Log.e(TAG, message, throwable);
            }
        }
    }

4.關於相關方法說明

4.1 關於VideoPlayer類中方法說明

  • 4.1.1 關於必定須要這四步
//設置播放類型
    // IjkPlayer or MediaPlayer
    videoPlayer1.setPlayerType(VideoPlayer.TYPE_NATIVE);
    //設置視頻地址和請求頭部
    videoPlayer1.setUp(videoUrl, null);
    //建立視頻控制器
    VideoPlayerController controller = new VideoPlayerController(this);
    //設置視頻控制器
    videoPlayer1.setController(controller);
  • 4.1.2 關於VideoPlayer中設置屬性方法
//設置播放類型
    // MediaPlayer
    videoPlayer.setPlayerType(VideoPlayer.TYPE_NATIVE);
    // IjkPlayer
    videoPlayer.setPlayerType(VideoPlayer.TYPE_IJK);
    //網絡視頻地址
    String videoUrl = DataUtil.getVideoListData().get(1).getVideoUrl();
    //設置視頻地址和請求頭部
    videoPlayer.setUp(videoUrl, null);
    //是否從上一次的位置繼續播放
    videoPlayer.continueFromLastPosition(false);
    //設置播放速度
    videoPlayer.setSpeed(1.0f);
    //設置播放位置
    //videoPlayer.seekTo(3000);
    //設置音量
    videoPlayer.setVolume(50);

    //設置全屏播放
    videoPlayer.enterFullScreen();
    //設置小屏幕播放
    videoPlayer.enterTinyWindow();
    //退出全屏
    videoPlayer.exitFullScreen();
    //退出小窗口播放
    videoPlayer.exitTinyWindow();
    //釋放,內部的播放器被釋放掉,同時若是在全屏、小窗口模式下都會退出
    videoPlayer.release();
    //釋放播放器,注意必定要判斷對象是否爲空,加強嚴謹性
    videoPlayer.releasePlayer();
  • 4.1.3 關於VideoPlayer中獲取屬性方法
//是否從上一次的位置繼續播放,沒必要須
        videoPlayer.continueFromLastPosition(false);
        //獲取最大音量
        int maxVolume = videoPlayer.getMaxVolume();
        //獲取音量值
        int volume = videoPlayer.getVolume();
        //獲取持續時長
        long duration = videoPlayer.getDuration();
        //獲取播放位置
        long currentPosition = videoPlayer.getCurrentPosition();
        //獲取緩衝區百分比
        int bufferPercentage = videoPlayer.getBufferPercentage();
        //獲取播放速度
        float speed = videoPlayer.getSpeed(1);
  • 4.1.4 關於VideoPlayer中設置播放狀態方法
//開始播放
    videoPlayer.start();
    //開始播放,從某位置播放
    videoPlayer.start(3000);
    //從新播放
    videoPlayer.restart();
    //暫停播放
    videoPlayer.pause();
  • 4.1.5 關於VideoPlayer中獲取播放狀態方法
//判斷是否開始播放
    boolean idle = videoPlayer.isIdle();
    //判斷視頻是否播放準備中
    boolean preparing = videoPlayer.isPreparing();
    //判斷視頻是否準備就緒
    boolean prepared = videoPlayer.isPrepared();
    //判斷視頻是否正在緩衝
    boolean bufferingPlaying = videoPlayer.isBufferingPlaying();
    //判斷是不是否緩衝暫停
    boolean bufferingPaused = videoPlayer.isBufferingPaused();
    //判斷視頻是否暫停播放
    boolean paused = videoPlayer.isPaused();
    //判斷視頻是否正在播放
    boolean playing = videoPlayer.isPlaying();
    //判斷視頻是否播放錯誤
    boolean error = videoPlayer.isError();
    //判斷視頻是否播放完成
    boolean completed = videoPlayer.isCompleted();
    //判斷視頻是否播放全屏
    boolean fullScreen = videoPlayer.isFullScreen();
    //判斷視頻是否播放小窗口
    boolean tinyWindow = videoPlayer.isTinyWindow();
    //判斷視頻是否正常播放
    boolean normal = videoPlayer.isNormal();

4.2 關於VideoPlayerController類[控制器]中方法說明

  • 4.2.1 關於控制器方法
//建立視頻控制器
    VideoPlayerController controller = new VideoPlayerController(this);
    //設置視頻標題
    controller.setTitle("高仿優酷視頻播放頁面");
    //設置視頻時長
    //controller.setLength(98000);
    //設置視頻加載緩衝時加載窗的類型,多種類型
    controller.setLoadingType(2);
    ArrayList<String> content = new ArrayList<>();
    content.add("試看結束,觀看所有內容請開通會員1111。");
    content.add("試看結束,觀看所有內容請開通會員2222。");
    content.add("試看結束,觀看所有內容請開通會員3333。");
    content.add("試看結束,觀看所有內容請開通會員4444。");
    //設置會員權限話術內容
    controller.setMemberContent(content);
    //設置不操做後,5秒自動隱藏頭部和底部佈局
    controller.setHideTime(5000);
    //設置設置會員權限類型,第一個參數是否登陸,第二個參數是否有權限看,第三個參數試看完後展現的文字內容,第四個參數是否保存進度位置
    controller.setMemberType(false,false,3,true);
    //設置背景圖片
    controller.imageView().setBackgroundResource(R.color.blackText);
    //ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
    //設置試看結束後,登陸或者充值會員按鈕的點擊事件
    controller.setOnMemberClickListener(new OnMemberClickListener() {
        @Override
        public void onClick(int type) {
            switch (type){
                case ConstantKeys.Gender.LOGIN:
                    //調到用戶登陸也米娜
                    startActivity(MeLoginActivity.class);
                    break;
                case ConstantKeys.Gender.MEMBER:
                    //調到用戶充值會員頁面
                    startActivity(MeMemberActivity.class);
                    break;
                default:
                    break;
            }
        }
    });
    //設置視頻清晰度
    //videoPlayer.setClarity(list,720);
    //設置視頻控制器
    videoPlayer.setController(controller);

4.3 關於對象的銷燬

  • 4.3.1在VideoPlayer中如何釋放資源的呢?源代碼以下所示
@Override
    public void release() {
        // 保存播放位置
        if (isPlaying() || isBufferingPlaying() || isBufferingPaused() || isPaused()) {
            VideoPlayerUtils.savePlayPosition(mContext, mUrl, getCurrentPosition());
        } else if (isCompleted()) {
            //若是播放完成,則保存播放位置爲0,也就是初始位置
            VideoPlayerUtils.savePlayPosition(mContext, mUrl, 0);
        }
        // 退出全屏或小窗口
        if (isFullScreen()) {
            exitFullScreen();
        }
        if (isTinyWindow()) {
            exitTinyWindow();
        }
        mCurrentMode = MODE_NORMAL;

        // 釋放播放器
        releasePlayer();

        // 恢復控制器
        if (mController != null) {
            mController.reset();
        }
        // gc回收
        Runtime.getRuntime().gc();
    }
    //釋放播放器,注意必定要判斷對象是否爲空,加強嚴謹性
    @Override
    public void releasePlayer() {
        if (mAudioManager != null) {
            //放棄音頻焦點。使之前的焦點全部者(若是有的話)接收焦點。
            mAudioManager.abandonAudioFocus(null);
            //置空
            mAudioManager = null;
        }
        if (mMediaPlayer != null) {
            //釋放視頻焦點
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
        //從視圖中移除TextureView
        mContainer.removeView(mTextureView);
        if (mSurface != null) {
            mSurface.release();
            mSurface = null;
        }
        //若是SurfaceTexture不爲null,則釋放
        if (mSurfaceTexture != null) {
            mSurfaceTexture.release();
            mSurfaceTexture = null;
        }
        //設置狀態
        mCurrentState = STATE_IDLE;
    }

5.關於封裝的思路

5.1 參考的案例思路

  • 5.1.1目前參考的案例有
  • 能夠直接看下面的參考案例,有記錄
  • 5.1.2針對jiaozi代碼簡單分析
  • JZVideoPlayer爲繼承自FrameLayout實現的一個組合自定義View來實現了視頻播放器的View相關的內容。
  • JZVideoPlayerStandard則是繼承自JZVideoPlayer實現了一些自身的功能。
  • JZMediaManager是用來對於MediaPlayer的管理,對於MediaPlayer的一些監聽器方法的回調和TextrueView的相關回調處理。
  • JZVideoPlayerManager管理JZVideoPlayer
  • 和自定義相關的工做,最主要是先繼承JCVideoPlayerStandard
  • JZMediaSystem主要是實現系統的播放引擎
  • 不得不說,大神封裝代碼的思路以及代碼邏輯的確很強
  • 關於封裝庫其餘感覺
  • 第一:不過,感受大神更新頻率大高,並且沒有找到每次更新的日誌說明,不知道大神又解決了那些bug
  • 第二:黃色警告多,並且註釋少,由於視頻封裝庫不像通常庫,有時候需求不一樣,可拓展性要求高。除了本身繼承JCVideoPlayerStandard建立視頻播放器,其餘若是想改代碼,仍是有點複雜的。
  • 第三:關於使用雖然很簡單,可是在JZVideoPlayerStandard這個方法中,佈局的對象都是用public修飾,若是你要想本身甚至某個控件背景或者圖標等等,則要這樣應用。若是你不去看看源代碼中佈局名稱,你根本就不知道這個對象對應的是什麼東西。對於不一樣修飾符,要合適的,若是不合適,那麼就會有淡黃色警告。我看了buttonKnife,retrofit,阿里vlayout等等,能夠說黃色警告不多……
Picasso.with(this)
                .load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png")
                .into(jzVideo.thumbImageView);

5.2 封裝的基本思路

  • 5.2.1關於簡單的思路分析
  • a1.能夠把視頻播放和設置視頻屬性控制器分離,對於VideoPlayer中,各類UI狀態和操做反饋都封裝到VideoPlayerController控制器裏面。若是須要根據不一樣的項目需求來修改播放器的功能,就只重寫VideoPlayerController就能夠了。
  • a2.對於VideoPlayer這個類,能夠先建立一個幀佈局容器,而後在初始化的時候將視頻播放器控制器放到裏面,而後經過設置控制器來進行視頻播放
  • a3.當調用了開始播放的方法後,就初始化播放器,包括原生的,還有IjkMediaPlayer
  • a4.而基於IjkMediaPlayer的視頻播放,須要添加各類監聽事件,經過閱讀IMediaPlayer源碼能夠知道:能夠在這些監聽事件中添加各類對視頻的操做邏輯,具體能夠看代碼。
void setOnPreparedListener(IMediaPlayer.OnPreparedListener var1);
    void setOnCompletionListener(IMediaPlayer.OnCompletionListener var1);
    void setOnBufferingUpdateListener(IMediaPlayer.OnBufferingUpdateListener var1);
    void setOnSeekCompleteListener(IMediaPlayer.OnSeekCompleteListener var1);
    void setOnVideoSizeChangedListener(IMediaPlayer.OnVideoSizeChangedListener var1);
    void setOnErrorListener(IMediaPlayer.OnErrorListener var1);
    void setOnInfoListener(IMediaPlayer.OnInfoListener var1);
    void setOnTimedTextListener(IMediaPlayer.OnTimedTextListener var1);
  • a5.定義好了監聽事件後,就建立了播放,重置播放,暫停等各類方法

5.3 關於窗口切換分析

  • 5.3.1 關於窗口切換調用的代碼
//設置全屏播放
    videoPlayer.enterFullScreen();
    //設置小屏幕播放
    videoPlayer.enterTinyWindow();
    //退出全屏
    videoPlayer.exitFullScreen();
    //退出小窗口播放
    videoPlayer.exitTinyWindow();
    //釋放,內部的播放器被釋放掉,同時若是在全屏、小窗口模式下都會退出
    videoPlayer.release();
    //釋放播放器,注意必定要判斷對象是否爲空,加強嚴謹性
    videoPlayer.releasePlayer();

5.4 關於VideoPlayerManager視頻播放器管理器分析

  • 5.4.1能夠直接看源代碼,我對每一個方法都有詳細的註釋
public class VideoPlayerManager {

    private VideoPlayer mVideoPlayer;
    private static VideoPlayerManager sInstance;
    private VideoPlayerManager() {}
    //必定要使用單例模式,保證同一時刻只有一個視頻在播放,其餘的都是初始狀態
    public static synchronized VideoPlayerManager instance() {
        if (sInstance == null) {
            sInstance = new VideoPlayerManager();
        }
        return sInstance;
    }

    public VideoPlayer getCurrentVideoPlayer() {
        return mVideoPlayer;
    }

    void setCurrentVideoPlayer(VideoPlayer videoPlayer) {
        if (mVideoPlayer != videoPlayer) {
            releaseVideoPlayer();
            mVideoPlayer = videoPlayer;
        }
    }
    //當視頻正在播放或者正在緩衝時,調用該方法暫停視頻
    public void suspendVideoPlayer() {
        if (mVideoPlayer != null && (mVideoPlayer.isPlaying() || mVideoPlayer.isBufferingPlaying())) {
            mVideoPlayer.pause();
        }
    }
    //當視頻暫停時或者緩衝暫停時,調用該方法從新開啓視頻播放
    public void resumeVideoPlayer() {
        if (mVideoPlayer != null && (mVideoPlayer.isPaused() || mVideoPlayer.isBufferingPaused())) {
            mVideoPlayer.restart();
        }
    }
    //釋放,內部的播放器被釋放掉,同時若是在全屏、小窗口模式下都會退出
    public void releaseVideoPlayer() {
        if (mVideoPlayer != null) {
            mVideoPlayer.release();
            mVideoPlayer = null;
        }
    }
     //處理返回鍵邏輯.若是是全屏,則退出全屏 若是是小窗口,則退出小窗口
    public boolean onBackPressed() {
        if (mVideoPlayer != null) {
            if (mVideoPlayer.isFullScreen()) {
                return mVideoPlayer.exitFullScreen();
            } else if (mVideoPlayer.isTinyWindow()) {
                return mVideoPlayer.exitTinyWindow();
            }
        }
        return false;
    }
}

5.5 關於VideoPlayerController視頻控制器分析

  • 5.5.1VideoPlayerController的做用
  • 播放控制界面上,播放、暫停、播放進度、緩衝動畫、全屏/小屏等觸發都是直接調用播放器對應的操做的。
  • 5.5.2VideoPlayerController的方法以下所示
//建立視頻控制器
    VideoPlayerController controller = new VideoPlayerController(this);
    //設置視頻標題
    controller.setTitle("高仿優酷視頻播放頁面");
    //設置視頻時長
    //controller.setLength(98000);
    //設置視頻加載緩衝時加載窗的類型,多種類型
    controller.setLoadingType(2);
    ArrayList<String> content = new ArrayList<>();
    content.add("試看結束,觀看所有內容請開通會員1111。");
    content.add("試看結束,觀看所有內容請開通會員2222。");
    content.add("試看結束,觀看所有內容請開通會員3333。");
    content.add("試看結束,觀看所有內容請開通會員4444。");
    //設置會員權限話術內容
    controller.setMemberContent(content);
    //設置不操做後,5秒自動隱藏頭部和底部佈局
    controller.setHideTime(5000);
    //設置設置會員權限類型,第一個參數是否登陸,第二個參數是否有權限看,第三個參數試看完後展現的文字內容,第四個參數是否保存進度位置
    controller.setMemberType(false,false,3,true);
    //設置背景圖片
    controller.imageView().setBackgroundResource(R.color.blackText);
    //ImageUtil.loadImgByPicasso(this, R.color.blackText, R.drawable.image_default, controller.imageView());
    //設置試看結束後,登陸或者充值會員按鈕的點擊事件
    controller.setOnMemberClickListener(new OnMemberClickListener() {
        @Override
        public void onClick(int type) {
            switch (type){
                case ConstantKeys.Gender.LOGIN:
                    //調到用戶登陸也米娜
                    startActivity(MeLoginActivity.class);
                    break;
                case ConstantKeys.Gender.MEMBER:
                    //調到用戶充值會員頁面
                    startActivity(MeMemberActivity.class);
                    break;
                default:
                    break;
            }
        }
    });
    //設置視頻清晰度
    //videoPlayer.setClarity(list,720);
    //設置視頻控制器
    videoPlayer.setController(controller);

5.6 關於InterVideoPlayer接口分析

  • 5.6.1關於此接口方法有
  • 跟jiaozi代碼相似
/**
     * 設置視頻Url,以及headers
     *
     * @param url           視頻地址,能夠是本地,也能夠是網絡視頻
     * @param headers       請求header.
     */
    void setUp(String url, Map<String, String> headers);

    /**
     * 開始播放
     */
    void start();

    /**
     * 從指定的位置開始播放
     *
     * @param position      播放位置
     */
    void start(long position);

    /**
     * 從新播放,播放器被暫停、播放錯誤、播放完成後,須要調用此方法從新播放
     */
    void restart();

    /**
     * 暫停播放
     */
    void pause();

    /**
     * seek到制定的位置繼續播放
     *
     * @param pos 播放位置
     */
    void seekTo(long pos);

    /**
     * 設置音量
     *
     * @param volume 音量值
     */
    void setVolume(int volume);

    /**
     * 設置播放速度,目前只有IjkPlayer有效果,原生MediaPlayer暫不支持
     *
     * @param speed 播放速度
     */
    void setSpeed(float speed);

    /**
     * 開始播放時,是否從上一次的位置繼續播放
     *
     * @param continueFromLastPosition true 接着上次的位置繼續播放,false從頭開始播放
     */
    void continueFromLastPosition(boolean continueFromLastPosition);

6.關於如何自定義你想要的視頻播放模式

6.1 自定義視頻播放器

  • 6.1.1如何自定義本身的播放器
  • 第一步:首先繼承VideoPlayer這個類
  • 第二步:而後重寫部分你須要更改功能的方法,只須要選擇你須要重寫的方法便可。
  • 6.1.2代碼展現以下所示
public class YCVideoPlayer extends VideoPlayer {

        public YCVideoPlayer(Context context) {
            super(context);
        }

        @Override
        public void setUp(String url, Map<String, String> headers) {
            super.setUp(url, headers);
        }

        @Override
        public void setController(AbsVideoPlayerController controller) {
            super.setController(controller);
        }

        @Override
        public void setPlayerType(int playerType) {
            super.setPlayerType(playerType);
        }

        @Override
        public void continueFromLastPosition(boolean continueFromLastPosition) {
            super.continueFromLastPosition(continueFromLastPosition);
        }

        @Override
        public void setSpeed(float speed) {
            super.setSpeed(speed);
        }

        @Override
        public void start() {
            super.start();
        }

        @Override
        public void start(long position) {
            super.start(position);
        }

        @Override
        public void restart() {
            super.restart();
        }

        @Override
        public void pause() {
            super.pause();
        }

        @Override
        public void seekTo(long pos) {
            super.seekTo(pos);
        }

        @Override
        public void setVolume(int volume) {
            super.setVolume(volume);
        }

        @Override
        public boolean isIdle() {
            return super.isIdle();
        }

        @Override
        public boolean isPreparing() {
            return super.isPreparing();
        }

        @Override
        public boolean isPrepared() {
            return super.isPrepared();
        }

        @Override
        public boolean isBufferingPlaying() {
            return super.isBufferingPlaying();
        }

        @Override
        public boolean isBufferingPaused() {
            return super.isBufferingPaused();
        }

        @Override
        public boolean isPlaying() {
            return super.isPlaying();
        }

        @Override
        public boolean isPaused() {
            return super.isPaused();
        }

        @Override
        public boolean isError() {
            return super.isError();
        }
    }

7.關於效果圖的展現

7.1 效果圖以下所示

image
image
image
image
image
image
image
image
image
image
image

8.關於遇到的問題說明

8.1 視頻難點

  • 8.1.1 當視頻切換全屏或者從全屏切換到正常小屏幕時,如何管理activity的生命週期
  • 8.1.2 在列表list頁面,滑動顯示小窗口,那麼何時顯示小窗口呢?關於RecyclerView的滑動位移超出屏幕有沒有更好的解決辦法?
  • 8.1.2 當屏幕從全屏退出時,播放位置要滑到記錄的位置,代碼邏輯複雜,如何避免耦合度過高

8.2 遇到的bug

  • 8.2.1 當視頻切花時,如何避免視頻不卡頓
  • 8.2.2 在fragment中,當左右滑動出另外一個fragment中,視頻還在播放,怎麼樣處理這部分邏輯
  • 8.2.3 在顯示緩衝比時,網絡很差或者暫停緩衝時有問題,因此暫停尚未添加該功能
  • 8.2.4 播放進度條seekbar跳動問題,有人反映不是那麼順暢
  • 8.2.5 部分華爲手機播放視頻有問題,在找緣由
  • 8.2.6 在拖動時顯示當前幀的畫面圖片,相似優酷那個功能,最終仍是沒有實現

8.3 後期須要實現的功能

  • 8.3.1 若是有多集視頻,則添加上一集和下一集的功能
  • 8.3.2 拖動滑動條,顯示幀畫面
  • 8.3.3 實現彈幕功能
  • 8.4.4 有些手機播放有問題,測試找問題
  • 8.5.5 切換視頻清晰度有問題,是從新開始播放,由於切換清晰度時,調用的視頻連接是不一樣的。好比高清視頻和標準視頻連接是不一樣的,因此難以實現切換後記錄位置播放。可是看了下優酷,愛奇藝視頻,切換後是接着以前觀看的位置播放,這個須要思考下怎麼實現。歡迎同行給出好的建議。
  • 8.5.6 待定

9.關於版本更新說明

  • 9.1 V1.0.0 更新於2017年10月4日
  • 9.2 V1.0.1 更新於2017年11月18日
  • 9.3 v1.1.0 更新於2018年1月15日

10.關於參考文檔說明

10.1 參考的項目

  • 10.1.1參考的開源項目有
https://github.com/CarGuo/GSYVideoPlayer
https://github.com/danylovolokh/VideoPlayerManager
https://github.com/HotBitmapGG/bilibili-android-client
https://github.com/jjdxmashl/jjdxm_ijkplayer
https://github.com/JasonChow1989/JieCaoVideoPlayer-develop          2年前
https://github.com/open-android/JieCaoVideoPlayer                   1年前
https://github.com/lipangit/JiaoZiVideoPlayer                       4個月前
https://github.com/xiaoyanger0825/NiceVieoPlayer
https://github.com/curtis2/SuperVideoPlayer
https://github.com/tcking/GiraffePlayer

10.2 參考的博客

  • 10.2.1參考的博客有'
https://segmentfault.com/a/1190000011959615
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1213/2153.html
http://blog.csdn.net/junwang19891012/article/details/8444743
https://www.jianshu.com/p/420f7b14d6f6
http://blog.csdn.net/candicelijx/article/details/39495271

11.關其餘說明

11.1 目前市場流行的視頻框架

  • 1.Android原生VideoView
  • 2.Google 開源視頻播放框架 ExoPlayer
  • 3.Vitamio 視頻播放框架
  • 4.Bilibili 開源視頻播放框架ijkplayer

11.2 如何選擇

  • 11.2.1.Android原生VideoView
  • 1.1 VideoView 的使用很是簡單,播放視頻的步驟:

    • 在界面佈局文件中定義 VideoView 組件,或在程序中建立 VideoView 組件
    • 調用 VideoView 的以下兩個方法來加載指定的視頻:

      • setVidePath(String path):加載 path 文件表明的視頻
      • setVideoURI(Uri uri):加載 uri 所對應的視頻
    • 調用 VideoView 的 start()、stop()、psuse() 方法來控制視頻的播放
  • 11.2.2.Google 開源視頻播放框架 ExoPlayer
  • 2.1 框架地址:https://github.com/google/ExoPlayer
  • 2.2 用法

    • ExoPlayer 開源項目包含了 library 和 示例:

      • ExoPlayer library – 這部分是核心的庫
      • Demo app – 這部分是演示怎麼使用 ExoPlayer 的 Demo
    • ExoPlayer 庫的核心類是 ExoPlayer 類。該類維護了播放器的全局狀態 。好比如何獲取媒體數據,如何緩衝以及是怎樣的編碼格式。
    • ExoPlayer 基於 MediaCodec 和 AudioTrack 提供了默認的音視頻的 TrackRenderer 實現。全部的 renderers 都須要 SampleSource 對象,ExoPlayer 從 SampleSource 得到 media samples 用於播放。下圖展現了 ExoPlayer 是如何配置組合這些組件用於播放音視頻的。
    • standard-model
    • ExoPlayer 庫提供了一些不一樣類型的 SampleSource 實例:
    • ExtractorSampleSource – 用於 MP3,M4A,WebM,MPEG-TS 和 AAC;

      • ChunkSampleSource – 用於 DASH 和平滑流的播放;
      • HlsSampleSource – 用於 HLS 播放;
    • 在 ExoPlayer 的 Dome 中使用 DemoPlayer 對 ExoPlayer 進行了封裝,並提供了使用上述幾種 SampleSource 構建 TrackRenderer 的 Builder。

      • SmoothStreamingRendererBuilder
      • DashRendererBuilder
      • ExtractorRendererBuilder
    • 在使用的時候咱們根據不一樣的需求建立對應的 RendererBuilder,而後將 RendererBuilder 傳遞給 DemoPlayer 而後調用 DemoPlayer 的 setPlayWhenReady 方法。
  • 2.3 優缺點
  • ExoPlayer 相較於 MediaPlayer 有不少不少的優勢:

    • 支持動態的自適應流 HTTP (DASH) 和 平滑流,任何目前 MediaPlayer 支持的視頻格式(同時它還支持 HTTP 直播(HLS),MP4,MP3,WebM,M4A,MPEG-TS 和 AAC)。
    • 支持高級的 HLS 特性,例如正確處理 EXT-X-DISCONTINUITY 標籤;
    • 支持自定義和擴治你的使用場景。ExoPlayer 專門爲此設計;
    • 便於隨着 App 的升級而升級。由於 ExoPlayer 是一個包含在你的應用中的庫,對於你使用哪一個版本有徹底的控制權,而且你能夠簡單的跟隨應用的升級而升級;
    • 更少的適配性問題。
  • ExoPlayer 的缺點:

    • ExoPlayer 的音頻和視頻組件依賴 Android 的 MediaCodec 接口,該接口發佈於 Android4.1(API 等級 16)。所以它不能工做於以前的Android 版本。
  • 11.2.3.Vitamio 視頻播放框架
  • 3.1 用法
  • 官網:https://www.vitamio.org
  • Vitamio 的使用步驟:

    • 1.下載 Vitamio 庫,並做爲工程依賴。
    • 2.在 Activity 的 onCreate 方法中添加以下代碼,初始化 Vitamio 的解碼器
  • 3.2 優勢

    • 強大,支持超多格式視頻和網絡視頻播放。
    • 使用簡單。調用很是簡單,方便使用。
    • 其官方還給出了其餘不少優勢,可是我的以爲不足以成爲優勢。
  • 11.2.4.Bilibili 開源視頻播放框架ijkplayer
  • 4.1 特色

    • HTTPS支持
    • 支持彈幕
    • 支持基本的拖動,聲音、亮度調節
    • 支持邊播邊緩存
    • 支持視頻自己自帶rotation的旋轉(90,270之類),重力旋轉與手動旋轉的同步支持
    • 支持列表播放,直接添加控件爲封面,列表全屏動畫,視頻加載速度,列表小窗口支持拖動
    • 5.0的過場效果,調整比例,多分辨率切換
    • 支持切換播放器,進度條小窗口預覽
    • 其餘一些小動畫效果,rtsp、concat、mpeg
  • 4.2 優缺點

    • ijkplayer 最大的優勢就是能夠根據須要編譯須要的解碼器。在編譯的時候經過 ln -s module-default.sh module.sh 選擇要編譯的解碼器。ijkplayer 在 config 目錄下提供了三種 module.sh 。也可本身修改 module.sh 。
    • ijkplayer 的缺點是庫太大。加入項目後會大大增長你的 APP 的大小。

11.3 關於個人我的博客和站點

閱讀原文http://click.aliyun.com/m/40402/

相關文章
相關標籤/搜索