zhaoyadi 2 yıl önce
ebeveyn
işleme
1e5c3b2d90
20 değiştirilmiş dosya ile 384 ekleme ve 509 silme
  1. 0 6
      kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerDef.java
  2. 0 18
      kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java
  3. 59 112
      kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java
  4. 0 15
      kit/src/main/java/com/tencent/liteav/demo/superplayer/player/ISuperPlayerListener.java
  5. 5 0
      kit/src/main/java/com/tencent/liteav/demo/superplayer/player/ITimeout.java
  6. 9 37
      kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayer.java
  7. 26 231
      kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayerImpl.java
  8. 2 8
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java
  9. 34 14
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java
  10. 0 7
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java
  11. 36 2
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/TimeOutPlayer.java
  12. 12 24
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java
  13. 105 0
      kit/src/main/java/com/tencent/liteav/demo/superplayer/util/CountDownUtil.java
  14. 2 1
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/dao/CountDownDao.kt
  15. 2 2
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/repo/PlayerRepository.kt
  16. 2 1
      kit/src/main/res/layout/superplayer_vod_player_timeout.xml
  17. 8 9
      kit/src/main/res/layout/superplayer_vod_player_window.xml
  18. 4 0
      kit/src/main/res/values/attrs.xml
  19. 9 0
      ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerActivity.kt
  20. 69 22
      ui/src/main/kotlin/com/tencent/liteav/demo/player/viewmodel/PlayerViewModel.kt

+ 0 - 6
kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerDef.java

@@ -14,10 +14,4 @@ public class SuperPlayerDef {
         LOADING,    // 缓冲中
         END         // 结束播放
     }
-
-    public enum TimeoutState {
-        RUNNING,
-        TIMEOUT,
-        NO_CONFIG,
-    }
 }

+ 0 - 18
kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerModel.java

@@ -10,30 +10,12 @@ package com.tencent.liteav.demo.superplayer;
  * 是URL播放方式扩展,可同时传入多条URL,用于进行码率切换
  */
 public class SuperPlayerModel {
-
-    /**
-     * 自动播放
-     */
-    public static final int PLAY_ACTION_AUTO_PLAY = 0;
-
-    /**
-     * 手动播放
-     */
-    public static final int PLAY_ACTION_MANUAL_PLAY = 1;
-
-    /**
-     * 预加载
-     */
-    public static final int PLAY_ACTION_PRELOAD = 2;
-
     public String url = "";      // 视频URL
 
     public String title = "";    // 视频文件名 (用于显示在UI层);
 
     public String cover;
 
-    public int playAction = PLAY_ACTION_AUTO_PLAY;
-
     public int duration;
 
     public boolean isEnableCache = false; // 是否开启缓存能力,默认关闭

+ 59 - 112
kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java

@@ -1,9 +1,5 @@
 package com.tencent.liteav.demo.superplayer;
 
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_AUTO_PLAY;
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY;
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_PRELOAD;
-
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -15,8 +11,6 @@ import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.widget.RelativeLayout;
 
-
-import com.tencent.liteav.demo.superplayer.player.ISuperPlayerListener;
 import com.tencent.liteav.demo.superplayer.player.SuperPlayer;
 import com.tencent.liteav.demo.superplayer.player.SuperPlayerImpl;
 import com.tencent.liteav.demo.superplayer.player.SuperPlayerObserver;
@@ -50,17 +44,14 @@ public class SuperPlayerView extends RelativeLayout {
     private SuperPlayer mSuperPlayer;                    // 超级播放器
 
     private SuperPlayerModel mCurrentSuperPlayerModel;        // 当前正在播放的SuperPlayerModel
-    private int mPlayAction;                     // 播放模式
+
     private int mPlayIndex;                      // 正在播放model的索引
     private List<SuperPlayerModel> mSuperPlayerModelList;           // SuperPlayerModel列表
 
-    private long mDuration;                       // 时长
-    private long mProgress;                       // 进度
-
     private boolean mIsPlayInit;
-    private boolean isCallResume = false;            //resume方法时候被调用,在预加载模式使用
-    private ISuperPlayerListener mSuperPlayerListener;
+    private boolean mIsTimeOut;
 
+    // 正常窗口模式下裁切的圆角值
     private float topLeftRadius;
     private float topRightRadius;
     private float bottomLeftRadius;
@@ -76,15 +67,11 @@ public class SuperPlayerView extends RelativeLayout {
 
     public SuperPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        initAttrs(context, attrs);
-        initialize(context);
-
-    }
-
-    private void initialize(Context context) {
         mContext = context;
         initView();
         initPlayer();
+        initAttrs(context, attrs);
+        setWillNotDraw(false);
     }
 
     private void initAttrs(Context context, AttributeSet attrs) {
@@ -97,8 +84,6 @@ public class SuperPlayerView extends RelativeLayout {
             bottomRightRadius = a.getDimension(R.styleable.RoundImageView_bottomRightRadius, radius);
             a.recycle();
         }
-
-        setWillNotDraw(false);
     }
 
     /**
@@ -161,11 +146,24 @@ public class SuperPlayerView extends RelativeLayout {
         mSuperPlayerModelList = models;
     }
 
+    /* 播放指定的 */
     public void playIndexModel(int index) {
         playModelInList(index);
     }
 
+    /* 播放下一个 */
+    private void playNextVideo() {
+        playModelInList(++mPlayIndex);
+    }
+
     private void playModelInList(int index) {
+        if ((index >= mSuperPlayerModelList.size())) {
+            return;
+        }
+        if (mIsTimeOut) return;
+
+        mPlayIndex = (index) % mSuperPlayerModelList.size();
+
         mIsPlayInit = false;
         mSuperPlayer.stop();
         mPlayIndex = index;
@@ -186,12 +184,10 @@ public class SuperPlayerView extends RelativeLayout {
     }
 
     private void playWithModelInner(SuperPlayerModel model) {
-        mPlayAction = mCurrentSuperPlayerModel.playAction;
-        if (mPlayAction == PLAY_ACTION_AUTO_PLAY || mPlayAction == PLAY_ACTION_PRELOAD) {
-            mSuperPlayer.play(model);
-        } else {
-            mSuperPlayer.reset();
-        }
+        if (mIsTimeOut) return;
+
+        mSuperPlayer.play(model);
+
         if (mPlayerViewCallback != null) {
             mPlayerViewCallback.onPlayIndex(mPlayIndex, model);
         }
@@ -199,35 +195,24 @@ public class SuperPlayerView extends RelativeLayout {
         mWindowPlayer.preparePlayVideo(model);
     }
 
-    /**
-     * 更新标题
-     *
-     * @param title 视频名称
-     */
-    private void updateTitle(String title) {
-        mWindowPlayer.updateTitle(title);
-        mFullScreenPlayer.updateTitle(title);
-    }
-
     /**
      * resume生命周期回调
      */
     public void onResume() {
-        if (mPlayAction == PLAY_ACTION_MANUAL_PLAY && mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.INIT) {
+        if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.INIT) {
             return;
         }
         mSuperPlayer.resume();
-        isCallResume = true;
     }
 
     /**
      * pause生命周期回调
      */
     public void onPause() {
-        if (mPlayAction == PLAY_ACTION_MANUAL_PLAY && mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.INIT) {
+        if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.INIT) {
             return;
         }
-        mSuperPlayer.pauseVod();
+        mSuperPlayer.pause();
     }
 
     /**
@@ -245,6 +230,14 @@ public class SuperPlayerView extends RelativeLayout {
         mSuperPlayer.stop();
     }
 
+    public void setTimeOut(final boolean timeout) {
+        this.mIsTimeOut = timeout;
+
+        mSuperPlayer.setTimeout(timeout);
+        mWindowPlayer.setTimeoutState(timeout);
+        mFullScreenPlayer.setTimeoutState(timeout);
+    }
+
     /**
      * 设置超级播放器的回掉
      *
@@ -254,15 +247,20 @@ public class SuperPlayerView extends RelativeLayout {
         mPlayerViewCallback = callback;
     }
 
-    /**
-     * 设置超级播放器中点播播放器和直播播放器的回调
-     *
-     * @param superPlayerListener
-     */
-    public void setSuperPlayerListener(ISuperPlayerListener superPlayerListener) {
-        mSuperPlayerListener = superPlayerListener;
-        if (mSuperPlayer != null) {
-            mSuperPlayer.setSuperPlayerListener(mSuperPlayerListener);
+    public void switchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
+        if (mControllerCallback != null) {
+            mControllerCallback.onSwitchPlayMode(playerMode);
+        }
+    }
+
+    private void handleSwitchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
+        mFullScreenPlayer.hide();
+        mWindowPlayer.hide();
+        //请求全屏模式
+        if (playerMode == SuperPlayerDef.PlayerMode.FULLSCREEN) {
+            onSwitchFullMode(playerMode);
+        } else if (playerMode == SuperPlayerDef.PlayerMode.WINDOW) { // 请求窗口模式
+            onSwitchWindowMode(playerMode);
         }
     }
 
@@ -296,17 +294,6 @@ public class SuperPlayerView extends RelativeLayout {
         mSuperPlayer.switchPlayMode(playerMode);
     }
 
-    private void handleSwitchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
-        mFullScreenPlayer.hide();
-        mWindowPlayer.hide();
-        //请求全屏模式
-        if (playerMode == SuperPlayerDef.PlayerMode.FULLSCREEN) {
-            onSwitchFullMode(playerMode);
-        } else if (playerMode == SuperPlayerDef.PlayerMode.WINDOW) { // 请求窗口模式
-            onSwitchWindowMode(playerMode);
-        }
-    }
-
     /**
      * 初始化controller回调
      */
@@ -367,15 +354,10 @@ public class SuperPlayerView extends RelativeLayout {
     };
 
     private void handleResume() {
-        if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.LOADING
-                && mPlayAction == PLAY_ACTION_PRELOAD) {
+        if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.LOADING) {
             mSuperPlayer.resume();
         } else if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.INIT) {
-            if (mPlayAction == PLAY_ACTION_PRELOAD) {
-                mSuperPlayer.resume();
-            } else if (mPlayAction == PLAY_ACTION_MANUAL_PLAY) {
-                mSuperPlayer.play(mCurrentSuperPlayerModel);
-            }
+            mSuperPlayer.resume();
         } else if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.END) { //重播
             mSuperPlayer.restart();
         } else if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.PAUSE) { //继续播放
@@ -383,24 +365,6 @@ public class SuperPlayerView extends RelativeLayout {
         }
     }
 
