• 03.视频播放器Api说明


    03.视频播放器Api说明

    目录介绍

    • 01.最简单的播放
    • 02.如何切换视频内核
    • 03.切换视频模式
    • 04.切换视频清晰度
    • 05.视频播放监听
    • 06.列表中播放处理
    • 07.悬浮窗口播放
    • 08.其他重要功能Api
    • 09.播放多个视频
    • 10.VideoPlayer相关Api
    • 11.Controller相关Api
    • 12.边播放边缓存api
    • 13.类似抖音视频预加载
    • 14.视频播放器埋点

    00.视频播放器通用框架

    • 基础封装视频播放器player,可以在ExoPlayer、MediaPlayer,声网RTC视频播放器内核,原生MediaPlayer可以自由切换
    • 对于视图状态切换和后期维护拓展,避免功能和业务出现耦合。比如需要支持播放器UI高度定制,而不是该lib库中UI代码
    • 针对视频播放,音频播放,播放回放,以及视频直播的功能。使用简单,代码拓展性强,封装性好,主要是和业务彻底解耦,暴露接口监听给开发者处理业务具体逻辑
    • 该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层
    • 项目地址:github.com/yangchong21…
    • 关于视频播放器整体功能介绍文档:juejin.im/post/688345…

    01.最简单的播放

    • 必须需要的四步骤代码如下所示
      //创建基础视频播放器,一般播放器的功能
      BasisVideoController controller = new BasisVideoController(this);
      //设置控制器
      mVideoPlayer.setVideoController(controller);
      //设置视频播放链接地址
      mVideoPlayer.setUrl(url);
      //开始播放
      mVideoPlayer.start();
      复制代码
    • 开始播放
      //播放视频
      videoPlayer.start();
      复制代码

    02.如何切换视频内核

    • 创建视频播放器
      PlayerFactory playerFactory = IjkPlayerFactory.create();
      IjkVideoPlayer ijkVideoPlayer = (IjkVideoPlayer) playerFactory.createPlayer(this);
      PlayerFactory playerFactory = ExoPlayerFactory.create();
      ExoMediaPlayer exoMediaPlayer = (ExoMediaPlayer) playerFactory.createPlayer(this);
      PlayerFactory playerFactory = MediaPlayerFactory.create();
      AndroidMediaPlayer androidMediaPlayer = (AndroidMediaPlayer) playerFactory.createPlayer(this);
      复制代码
    • 如何配置视频内核
      //播放器配置,注意:此为全局配置,例如下面就是配置ijk内核播放器
      VideoViewManager.setConfig(VideoPlayerConfig.newBuilder()
              .setLogEnabled(true)//调试的时候请打开日志,方便排错
              .setPlayerFactory(IjkPlayerFactory.create())
              .build());
      复制代码
    • 切换视频内核处理代码
      @SuppressLint("SetTextI18n")
      private void setChangeVideoType(@ConstantKeys.PlayerType int type){
          //切换播放核心,不推荐这么做,我这么写只是为了方便测试
          VideoPlayerConfig config = VideoViewManager.getConfig();
          try {
              Field mPlayerFactoryField = config.getClass().getDeclaredField("mPlayerFactory");
              mPlayerFactoryField.setAccessible(true);
              PlayerFactory playerFactory = null;
              switch (type) {
                  case ConstantKeys.VideoPlayerType.TYPE_IJK:
                      playerFactory = IjkPlayerFactory.create();
                      mTvTitle.setText("视频内核:" + " (IjkPlayer)");
                      break;
                  case ConstantKeys.VideoPlayerType.TYPE_EXO:
                      playerFactory = ExoPlayerFactory.create();
                      mTvTitle.setText("视频内核:" + " (ExoPlayer)");
                      break;
                  case ConstantKeys.VideoPlayerType.TYPE_NATIVE:
                      playerFactory = MediaPlayerFactory.create();
                      mTvTitle.setText("视频内核:" + " (MediaPlayer)");
                      break;
                  case ConstantKeys.VideoPlayerType.TYPE_RTC:
                      break;
              }
              mPlayerFactoryField.set(config, playerFactory);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      复制代码

    03.切换视频模式

    • 关于全屏模式相关api
      //进入全屏
      mVideoPlayer.startFullScreen();
      //退出全屏
      mVideoPlayer.stopFullScreen();
      复制代码
    • 关于小窗口播放相关api
      //开启小屏
      mVideoPlayer.startTinyScreen();
      //退出小屏
      mVideoPlayer.stopTinyScreen();
      复制代码

    04.切换视频清晰度

    05.视频播放监听

    • 这个分为两部分:第一部分是播放模式监听,第二部分是播放状态监听,暴露给开发者。这里不建议使用0,1,非常不方便简明之意,采用注解限定。
      mVideoPlayer.setOnStateChangeListener(new OnVideoStateListener() {
          /**
           * 播放模式
           * 普通模式,小窗口模式,正常模式三种其中一种
           * MODE_NORMAL              普通模式
           * MODE_FULL_SCREEN         全屏模式
           * MODE_TINY_WINDOW         小屏模式
           * @param playerState                       播放模式
           */
          @Override
          public void onPlayerStateChanged(int playerState) {
              switch (playerState) {
                  case ConstantKeys.PlayMode.MODE_NORMAL:
                      //普通模式
                      break;
                  case ConstantKeys.PlayMode.MODE_FULL_SCREEN:
                      //全屏模式
                      break;
                  case ConstantKeys.PlayMode.MODE_TINY_WINDOW:
                      //小屏模式
                      break;
              }
          }
      
          /**
           * 播放状态
           * -1               播放错误
           * 0                播放未开始
           * 1                播放准备中
           * 2                播放准备就绪
           * 3                正在播放
           * 4                暂停播放
           * 5                正在缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,缓冲区数据足够后恢复播放)
           * 6                暂停缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,此时暂停播放器,继续缓冲,缓冲区数据足够后恢复暂停
           * 7                播放完成
           * 8                开始播放中止
           * @param playState                         播放状态,主要是指播放器的各种状态
           */
          @Override
          public void onPlayStateChanged(int playState) {
              switch (playState) {
                  case ConstantKeys.CurrentState.STATE_IDLE:
                      //播放未开始,初始化
                      break;
                  case ConstantKeys.CurrentState.STATE_START_ABORT:
                      //开始播放中止
                      break;
                  case ConstantKeys.CurrentState.STATE_PREPARING:
                      //播放准备中
                      break;
                  case ConstantKeys.CurrentState.STATE_PREPARED:
                      //播放准备就绪
                      break;
                  case ConstantKeys.CurrentState.STATE_ERROR:
                      //播放错误
                      break;
                  case ConstantKeys.CurrentState.STATE_BUFFERING_PLAYING:
                      //正在缓冲
                      break;
                  case ConstantKeys.CurrentState.STATE_PLAYING:
                      //正在播放
                      break;
                  case ConstantKeys.CurrentState.STATE_PAUSED:
                      //暂停播放
                      break;
                  case ConstantKeys.CurrentState.STATE_BUFFERING_PAUSED:
                      //暂停缓冲
                      break;
                  case ConstantKeys.CurrentState.STATE_COMPLETED:
                      //播放完成
                      break;
              }
          }
      });
      复制代码

    06.在列表中播放

    • 第一步:初始化视频播放器,创建VideoPlayer对象
      mVideoView = new VideoPlayer(context);
      mVideoView.setOnStateChangeListener(new VideoPlayer.SimpleOnStateChangeListener() {
          @Override
          public void onPlayStateChanged(int playState) {
              //监听VideoViewManager释放,重置状态
              if (playState == ConstantKeys.CurrentState.STATE_IDLE) {
                  PlayerUtils.removeViewFormParent(mVideoView);
                  mLastPos = mCurPos;
                  mCurPos = -1;
              }
          }
      });
      mController = new BasisVideoController(context);
      mVideoView.setController(mController);
      复制代码
    • 第二步:设置RecyclerView和Adapter
      mAdapter.setOnItemChildClickListener(new OnItemChildClickListener() {
          @Override
          public void onItemChildClick(int position) {
              //点击item播放视频
              startPlay(position);
          }
      });
      mRecyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
          @Override
          public void onChildViewAttachedToWindow(@NonNull View view) {
      
          }
      
          @Override
          public void onChildViewDetachedFromWindow(@NonNull View view) {
              FrameLayout playerContainer = view.findViewById(R.id.player_container);
              View v = playerContainer.getChildAt(0);
              if (v != null && v == mVideoView && !mVideoView.isFullScreen()) {
                  //销毁视频
                  releaseVideoView();
              }
          }
      });
      复制代码
    • 第三步:播放视频和销毁视频的逻辑代码
      /**
       * 开始播放
       * @param position 列表位置
       */
      protected void startPlay(int position) {
          if (mCurPos == position) return;
          if (mCurPos != -1) {
              releaseVideoView();
          }
          VideoInfoBean videoBean = mVideos.get(position);
          mVideoView.setUrl(videoBean.getVideoUrl());
          View itemView = mLinearLayoutManager.findViewByPosition(position);
          if (itemView == null) return;
          VideoRecyclerViewAdapter.VideoHolder viewHolder = (VideoRecyclerViewAdapter.VideoHolder) itemView.getTag();
          //把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。
          mController.addControlComponent(viewHolder.mPrepareView, true);
          PlayerUtils.removeViewFormParent(mVideoView);
          viewHolder.mPlayerContainer.addView(mVideoView, 0);
          //播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它
          VideoViewManager.instance().add(mVideoView, "list");
          mVideoView.start();
          mCurPos = position;
      }
      
      private void releaseVideoView() {
          mVideoView.release();
          if (mVideoView.isFullScreen()) {
              mVideoView.stopFullScreen();
          }
          if(getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
              getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
          }
          mCurPos = -1;
      }
      复制代码

    08.其他重要功能Api

    • 设置视频播放器背景图,和视频标题。
      //注意,下面这个controller是指BasisVideoController
      //设置视频背景图
      ImageView thumb = controller.getThumb();
      Glide.with(this).load(R.drawable.image_default).into(controller.getThumb());
      //设置视频标题
      controller.setTitle("视频标题");
      复制代码
    • 判断是否锁屏
      //判断是否锁屏
      boolean locked = controller.isLocked();
      //设置是否锁屏
      controller.setLocked(true);
      复制代码
    • 设置播放视频缩放类型。借鉴于网络博客,类似图片缩放。建议选择16:9类型,最常见
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_16_9);
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_DEFAULT);
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_4_3);
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_MATCH_PARENT);
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_ORIGINAL);
      mVideoPlayer.setScreenScaleType(ConstantKeys.PlayerScreenScaleType.SCREEN_SCALE_CENTER_CROP);
      复制代码

    09.播放多个视频

    • 这个举一个例子,比如同时播放两个视频,当然这种情况在app中可能比较少
      //必须设置
      player1.setUrl(VOD_URL_1);
      VideoPlayerBuilder.Builder builder = VideoPlayerBuilder.newBuilder();
      builder.setEnableAudioFocus(false);
      VideoPlayerBuilder videoPlayerBuilder = new VideoPlayerBuilder(builder);
      player1.setVideoBuilder(videoPlayerBuilder);
      BasisVideoController controller1 = new BasisVideoController(this);
      player1.setController(controller1);
      mVideoViews.add(player1);
      
      //必须设置
      player2.setUrl(VOD_URL_2);
      VideoPlayerBuilder.Builder builder2 = VideoPlayerBuilder.newBuilder();
      builder.setEnableAudioFocus(false);
      VideoPlayerBuilder videoPlayerBuilder2 = new VideoPlayerBuilder(builder2);
      player2.setVideoBuilder(videoPlayerBuilder2);
      BasisVideoController controller2 = new BasisVideoController(this);
      player2.setController(controller2);
      mVideoViews.add(player2);
      复制代码
    • 那么要是页面切换到后台,如何处理多个视频的暂停功能呢?如下所示:
      @Override
      protected void onPause() {
          super.onPause();
          for (VideoPlayer vv : mVideoViews) {
              vv.pause();
          }
      }
      
      @Override
      protected void onResume() {
          super.onResume();
          for (VideoPlayer vv : mVideoViews) {
              vv.pause();
          }
      }
      
      @Override
      protected void onDestroy() {
          super.onDestroy();
          for (VideoPlayer vv : mVideoViews) {
              vv.release();
          }
      }
      
      @Override
      public void onBackPressed() {
          for (VideoPlayer vv : mVideoViews) {
              if (vv.onBackPressed())
                  return;
          }
          super.onBackPressed();
      }
      复制代码

    10.VideoPlayer相关Api

    • 关于视频播放相关的api如下所示
      //暂停播放
      mVideoPlayer.pause();
      //视频缓冲完毕,准备开始播放时回调
      mVideoPlayer.onPrepared();
      //重新播放
      mVideoPlayer.replay(true);
      //继续播放
      mVideoPlayer.resume();
      //调整播放进度
      mVideoPlayer.seekTo(100);
      //循环播放, 默认不循环播放
      mVideoPlayer.setLooping(true);
      //设置播放速度
      mVideoPlayer.setSpeed(1.1f);
      //设置音量 0.0f-1.0f 之间
      mVideoPlayer.setVolume(1,1);
      //开始播放
      mVideoPlayer.start();
      复制代码
    • 关于视频切换播放模式相关api
      //判断是否处于全屏状态
      boolean fullScreen = mVideoPlayer.isFullScreen();
      //是否是小窗口模式
      boolean tinyScreen = mVideoPlayer.isTinyScreen();
      //进入全屏
      mVideoPlayer.startFullScreen();
      //退出全屏
      mVideoPlayer.stopFullScreen();
      //开启小屏
      mVideoPlayer.startTinyScreen();
      //退出小屏
      mVideoPlayer.stopTinyScreen();
      复制代码
    • 关于其他比如获取速度,音量,设置属性相关Api
      //VideoPlayer相关
      VideoPlayerBuilder.Builder builder = VideoPlayerBuilder.newBuilder();
      VideoPlayerBuilder videoPlayerBuilder = new VideoPlayerBuilder(builder);
      //设置视频播放器的背景色
      builder.setPlayerBackgroundColor(Color.BLACK);
      //设置小屏的宽高
      int[] mTinyScreenSize = {0, 0};
      builder.setTinyScreenSize(mTinyScreenSize);
      //是否开启AudioFocus监听, 默认开启
      builder.setEnableAudioFocus(false);
      mVideoPlayer.setVideoBuilder(videoPlayerBuilder);
      //截图
      Bitmap bitmap = mVideoPlayer.doScreenShot();
      //移除所有播放状态监听
      mVideoPlayer.clearOnStateChangeListeners();
      //获取当前缓冲百分比
      int bufferedPercentage = mVideoPlayer.getBufferedPercentage();
      //获取当前播放器的状态
      int currentPlayerState = mVideoPlayer.getCurrentPlayerState();
      //获取当前的播放状态
      int currentPlayState = mVideoPlayer.getCurrentPlayState();
      //获取当前播放的位置
      long currentPosition = mVideoPlayer.getCurrentPosition();
      //获取视频总时长
      long duration = mVideoPlayer.getDuration();
      //获取倍速速度
      float speed = mVideoPlayer.getSpeed();
      //获取缓冲速度
      long tcpSpeed = mVideoPlayer.getTcpSpeed();
      //获取视频宽高
      int[] videoSize = mVideoPlayer.getVideoSize();
      //是否处于静音状态
      boolean mute = mVideoPlayer.isMute();
      复制代码

    11.Controller相关Api

    • Controller控制器相关的Api说明
      //设置视频背景图
      ImageView thumb = controller.getThumb();
      Glide.with(this).load(R.drawable.image_default).into(controller.getThumb());
      //设置视频标题
      controller.setTitle("视频标题");
      //添加自定义视图。每添加一个视图,都是方式层级树的最上层
      CustomErrorView customErrorView = new CustomErrorView(this);
      controller.addControlComponent(customErrorView);
      //移除控制组件
      controller.removeControlComponent(customErrorView);
      //移除所有的组件
      controller.removeAllControlComponent();
      //隐藏播放视图
      controller.hide();
      //显示播放视图
      controller.show();
      //是否开启根据屏幕方向进入/退出全屏
      controller.setEnableOrientation(true);
      //显示移动网络播放提示
      controller.showNetWarning();
      //刘海的高度
      int cutoutHeight = controller.getCutoutHeight();
      //是否有刘海屏
      boolean b = controller.hasCutout();
      //设置是否适配刘海屏
      controller.setAdaptCutout(true);
      //停止刷新进度
      controller.stopProgress();
      //开始刷新进度,注意:需在STATE_PLAYING时调用才会开始刷新进度
      controller.startProgress();
      //判断是否锁屏
      boolean locked = controller.isLocked();
      //设置是否锁屏
      controller.setLocked(true);
      //取消计时
      controller.stopFadeOut();
      //开始计时
      controller.startFadeOut();
      //设置播放视图自动隐藏超时
      controller.setDismissTimeout(8);
      //销毁
      controller.destroy();
      复制代码

    12.边播放边缓存api

    • 如下所示
      controller = new BasisVideoController(this);
      //设置视频背景图
      Glide.with(this).load(R.drawable.image_default).into(controller.getThumb());
      //设置控制器
      mVideoPlayer.setController(controller);
      HttpProxyCacheServer server = new HttpProxyCacheServer(this);
      String proxyVideoUrl = server.getProxyUrl(URL_AD);
      mVideoPlayer.setUrl(proxyUrl);
      mVideoPlayer.start();
      复制代码

    13.类似抖音视频预加载

    • 如下所示,这个是针对ViewPager
      //获取PreloadManager预加载管理者对象
      mPreloadManager = PreloadManager.getInstance(this);
      //在播放视频的时候
      String playUrl = mPreloadManager.getPlayUrl(url);
      VideoLogUtils.i("startPlay: " + "position: " + position + "  url: " + playUrl);
      mVideoPlayer.setUrl(playUrl);
      //在页面滚动的时候
      mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
          @Override
          public void onPageScrollStateChanged(int state) {
              super.onPageScrollStateChanged(state);
              if (state == VerticalViewPager.SCROLL_STATE_IDLE) {
                  mPreloadManager.resumePreload(mCurPos, mIsReverseScroll);
              } else {
                  mPreloadManager.pausePreload(mCurPos, mIsReverseScroll);
              }
          }
      });
      复制代码
    • 如下所示,这个是针对RecyclerView
      recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
          /**
           * 是否反向滑动
           */
          private boolean mIsReverseScroll;
      
          @Override
          public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);
              if (dy>0){
                  //表示下滑
                  mIsReverseScroll = false;
              } else {
                  //表示上滑
                  mIsReverseScroll = true;
              }
          }
      
          @Override
          public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
              super.onScrollStateChanged(recyclerView, newState);
              if (newState == VerticalViewPager.SCROLL_STATE_IDLE) {
                  mPreloadManager.resumePreload(mCurPos, mIsReverseScroll);
              } else {
                  mPreloadManager.pausePreload(mCurPos, mIsReverseScroll);
              }
          }
      });
      复制代码

    14.视频播放器埋点

    • 代码如下所示,写一个类,实现BuriedPointEvent即可。即可埋点视频的播放次数,播放进度,点击视频广告啥的,方便统一管理
    public class BuriedPointEventImpl implements BuriedPointEvent {
    
        /**
         * 进入视频播放
         * @param url                       视频url
         */
        @Override
        public void playerIn(String url) {
    
        }
        
        /**
         * 退出视频播放
         * @param url                       视频url
         */
        @Override
        public void playerDestroy(String url) {
    
        }
    
        /**
         * 视频播放完成
         * @param url                       视频url
         */
        @Override
        public void playerCompletion(String url) {
    
        }
    
        /**
         * 视频播放异常
         * @param url                       视频url
         * @param isNetError                是否是网络异常
         */
        @Override
        public void onError(String url, boolean isNetError) {
    
        }
    
        /**
         * 点击了视频广告
         * @param url                       视频url
         */
        @Override
        public void clickAd(String url) {
    
        }
    
        /**
         * 退出视频播放时候的播放进度百度分
         * @param url                       视频url
         * @param progress                  视频进度,计算百分比【退出时候进度 / 总进度】
         */
        @Override
        public void playerOutProgress(String url, float progress) {
    
        }
    
        /**
         * 视频切换音频
         * @param url                       视频url
         */
        @Override
        public void videoToMedia(String url) {
    
        }
    }
    复制代码

    15.播放器示例展示图

    image image image image image image image image image image


    作者:杨充
    链接:https://juejin.im/post/6884021420293259271
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    博客园20071027上海聚会
    上海招聘.NET(C#)程序员
    招人
    漂亮的后台WebUi框架(有源码下载)
    js插件库系列导航
    PrestoSQL(trinodb)源码分析 执行(下)
    Extjs4 (二)
    Struts2(1)简介
    css中的字体
    什么是REST架构
  • 原文地址:https://www.cnblogs.com/yc211/p/13824643.html
Copyright © 2020-2023  润新知