Skip to content

超级播放器实现原理概述

ZERO edited this page Apr 4, 2019 · 3 revisions

超级播放器实现原理

  • 基于腾讯云 LiveAV SDK 播放器 TXVodPlayer 、 TXLivePlayer 封装
  • 代码开源,采用 MVC 结构。代码结构清晰,易于修改定制。

SuperPlayerView View核心类

  • 实现超级播放器的核心 View ,采用 MVC 模式实现逻辑解耦
  • 有三个 Controller 对 SuperPlayerView 进行管理,分别是:TCVodControllerLarge、TCVodControllerSmall、TCVodControllerFloat
  • TCVodControllerLarge 管理全屏状态时候的播放
  • TCVodControllerSmall 管理小屏状态时候的播放
  • TCVodControllerFloat 管理悬浮状态时候的播放

OnSuperPlayerViewCallback 回调

  • SuperPlayerView 暴露的回调
  • onStartFullScreenPlay、onStopFullScreenPlay 实现全屏播放的关键,实现其他 View 隐藏,实现全屏播放
  • onStartFloatWindowPlay 开始悬浮模式播放的回调
  • onClickFloatCloseBtn 悬浮模式下点击 x 按钮的回调,可以实现自己的逻辑
  • onClickSmallReturnBtn 小窗模式下点击 < 返回的回调,可以实现自己的逻辑

egg.

@Override
public void onStartFullScreenPlay() {
    // 隐藏其他元素实现全屏
    mLayoutTitle.setVisibility(GONE); 
    if (mIvAdd != null) {
        mIvAdd.setVisibility(GONE);
    }
}
@Override
public void onStopFullScreenPlay() {
    // 恢复原有元素
    mLayoutTitle.setVisibility(VISIBLE);
    if (mIvAdd != null) {
        mIvAdd.setVisibility(VISIBLE);
    }
}
@Override
public void onClickFloatCloseBtn() {
    // 点击悬浮窗关闭按钮,那么结束整个播放
    mSuperPlayerView.resetPlayer();
    finish();
}
@Override
public void onClickSmallReturnBtn() {
    // 点击小窗模式下返回按钮,开始悬浮播放
    showFloatWindow();
}
@Override
public void onStartFloatWindowPlay() {
    // 开始悬浮播放后,直接返回到桌面,进行悬浮播放
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addCategory(Intent.CATEGORY_HOME);
    startActivity(intent);
}

实现全屏播放原理概述

SuperPlayerView小窗模式下仅仅占用一小块地方,点击后是如何实现全屏播放的?

  • 利用反射构造铺满的LayoutParams
try {
    // 依据上层Parent的LayoutParam类型来实例化一个新的fullscreen模式下的LayoutParam
    Class parentLayoutParamClazz = getLayoutParams().getClass();
    Constructor constructor = parentLayoutParamClazz.getDeclaredConstructor(int.class, int.class);
    mLayoutParamFullScreenMode = (ViewGroup.LayoutParams) constructor.newInstance(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
} catch (Exception e) {
    e.printStackTrace();
}

会利用反射回去父类的LayoutParams, 然后宽高设置MATCH_PARENT,备用。

  • 外部实现onStartFullScreenPlay回调,开始全屏播放的时候,将其他元素全部隐藏
@Override
public void onStartFullScreenPlay() {
    mLayoutTitle.setVisibility(GONE);
    if (mIvAdd != null) {
        mIvAdd.setVisibility(GONE);
    }
}
  • SuperPlayerView设置步骤1准备的全屏LayoutParams
//请求全屏模式
if (requestPlayMode == SuperPlayerConst.PLAYMODE_FULLSCREEN) {
    TXCLog.i(TAG, "requestPlayMode FullScreen");
    if (mLayoutParamFullScreenMode == null)
        return;
    removeView(mVodControllerSmall);
    addView(mVodControllerLarge, mVodControllerLargeParams);
    setLayoutParams(mLayoutParamFullScreenMode);
    rotateScreenOrientation(SuperPlayerConst.ORIENTATION_LANDSCAPE);
    if (mPlayerViewCallback != null) {
        mPlayerViewCallback.onStartFullScreenPlay();
    }
}

实现悬浮播放概述

SuperPlayerView 是如何实现悬浮播放的?

  • 往 WindowManager 直接塞 View 进去
mWindowManager = (WindowManager) mContext.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
mWindowParams = new WindowManager.LayoutParams();
  mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    mWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
SuperPlayerGlobalConfig.TXRect rect = prefs.floatViewRect;
mWindowParams.x = rect.x;
mWindowParams.y = rect.y;
mWindowParams.width = rect.width;
mWindowParams.height = rect.height;
try {
    mWindowManager.addView(mVodControllerFloat, mWindowParams);
} catch (Exception e) {
    Toast.makeText(SuperPlayerView.this.getContext(), "悬浮播放失败", Toast.LENGTH_SHORT).show();
    return;
}
TXCloudVideoView videoView = mVodControllerFloat.getFloatVideoView();
if (videoView != null) {
    if (mCurrentPlayType == SuperPlayerConst.PLAYTYPE_VOD) {
        mVodPlayer.setPlayerView(videoView);
    } else {
        mLivePlayer.setPlayerView(videoView);
    }
    resume();
}

需要特别注意的是悬浮窗权限