-    private void playNextVideo() {
-        playIndexVideo(++mPlayIndex);
-//        if (!mIsLoopPlayList && (mPlayIndex == mSuperPlayerModelList.size() - 1)) {
-//            return;
-//        }
-//        mPlayIndex = (++mPlayIndex) % mSuperPlayerModelList.size();
-//        playModelInList(mPlayIndex);
-    }
-
-    private void playIndexVideo(int index) {
-        if ((index >= mSuperPlayerModelList.size())) {
-            return;
-        }
-
-        mPlayIndex = (index) % mSuperPlayerModelList.size();
-        playModelInList(mPlayIndex);
-    }
-
     /**
      * SuperPlayerView的回调接口
      */
@@ -458,12 +422,6 @@ public class SuperPlayerView extends RelativeLayout {
         }
     }
 
-    public void switchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
-        if (mControllerCallback != null) {
-            mControllerCallback.onSwitchPlayMode(playerMode);
-        }
-
-    }
 
     public SuperPlayerDef.PlayerMode getPlayerMode() {
         return mSuperPlayer.getPlayerMode();
@@ -474,10 +432,8 @@ public class SuperPlayerView extends RelativeLayout {
     }
 
     private void actonOfPreloadOnPlayPrepare() {
-        if (mPlayAction != PLAY_ACTION_PRELOAD) {
-            mWindowPlayer.prepareLoading();
-            mFullScreenPlayer.prepareLoading();
-        }
+        mWindowPlayer.prepareLoading();
+        mFullScreenPlayer.prepareLoading();
     }
 
 
@@ -488,6 +444,9 @@ public class SuperPlayerView extends RelativeLayout {
             mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.INIT);
             mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.INIT);
             actonOfPreloadOnPlayPrepare();
+
+            mWindowPlayer.showBackground();
+            mFullScreenPlayer.showBackground();
         }
 
         @Override
@@ -495,8 +454,8 @@ public class SuperPlayerView extends RelativeLayout {
             mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.PLAYING);
             mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.PLAYING);
 
-            updateTitle(name);
             mWindowPlayer.hideBackground();
+            mFullScreenPlayer.hideBackground();
             notifyCallbackPlaying();
         }
 
@@ -517,22 +476,12 @@ public class SuperPlayerView extends RelativeLayout {
 
         @Override
         public void onPlayLoading() {
-            //预加载模式进行特殊处理
-            if (mPlayAction == PLAY_ACTION_PRELOAD) {
-                if (isCallResume) {
-                    mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
-                    mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
-                }
-            } else {
-                mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
-                mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
-            }
+            mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
+            mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
         }
 
         @Override
         public void onPlayProgress(long current, long duration) {
-            mProgress = current;
-            mDuration = duration;
             mWindowPlayer.updateVideoProgress(current, duration);
             mFullScreenPlayer.updateVideoProgress(current, duration);
         }
@@ -586,10 +535,6 @@ public class SuperPlayerView extends RelativeLayout {
         mSuperPlayer.setNeedToPause(value);
     }
 
-    public void setIsAutoPlay(boolean b) {
-        mSuperPlayer.setAutoPlay(b);
-    }
-
     public void setStartTime(double startTime) {
         mSuperPlayer.setStartTime((float) startTime);
     }
@@ -598,6 +543,8 @@ public class SuperPlayerView extends RelativeLayout {
         mSuperPlayer.setLoop(b);
     }
 
+    /*                                                  */
+    /*----------------正常窗口播放时裁切圆角----------------*/
     @Override
     public void draw(Canvas canvas) {
         canvas.save();

+ 0 - 15
kit/src/main/java/com/tencent/liteav/demo/superplayer/player/ISuperPlayerListener.java

@@ -1,15 +0,0 @@
-package com.tencent.liteav.demo.superplayer.player;
-
-import android.os.Bundle;
-
-import com.tencent.rtmp.TXVodPlayer;
-
-public interface ISuperPlayerListener {
-    public void onVodPlayEvent(final TXVodPlayer player, final int event, final Bundle param);
-
-    public void onVodNetStatus(final TXVodPlayer player, final Bundle status);
-
-    public void onLivePlayEvent(final int event, final Bundle param);
-
-    public void onLiveNetStatus(final Bundle status);
-}

+ 5 - 0
kit/src/main/java/com/tencent/liteav/demo/superplayer/player/ITimeout.java

@@ -0,0 +1,5 @@
+package com.tencent.liteav.demo.superplayer.player;
+
+public interface ITimeout {
+    void setTimeout(boolean timeout);
+}

+ 9 - 37
kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayer.java

@@ -2,16 +2,10 @@ package com.tencent.liteav.demo.superplayer.player;
 
 import com.tencent.liteav.demo.superplayer.SuperPlayerDef;
 import com.tencent.liteav.demo.superplayer.SuperPlayerModel;
-import com.tencent.rtmp.TXLivePlayer;
-import com.tencent.rtmp.ui.TXCloudVideoView;
-
-public interface SuperPlayer {
-
 
+public interface SuperPlayer extends ITimeout {
     /**
      * 开始播放
-     *
-     * @param model 超级播放器模型
      */
     void play(SuperPlayerModel model);
 
@@ -26,11 +20,6 @@ public interface SuperPlayer {
      */
     void pause();
 
-    /**
-     * 暂停点播视频
-     */
-    void pauseVod();
-
     /**
      * 恢复播放
      */
@@ -42,12 +31,15 @@ public interface SuperPlayer {
     void stop();
 
     /**
-     * 销毁播放器
+     * 重置播放
      */
-    void destroy();
-
     void reset();
 
+    /**
+     * 跳转位置
+     */
+    void seek(int position);
+
     /**
      * 切换播放器模式
      *
@@ -56,16 +48,6 @@ public interface SuperPlayer {
      */
     void switchPlayMode(SuperPlayerDef.PlayerMode playerMode);
 
-    void enableHardwareDecode(boolean enable);
-
-    void setPlayerView(TXCloudVideoView videoView);
-
-    void seek(int position);
-
-    void setRate(float speedLevel);
-
-    String getPlayURL();
-
     /**
      * 获取当前播放器模式
      *
@@ -91,29 +73,19 @@ public interface SuperPlayer {
      */
     void setObserver(SuperPlayerObserver observer);
 
-    /**
-     * 设置超级播放器中点播事件和直播事件的回调
-     * @param superPlayerListener
-     */
-    void setSuperPlayerListener(ISuperPlayerListener superPlayerListener);
-
     /**
      * 设置是否循环
+     *
      * @param isLoop true循环,false不循环
      */
     void setLoop(boolean isLoop);
 
     /**
      * 设置开始时间
+     *
      * @param startPos 开始时间
      */
     void setStartTime(float startPos);
 
-    /**
-     * 设置是否自动播放
-     * @param isAutoPlay true自动播放,false不自动播放
-     */
-    void setAutoPlay(boolean isAutoPlay);
-
     void setNeedToPause(boolean value);
 }

+ 26 - 231
kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayerImpl.java

@@ -1,9 +1,5 @@
 package com.tencent.liteav.demo.superplayer.player;
 
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_AUTO_PLAY;
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY;
-import static com.tencent.liteav.demo.superplayer.SuperPlayerModel.PLAY_ACTION_PRELOAD;
-
 import android.content.Context;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -13,19 +9,16 @@ import com.tencent.liteav.demo.superplayer.SuperPlayerCode;
 import com.tencent.liteav.demo.superplayer.SuperPlayerDef;
 import com.tencent.liteav.demo.superplayer.SuperPlayerGlobalConfig;
 import com.tencent.liteav.demo.superplayer.SuperPlayerModel;
-import com.tencent.rtmp.ITXLivePlayListener;
 import com.tencent.rtmp.ITXVodPlayListener;
 import com.tencent.rtmp.TXLiveBase;
 import com.tencent.rtmp.TXLiveConstants;
-import com.tencent.rtmp.TXLivePlayConfig;
-import com.tencent.rtmp.TXLivePlayer;
 import com.tencent.rtmp.TXVodPlayConfig;
 import com.tencent.rtmp.TXVodPlayer;
 import com.tencent.rtmp.ui.TXCloudVideoView;
 
 import java.io.File;
 
-public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLivePlayListener {
+public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener {
 
     private static final String TAG = "SuperPlayerImpl";
     private static final int SUPERPLAYER_MODE = 1;
@@ -38,91 +31,26 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
     private TXVodPlayer mVodPlayer;       // 点播播放器
     private TXVodPlayConfig mVodPlayConfig;   // 点播播放器配置
 
-    private ISuperPlayerListener mSuperPlayerListener;
     private SuperPlayerModel mCurrentModel;  // 当前播放的model
     private SuperPlayerObserver mObserver;
     private SuperPlayerDef.PlayerMode mCurrentPlayMode = SuperPlayerDef.PlayerMode.WINDOW;    // 当前播放模式
     private SuperPlayerDef.PlayerState mCurrentPlayState = SuperPlayerDef.PlayerState.INIT;
 
     private String mCurrentPlayVideoURL;    // 当前播放的URL
-    private int mSeekPos;                   // 记录切换硬解时的播放时间
     private float mStartPos;                // 视频开始播放时间
-    private long mReportLiveStartTime = -1; // 直播开始时间,用于上报使用时长
-    private long mReportVodStartTime = -1;  // 点播开始时间,用于上报使用时长
-    private long mMaxLiveProgressTime;      // 观看直播的最大时长
-    private boolean mIsAutoPlay = true;      // 是否自动播放
-    private boolean mChangeHWAcceleration;  // 切换硬解后接收到第一个关键帧前的标记位
-    private int mPlayAction;            //播放模式
+
     private boolean isPrepared = false;
     private boolean isNeedResume = false;
     private boolean mNeedToPause = false;
+    private boolean mTimeout = false;
 
     public SuperPlayerImpl(Context context, TXCloudVideoView videoView) {
-        initialize(context, videoView);
-    }
-
-    /**
-     * 直播播放器事件回调
-     *
-     * @param event
-     * @param param
-     */
-    @Override
-    public void onPlayEvent(int event, Bundle param) {
-        if (event != TXLiveConstants.PLAY_EVT_PLAY_PROGRESS) {
-            String playEventLog = "TXLivePlayer onPlayEvent event: " + event + ", "
-                    + param.getString(TXLiveConstants.EVT_DESCRIPTION);
-            Log.d(TAG, playEventLog);
-        }
-        switch (event) {
-            case TXLiveConstants.PLAY_EVT_VOD_PLAY_PREPARED: //视频播放开始
-            case TXLiveConstants.PLAY_EVT_PLAY_BEGIN:
-                updatePlayerState(SuperPlayerDef.PlayerState.PLAYING);
-                break;
-            case TXLiveConstants.PLAY_ERR_NET_DISCONNECT:
-            case TXLiveConstants.PLAY_EVT_PLAY_END:
-                stop();
-                updatePlayerState(SuperPlayerDef.PlayerState.END);
-                if (event == TXLiveConstants.PLAY_ERR_NET_DISCONNECT) {
-                    onError(SuperPlayerCode.NET_ERROR, "网络不给力,点击重试");
-                } else {
-                    onError(SuperPlayerCode.LIVE_PLAY_END, param.getString(TXLiveConstants.EVT_DESCRIPTION));
-                }
-                break;
-            case TXLiveConstants.PLAY_EVT_PLAY_LOADING:
-//            case TXLiveConstants.PLAY_WARNING_RECONNECT:  //暂时去掉,回调该状态时,播放画面可能是正常的,loading 状态只在 TXLiveConstants.PLAY_EVT_PLAY_LOADING 处理
-                updatePlayerState(SuperPlayerDef.PlayerState.LOADING);
-                break;
-            case TXLiveConstants.PLAY_EVT_RCV_FIRST_I_FRAME:
-                updatePlayerState(SuperPlayerDef.PlayerState.PLAYING);
-                mObserver.onRcvFirstIframe();
-                break;
-            case TXLiveConstants.PLAY_EVT_PLAY_PROGRESS:
-                int progress = param.getInt(TXLiveConstants.EVT_PLAY_PROGRESS_MS);
-                mMaxLiveProgressTime = progress > mMaxLiveProgressTime ? progress : mMaxLiveProgressTime;
-                updatePlayProgress(progress / 1000, mMaxLiveProgressTime / 1000);
-                break;
-            default:
-                break;
-        }
-        if (mSuperPlayerListener != null) {
-            mSuperPlayerListener.onLivePlayEvent(event, param);
-        }
-    }
+        mContext = context;
+        mVideoView = videoView;
 
-    /**
-     * 直播播放器网络状态回调
-     *
-     * @param bundle
-     */
-    @Override
-    public void onNetStatus(Bundle bundle) {
-        if (mSuperPlayerListener != null) {
-            mSuperPlayerListener.onLiveNetStatus(bundle);
-        }
+        initVodPlayer(mContext);
     }
 
-
     @Override
     public void setNeedToPause(boolean value) {
         mNeedToPause = value;
@@ -151,11 +79,6 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
                 if (mNeedToPause) {
                     return;
                 }
-                if (mChangeHWAcceleration) { //切换软硬解码器后,重新seek位置
-                    Log.i(TAG, "seek pos:" + mSeekPos);
-                    seek(mSeekPos);
-                    mChangeHWAcceleration = false;
-                }
                 updatePlayerState(SuperPlayerDef.PlayerState.PLAYING);
                 mObserver.onRcvFirstIframe();
                 break;
@@ -189,17 +112,13 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
             updatePlayerState(SuperPlayerDef.PlayerState.PAUSE);
             onError(SuperPlayerCode.VOD_PLAY_FAIL, param.getString(TXLiveConstants.EVT_DESCRIPTION));
         }
-        if (mSuperPlayerListener != null) {
-            mSuperPlayerListener.onVodPlayEvent(player, event, param);
-        }
     }
 
     private void onVodPlayPrepared() {
-        Log.i(TAG, "PLAY_EVT_VOD_PLAY_PREPARED");
         isPrepared = true;
 
         if (mNeedToPause) {
-            pauseVod();
+            pause();
             return;
         }
         if (isNeedResume) {
@@ -215,16 +134,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
      */
     @Override
     public void onNetStatus(TXVodPlayer player, Bundle bundle) {
-        if (mSuperPlayerListener != null) {
-            mSuperPlayerListener.onVodNetStatus(player, bundle);
-        }
-    }
-
-    private void initialize(Context context, TXCloudVideoView videoView) {
-        mContext = context;
-        mVideoView = videoView;
-
-        initVodPlayer(mContext);
+        // DO NOTHING
     }
 
     /**
@@ -260,12 +170,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
      */
     public void playWithModel(SuperPlayerModel model) {
         reset();
-        if (!TextUtils.isEmpty(model.url)) {
-            playWithUrl(model);
-        }
-    }
 
-    private void playWithUrl(SuperPlayerModel model) {
         String videoURL = model.url;
 
         if (TextUtils.isEmpty(videoURL)) {
@@ -273,25 +178,12 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
             return;
         }
 
-        // 点播播放器:播放点播文件
-        mReportVodStartTime = System.currentTimeMillis();
         mVodPlayer.setPlayerView(mVideoView);
         playVodURL(videoURL);
 
         updatePlayProgress(0, model.duration);
     }
 
-    /**
-     * 播放视频
-     *
-     * @param model
-     */
-    private void playModeVideo(SuperPlayerModel model) {
-        if (!TextUtils.isEmpty(model.url)) {
-            playVodURL(model.url);
-        }
-    }
-
     /**
      * 播放点播url
      */
@@ -303,55 +195,13 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
 
         if (mVodPlayer != null) {
             mVodPlayer.setStartTime(mStartPos);
-            mVodPlayer.setAutoPlay(mIsAutoPlay);
-            if (mPlayAction == PLAY_ACTION_AUTO_PLAY || mPlayAction == PLAY_ACTION_MANUAL_PLAY) {
-                mVodPlayer.setAutoPlay(true);
-            } else if (mPlayAction == PLAY_ACTION_PRELOAD) {
-                mVodPlayer.setAutoPlay(false);
-                mPlayAction = PLAY_ACTION_AUTO_PLAY;
-            }
+            mVodPlayer.setAutoPlay(true);
             mVodPlayer.setVodListener(this);
             mVodPlayer.setToken(null);
             mVodPlayer.startPlay(url);
         }
     }
 
-    private boolean isVersionSupportAppendUrl() {
-        String strVersion = TXLiveBase.getSDKVersionStr();
-        String[] strVers = strVersion.split("\\.");
-        if (strVers.length <= 1) {
-            return false;
-        }
-        int majorVer = 0;
-        int minorVer = 0;
-        try {
-            majorVer = Integer.parseInt(strVers[0]);
-            minorVer = Integer.parseInt(strVers[1]);
-        } catch (NumberFormatException e) {
-            Log.e(TAG, "parse version failed.", e);
-            majorVer = 0;
-            minorVer = 0;
-        }
-        Log.i(TAG, strVersion + " , " + majorVer + " , " + minorVer);
-        return majorVer > SUPPORT_MAJOR_VERSION || (majorVer == SUPPORT_MAJOR_VERSION && minorVer >= SUPPORT_MINOR_VERSION);
-    }
-
-    /**
-     * 上报播放时长
-     */
-    private void reportPlayTime() {
-        if (mReportLiveStartTime != -1) {
-            long reportEndTime = System.currentTimeMillis();
-            long diff = (reportEndTime - mReportLiveStartTime) / 1000;
-            mReportLiveStartTime = -1;
-        }
-        if (mReportVodStartTime != -1) {
-            long reportEndTime = System.currentTimeMillis();
-            long diff = (reportEndTime - mReportVodStartTime) / 1000;
-            mReportVodStartTime = -1;
-        }
-    }
-
     /**
      * 更新播放进度
      *
@@ -407,36 +257,18 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         return title;
     }
 
-    /**
-     * 是否是RTMP协议
-     *
-     * @param videoURL
-     * @return
-     */
-    private boolean isRTMPPlay(String videoURL) {
-        return !TextUtils.isEmpty(videoURL) && videoURL.startsWith("rtmp");
-    }
-
-    /**
-     * 是否是HTTP-FLV协议
-     *
-     * @param videoURL
-     * @return
-     */
-    private boolean isFLVPlay(String videoURL) {
-        return (!TextUtils.isEmpty(videoURL) && videoURL.startsWith("http://")
-                || videoURL.startsWith("https://")) && videoURL.contains(".flv");
-    }
-
     @Override
     public void play(SuperPlayerModel model) {
-        mPlayAction = model.playAction;
+        if (mTimeout) return;
+
         mCurrentModel = model;
         playWithModel(model);
     }
 
     @Override
     public void restart() {
+        if (mTimeout) return;
+
         if (mCurrentPlayVideoURL != null) {
             playVodURL(mCurrentPlayVideoURL);
         } else {
@@ -450,14 +282,10 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         updatePlayerState(SuperPlayerDef.PlayerState.PAUSE);
     }
 
-    @Override
-    public void pauseVod() {
-        mVodPlayer.pause();
-        updatePlayerState(SuperPlayerDef.PlayerState.PAUSE);
-    }
-
     @Override
     public void resume() {
+        if (mTimeout) return;
+
         isNeedResume = true;
         if (isPrepared) {
             mVodPlayer.resume();
@@ -478,6 +306,13 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         updatePlayerState(SuperPlayerDef.PlayerState.INIT);
     }
 
+    @Override
+    public void setTimeout(boolean timeout) {
+        this.mTimeout = timeout;
+
+        if(timeout) pause();
+    }
+
     private void resetPlayer() {
         isPrepared = false;
         isNeedResume = false;
@@ -485,12 +320,6 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
             mVodPlayer.setVodListener(null);
             mVodPlayer.stopPlay(false);
         }
-        reportPlayTime();
-    }
-
-    @Override
-    public void destroy() {
-
     }
 
     @Override
@@ -499,27 +328,14 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
             return;
         }
         mCurrentPlayMode = playerMode;
-    }
 
-    @Override
-    public void enableHardwareDecode(boolean enable) {
-        mVodPlayer.enableHardwareDecode(enable);
-        if (mCurrentPlayState != SuperPlayerDef.PlayerState.END) {
-            mChangeHWAcceleration = true;
-            mSeekPos = (int) mVodPlayer.getCurrentPlaybackTime();
-            Log.i(TAG, "save pos:" + mSeekPos);
-            resetPlayer();
-            playModeVideo(mCurrentModel);
+        if (mCurrentPlayMode == SuperPlayerDef.PlayerMode.FULLSCREEN) {
+            if (getPlayerState() == SuperPlayerDef.PlayerState.PAUSE) {
+                resume();
+            }
         }
     }
 
-    @Override
-    public void setPlayerView(TXCloudVideoView videoView) {
-        mVideoView = videoView;
-        mVodPlayer.setPlayerView(videoView);
-
-    }
-
     @Override
     public void seek(int position) {
         if (mVodPlayer != null) {
@@ -534,16 +350,6 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         }
     }
 
-    @Override
-    public void setRate(float speedLevel) {
-        mVodPlayer.setRate(speedLevel);
-    }
-
-    @Override
-    public String getPlayURL() {
-        return mCurrentPlayVideoURL;
-    }
-
     @Override
     public SuperPlayerDef.PlayerMode getPlayerMode() {
         return mCurrentPlayMode;
@@ -559,11 +365,6 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         mObserver = observer;
     }
 
-    @Override
-    public void setSuperPlayerListener(ISuperPlayerListener superPlayerListener) {
-        mSuperPlayerListener = superPlayerListener;
-    }
-
     @Override
     public void setLoop(boolean isLoop) {
         mVodPlayer.setLoop(isLoop);
@@ -574,10 +375,4 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive
         this.mStartPos = startPos;
         mVodPlayer.setStartTime(startPos);
     }
-
-    @Override
-    public void setAutoPlay(boolean isAutoPlay) {
-        this.mIsAutoPlay = isAutoPlay;
-        mVodPlayer.setAutoPlay(isAutoPlay);
-    }
 }

+ 2 - 8
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/AbsPlayer.java

@@ -14,8 +14,7 @@ import com.tencent.liteav.demo.superplayer.SuperPlayerDef;
 public abstract class AbsPlayer extends RelativeLayout implements Player {
     protected static final int HIDDEN_DELAY = 3000;
 
-    protected static final int      MAX_SHIFT_TIME = 7200; // demo演示直播时移是MAX_SHIFT_TIMEs,即2小时
-    protected Callback     mControllerCallback; // 播放控制回调
+    protected Callback mControllerCallback; // 播放控制回调
 
     protected Runnable mHideViewRunnable = new Runnable() {
         @Override
@@ -51,7 +50,7 @@ public abstract class AbsPlayer extends RelativeLayout implements Player {
 
     }
 
-    public void timeOut(){
+    public void timeOut() {
 
     }
 
@@ -65,11 +64,6 @@ public abstract class AbsPlayer extends RelativeLayout implements Player {
 
     }
 
-    @Override
-    public void updateTitle(String title) {
-
-    }
-
     @Override
     public void updateVideoProgress(long current, long duration) {
 

+ 34 - 14
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/FullScreenPlayer.java

@@ -23,7 +23,7 @@ import com.tencent.liteav.demo.superplayer.ui.view.UnlockProgressView;
  * <p>
  * 2、触摸事件监听{@link #onTouchEvent(MotionEvent)}
  */
-public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.OnUnlockListener {
+public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.OnUnlockListener, TimeOutPlayer.Callback {
     private UnlockProgressView mIvLock;                                // 锁屏按钮
     private TextView mIvLockText;
     private ImageView mImageCover;                            // 封面图
@@ -67,15 +67,17 @@ public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.On
 
         mIvLockText = (TextView) findViewById(R.id.superplayer_iv_lock_text);
         mImageCover = (ImageView) findViewById(R.id.superplayer_cover_view);
+
         mTimeoutView = (TimeOutPlayer) findViewById(R.id.superplayer_timeout_view);
+        mTimeoutView.setCallback(this);
     }
 
     public void setPlayNextButtonVisibility(boolean isShowing) {
 
     }
 
-    public void setTimeoutState(SuperPlayerDef.TimeoutState state) {
-        if (state == SuperPlayerDef.TimeoutState.TIMEOUT) {
+    public void setTimeoutState(final boolean state) {
+        if (state) {
             hide();
             mTimeoutView.setVisibility(VISIBLE);
         } else {
@@ -84,7 +86,6 @@ public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.On
     }
 
     public void preparePlayVideo(SuperPlayerModel superPlayerModel) {
-        updateTitle(superPlayerModel.title);
         if (!isDestroy) {
             Glide.with(getContext())
                     .load(superPlayerModel.cover)
@@ -115,6 +116,14 @@ public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.On
         mIvLockText.setVisibility(GONE);
     }
 
+    @Override
+    public void timeOut() {
+        mIvLock.setVisibility(GONE);
+        mIvLockText.setVisibility(GONE);
+
+        mTimeoutView.setVisibility(VISIBLE);
+    }
+
     /**
      * 释放控件的内存
      */
@@ -146,16 +155,6 @@ public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.On
         mImageCover.setVisibility(VISIBLE);
     }
 
-    /**
-     * 更新视频名称
-     *
-     * @param title 视频名称
-     */
-    @Override
-    public void updateTitle(String title) {
-        // DO NOTHING UNLESS NEED
-    }
-
     /**
      * 更新实时播放进度
      *
@@ -201,4 +200,25 @@ public class FullScreenPlayer extends AbsPlayer implements UnlockProgressView.On
     public void unlockFailed() {
         postDelayed(mHideViewRunnable, HIDDEN_DELAY);
     }
+
+    @Override
+    public void onBack() {
+        if (mControllerCallback != null) {
+            mControllerCallback.onSwitchPlayMode(SuperPlayerDef.PlayerMode.WINDOW);
+        }
+    }
+
+    @Override
+    public void onResumePlay() {
+        if (mControllerCallback != null) {
+            mControllerCallback.onResume();
+        }
+    }
+
+    @Override
+    public void onReturn() {
+        if (mControllerCallback != null) {
+            mControllerCallback.onSwitchPlayMode(SuperPlayerDef.PlayerMode.WINDOW);
+        }
+    }
 }

+ 0 - 7
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/Player.java

@@ -50,13 +50,6 @@ public interface Player {
      */
     void updatePlayState(SuperPlayerDef.PlayerState playState);
 
-    /**
-     * 更新视频名称
-     *
-     * @param title 视频名称
-     */
-    void updateTitle(String title);
-
     /**
      * 更新视频播放进度
      *

+ 36 - 2
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/TimeOutPlayer.java

@@ -1,6 +1,7 @@
 package com.tencent.liteav.demo.superplayer.ui.player;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -16,6 +17,7 @@ public class TimeOutPlayer extends AbsPlayer implements View.OnClickListener {
     private LinearLayoutCompat resumePlay;
     private TextView returnHome;
 
+    private Callback mCallback;
 
     public TimeOutPlayer(Context context) {
         this(context, null);
@@ -34,8 +36,26 @@ public class TimeOutPlayer extends AbsPlayer implements View.OnClickListener {
         LayoutInflater.from(context).inflate(R.layout.superplayer_vod_player_timeout, this);
 
         backHome = findViewById(R.id.timeout_back);
+        backHome.setOnClickListener(this);
+
         resumePlay = findViewById(R.id.timeout_resume);
+        resumePlay.setOnClickListener(this);
+
         returnHome = findViewById(R.id.timeout_return);
+        returnHome.setOnClickListener(this);
+
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimeOutPlayer);
+            boolean show = a.getBoolean(R.styleable.TimeOutPlayer_showControl, true);
+
+            if (!show) {
+                backHome.setVisibility(GONE);
+                resumePlay.setVisibility(GONE);
+                returnHome.setVisibility(GONE);
+            }
+
+            a.recycle();
+        }
     }
 
     @Override
@@ -43,19 +63,33 @@ public class TimeOutPlayer extends AbsPlayer implements View.OnClickListener {
         final int id = v.getId();
 
         if (id == R.id.timeout_back || id == R.id.timeout_return) {
-
+            if (mCallback != null) mCallback.onBack();
         } else if (id == R.id.timeout_resume) {
-
+            if (mCallback != null) mCallback.onResumePlay();
         }
     }
 
     @Override
     public void hide() {
         super.hide();
+        this.setOnClickListener(null);
     }
 
     @Override
     public void show() {
         super.show();
+        this.setOnClickListener(this);
+    }
+
+    public void setCallback(Callback callback) {
+        this.mCallback = callback;
+    }
+
+    public interface Callback {
+        void onBack();
+
+        void onResumePlay();
+
+        void onReturn();
     }
 }

+ 12 - 24
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java

@@ -44,11 +44,12 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
     private PointProgressBar mBarProgress;                       // 播放进度条
 
     private ProgressBar mPbLiveLoading;                         // 加载圈
-    private ImageView mImageStartAndResume;                   // 开始播放的三角
     private ImageView mImageCover;                            // 封面图
     private VolumeBrightnessProgressLayout mGestureVolumeBrightnessProgressLayout; // 音量亮度调节布局
     private VideoProgressLayout mGestureVideoProgressLayout;            // 手势快进提示布局
 
+    private TimeOutPlayer mTimeoutView;
+
     private GestureDetector mGestureDetector;                       // 手势检测监听器
     private VideoGestureDetector mVideoGestureDetector;                      // 手势控制工具
 
@@ -184,9 +185,9 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
         mIvFullScreen = (ImageView) findViewById(R.id.superplayer_iv_fullscreen);
         mPbLiveLoading = (ProgressBar) findViewById(R.id.superplayer_pb_live);
         mImageCover = (ImageView) findViewById(R.id.superplayer_cover_view);
-        mImageStartAndResume = (ImageView) findViewById(R.id.superplayer_resume);
         mIvPlayNext = (ImageView) findViewById(R.id.superplayer_iv_play_next);
-        mImageStartAndResume.setOnClickListener(this);
+
+        mTimeoutView = (TimeOutPlayer) findViewById(R.id.superplayer_timeout_view);
 
         mIvPause.setOnClickListener(this);
         mIvPlayNext.setOnClickListener(this);
@@ -244,10 +245,8 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
 
     private void updateStartUI(boolean isAutoPlay) {
         if (isAutoPlay) {
-            toggleView(mImageStartAndResume, false);
             toggleView(mPbLiveLoading, true);
         } else {
-            toggleView(mImageStartAndResume, true);
             toggleView(mPbLiveLoading, false);
         }
     }
@@ -265,8 +264,8 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
         toggleView(mImageCover, true);
         mIvPause.setImageResource(R.mipmap.play_state);
         updateVideoProgress(0, superPlayerModel.duration);
-        mBarProgress.setEnabled(superPlayerModel.playAction != SuperPlayerModel.PLAY_ACTION_MANUAL_PLAY);
-        updateStartUI(superPlayerModel.playAction == SuperPlayerModel.PLAY_ACTION_AUTO_PLAY);
+        mBarProgress.setEnabled(true);
+        updateStartUI(true);
     }
 
     /**
@@ -287,13 +286,17 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
         mLayoutBottom.setVisibility(View.GONE);
     }
 
+    public void setTimeoutState(final boolean state) {
+        // DO NOTHING
+        mTimeoutView.setVisibility(state ? VISIBLE : GONE);
+    }
+
     public void toggleCoverView(boolean isVisible) {
         toggleView(mImageCover, isVisible);
     }
 
     public void prepareLoading() {
         toggleView(mPbLiveLoading, true);
-        toggleView(mImageStartAndResume, false);
     }
 
     @Override
@@ -305,7 +308,6 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
             case PLAYING:
                 mBarProgress.setEnabled(true);
                 mIvPause.setImageResource(R.mipmap.pause_state);
-                toggleView(mImageStartAndResume, false);
                 toggleView(mPbLiveLoading, false);
                 break;
             case LOADING:
@@ -314,10 +316,6 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
                 toggleView(mPbLiveLoading, true);
                 break;
             case PAUSE:
-                mIvPause.setImageResource(R.mipmap.play_state);
-                toggleView(mPbLiveLoading, false);
-                toggleView(mImageStartAndResume, true);
-                break;
             case END:
                 mIvPause.setImageResource(R.mipmap.play_state);
                 toggleView(mPbLiveLoading, false);
@@ -326,16 +324,6 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
         mCurrentPlayState = playState;
     }
 
-    /**
-     * 更新视频名称
-     *
-     * @param title 视频名称
-     */
-    @Override
-    public void updateTitle(String title) {
-
-    }
-
     /**
      * 更新视频播放进度
      *
@@ -446,7 +434,7 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
         mLastClickTime = System.currentTimeMillis();
         int id = view.getId();
 
-        if (id == R.id.superplayer_iv_pause || id == R.id.superplayer_resume) { //暂停\播放按钮
+        if (id == R.id.superplayer_iv_pause) { //暂停\播放按钮
             togglePlayState();
         } else if (id == R.id.superplayer_iv_fullscreen) { //全屏按钮
             if (mControllerCallback != null) {

+ 105 - 0
kit/src/main/java/com/tencent/liteav/demo/superplayer/util/CountDownUtil.java

@@ -0,0 +1,105 @@
+package com.tencent.liteav.demo.superplayer.util;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class CountDownUtil {
+    // 倒计时的监听者
+    public interface OnTickListener {
+        void onPause(int count, long period);
+
+        void onTimeout(int count, long period);
+    }
+
+    public static CountDownUtil build(long period, int maxCount, CountDownUtil.OnTickListener listener) {
+        return new CountDownUtil(period, maxCount, listener);
+    }
+
+    public static final int STATE_INIT = 0;
+    public static final int STATE_RUNNING = 0;
+    public static final int STATE_PAUSE = 0;
+    public static final int STATE_CANCEL = 0;
+
+    private int state = STATE_INIT;
+
+    // 周期时间
+    private final long period;
+
+    // 定时数
+    private final int maxCount;
+
+    // 回调监听对象
+    private final CountDownUtil.OnTickListener listener;
+
+    // 计时器
+    private final Timer timer = new Timer();
+
+    // 计时任务
+    private TimerTask task;
+
+    // 计数器
+    private final AtomicInteger count = new AtomicInteger(0);
+
+    private CountDownUtil(long period, int maxCount, CountDownUtil.OnTickListener listener) {
+        this.period = period;
+        this.maxCount = maxCount;
+        this.listener = listener;
+    }
+
+    public void start() {
+        if (STATE_INIT != state) {
+            throw new IllegalStateException("倒计时程序调用start时未处于初始态");
+        }
+
+        state = STATE_RUNNING;
+        count.set(0);
+        setupTask();
+    }
+
+    public void pause() {
+        if (STATE_RUNNING != state) {
+            throw new IllegalStateException("倒计时程序调用pause时未处于运行态");
+        }
+        state = STATE_PAUSE;
+
+        if (task != null) {
+            task.cancel();
+            timer.purge();
+            task = null;
+        }
+
+        if (listener != null) {
+            listener.onPause(count.get(), period);
+        }
+    }
+
+    public void resume() {
+        if (STATE_PAUSE != state) {
+            throw new IllegalStateException("倒计时程序调用resume时未处于暂停态");
+        }
+        state = STATE_RUNNING;
+
+        setupTask();
+    }
+
+    public void cancel() {
+        state = STATE_CANCEL;
+        timer.cancel();
+    }
+
+    private void setupTask() {
+        task = new TimerTask() {
+            @Override
+            public void run() {
+                count.addAndGet(1);
+
+                if(count.get() >= maxCount){
+                    listener.onTimeout(count.get(),period);
+                }
+            }
+        };
+
+        timer.schedule(task, 0, period);
+    }
+}

+ 2 - 1
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/dao/CountDownDao.kt

@@ -2,6 +2,7 @@ package com.tencent.liteav.demo.superplayer.database.dao
 
 import androidx.room.Dao
 import androidx.room.Insert
+import androidx.room.OnConflictStrategy.REPLACE
 import androidx.room.Query
 import com.tencent.liteav.demo.superplayer.database.entity.CountDown
 import kotlinx.coroutines.flow.Flow
@@ -12,6 +13,6 @@ interface CountDownDao {
     @Query("SELECT * FROM countdown WHERE course_id = :courseId LIMIT 1")
     fun getCountDownByCourseId(courseId: String): Flow<CountDown?>
 
-    @Insert
+    @Insert(onConflict = REPLACE)
     fun insertCountDown(countDown: CountDown)
 }

+ 2 - 2
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/repo/PlayerRepository.kt

@@ -28,12 +28,12 @@ public class PlayerRepository(
         return countDownDao.getCountDownByCourseId(courseId)
     }
 
-    suspend fun queryTodayEpisodeHistory(): Flow<Int> {
+    suspend fun queryTodayEpisodeHistory(courseId: String): Flow<Int> {
         val date = dateFormat.format(Date())
         return historyDao.queryTodayEpisodeHistory(date)
     }
 
-    suspend fun queryTodayDurationHistory(): Flow<List<History>> {
+    suspend fun queryTodayDurationHistory(courseId: String): Flow<List<History>> {
         val date = dateFormat.format(Date())
         return historyDao.queryTodayDurationHistory(date)
     }

+ 2 - 1
kit/src/main/res/layout/superplayer_vod_player_timeout.xml

@@ -2,7 +2,8 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="#adebd6">
 
     <ImageView
         android:layout_width="match_parent"

+ 8 - 9
kit/src/main/res/layout/superplayer_vod_player_window.xml

@@ -31,8 +31,8 @@
         android:layout_height="64dp"
         android:layout_alignParentBottom="true"
         android:background="@drawable/shadow_bottom"
-        android:orientation="horizontal"
-        android:clipChildren="false">
+        android:clipChildren="false"
+        android:orientation="horizontal">
 
         <!--播放/暂停-->
         <ImageView
@@ -138,12 +138,11 @@
         android:layout_centerInParent="true"
         android:visibility="gone" />
 
-    <ImageView
-        android:id="@+id/superplayer_resume"
-        android:layout_width="40dp"
-        android:layout_height="40dp"
-        android:layout_centerInParent="true"
-        android:src="@drawable/superplayer_ic_vod_play_normal"
-        android:visibility="visible" />
+    <com.tencent.liteav.demo.superplayer.ui.player.TimeOutPlayer
+        android:id="@+id/superplayer_timeout_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:showControl="false" />
 
 </RelativeLayout>

+ 4 - 0
kit/src/main/res/values/attrs.xml

@@ -25,4 +25,8 @@
         <attr name="bottomLeftRadius" format="dimension" />
         <attr name="bottomRightRadius" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="TimeOutPlayer">
+        <attr name="showControl" format="boolean" />
+    </declare-styleable>
 </resources>

+ 9 - 0
ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerActivity.kt

@@ -75,12 +75,21 @@ class PlayerActivity : AppCompatActivity(),
         initData()
         updateList()
         viewModel.loadConfig(this)
+        observe()
+    }
 
+    private fun observe() {
         viewModel.toastStr.observe(this) {
             Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
         }
+
+        viewModel.timeout.observe(this) {
+            mSuperPlayerView.setTimeOut(it)
+        }
+
     }
 
+
     override fun onBackPressed() {
         if (isFullScreen) {
             mSuperPlayerView.switchPlayMode(SuperPlayerDef.PlayerMode.WINDOW)

+ 69 - 22
ui/src/main/kotlin/com/tencent/liteav/demo/player/viewmodel/PlayerViewModel.kt

@@ -1,7 +1,6 @@
 package com.tencent.liteav.demo.player.viewmodel
 
 import android.content.Context
-import android.content.SharedPreferences
 import android.util.Log
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
@@ -12,6 +11,7 @@ import com.tencent.liteav.demo.superplayer.SuperPlayerModel
 import com.tencent.liteav.demo.superplayer.database.entity.CountDown
 import com.tencent.liteav.demo.superplayer.database.entity.History
 import com.tencent.liteav.demo.superplayer.database.repo.PlayerRepository
+import com.tencent.liteav.demo.superplayer.util.CountDownUtil
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
@@ -21,19 +21,21 @@ import java.util.*
 class PlayerViewModel(
     private val courseId: String,
     private val repository: PlayerRepository
-) : ViewModel(), PlayerTimerUtil.Listener {
+) : ViewModel(), PlayerTimerUtil.Listener, CountDownUtil.OnTickListener {
 
     init {
         PlayerTimerUtil.addListener(this)
     }
 
-    private var countDown: CountDown? = null
+    private lateinit var countDown: CountDown
 
-    private val countDownLiveData: MutableLiveData<Long> = MutableLiveData()
+    private var countDownUtil: CountDownUtil? = null
 
-    public val toastStr: MutableLiveData<String> = MutableLiveData()
+    val toastStr: MutableLiveData<String> = MutableLiveData()
 
-    public fun loadConfig(context: Context) = viewModelScope.launch {
+    val timeout: MutableLiveData<Boolean> = MutableLiveData()
+
+    fun loadConfig(context: Context) = viewModelScope.launch {
         val common = context.getSharedPreferences("player_timer", Context.MODE_PRIVATE)
         val defaultType = common.getInt("type", 0);
         val defaultValue = common.getInt("value", 0);
@@ -43,10 +45,18 @@ class PlayerViewModel(
             value = defaultValue,
         )
         val special = repository.findHasCountDown(courseId).first()
+
+        if (special == null) {
+            countDown = default
+        } else {
+            countDown = special
+        }
+
+        setCountDown(countDown)
     }
 
     /* 保存一条观看时间段的记录 */
-    public fun saveHistory(history: History) = viewModelScope.launch(Dispatchers.IO) {
+    fun saveHistory(history: History) = viewModelScope.launch(Dispatchers.IO) {
         repository.insertHistory(history)
     }
 
@@ -57,10 +67,16 @@ class PlayerViewModel(
 
     /* 开启定时器 */
     private fun setCountDown(countDown: CountDown) {
+        checkTimeout()
+
         if (countDown.type == CountDown.TYPE_EPISODE) {
 
         } else if (countDown.type == CountDown.TYPE_DURATION) {
-
+            countDownUtil?.cancel()
+            countDownUtil = CountDownUtil.build(1000, countDown.value, this)
+            countDownUtil?.start()
+        } else {
+            countDownUtil?.cancel()
         }
     }
 
@@ -79,21 +95,13 @@ class PlayerViewModel(
     override fun onListen(type: Int, value: Int) {
         Log.d("PVM", "onListen: $type , $value")
 
-        countDown = if (countDown == null) {
-            CountDown(
-                courseId = courseId,
-                type = type,
-                value = value
-            )
-        } else {
-            countDown!!.copyWith(
-                courseId = courseId,
-                type = type,
-                value = value
-            )
-        }
+        countDown = countDown.copyWith(
+            courseId = courseId,
+            type = type,
+            value = value
+        )
 
-        countDown!!.let {
+        countDown.let {
             setCountDown(it)
             saveCountDown(it)
         }
@@ -104,6 +112,45 @@ class PlayerViewModel(
         PlayerTimerUtil.removeListener(this)
     }
 
+    override fun onPause(count: Int, period: Long) {
+
+    }
+
+    override fun onTimeout(count: Int, period: Long) {
+        countDownUtil?.cancel()
+        sendTimeout(true)
+    }
+
+    private fun checkTimeout() {
+        countDown.also {
+            viewModelScope.launch {
+                if (it.type == CountDown.TYPE_DURATION) {
+                    checkDurationTimeout()
+                } else if (it.type == CountDown.TYPE_EPISODE) {
+                    checkEpisodeTimeout();
+                } else {
+                    sendTimeout(false)
+                }
+            }
+        }
+    }
+
+    private suspend fun checkEpisodeTimeout() = viewModelScope.launch(Dispatchers.IO) {
+
+    }
+
+    private suspend fun checkDurationTimeout() = viewModelScope.launch(Dispatchers.IO) {
+        var history = repository.queryTodayDurationHistory(courseId)
+
+    }
+
+
+    private fun sendTimeout(state: Boolean) {
+        viewModelScope.launch(Dispatchers.Main) {
+            timeout.value = state
+        }
+    }
+
 }
 
 class PlayerViewModelFactory(