您可以通过阿里云SDK使用短视频列表播放器,本文提供了搭建Android系统与iOS系统短视频列表播放器的操作步骤、示例及说明。

安装包及Demo下载

播放器SDK安装包及Demo源码Demo源码说明
Android播放器SDK
  • AliyunRenderView:自定义实体类,封装了AliyunPlayer和TextureView,将两者绑定。
  • AliPlayerPool:用于限制播放器对象个数。通过队列,保存了三个AliyunRenderView对象,当RecyclerViewAdapter获取AliyunRenderView 时,会从AliPlayerPool中获取。
  • AliPlayerPreload:通过MediaLoader实现预加载功能。
  • CustomLayoutManager:自定义LayoutMangaer,内部会通过一系列监听回调,来判断界面状态。

Android端关键实现说明

使用多个AliPlayer+MediaLoader(预加载)+预渲染(播放器)的方式实现短视频列表播放,相比使用AliListPlayer的实现方式,AliPlayer支持PlayAuth方式播放,支持多实例,支持m3u8格式的视频播放,使用更加方便灵活。

说明
  • 完整的实现逻辑及代码请参见Demo,以下仅对关键节点作出说明详解。
  • 当创建的AliPlayer实例过多导致内存崩溃时,建议您尝试复用AliPlayer来解决。

列表滑动和播放

列表滑动使用Android系统控件RecyclerView实现,借助辅助类PagerSnapHelper实现一次滑动一页的效果。

视频预加载

Android播放器SDK提供了MediaLoader用于实现预加载功能。在上述Demo中,如果当前播放的视频,buffer时长大于等于5s时,会按照视频源的顺序,从next视频开始加载,当next视频加载完成后,会依次加载next.nextnext.next.next的视频。如果在加载过程中,滑动列表,则会cancel正在加载的视频,并等待当前播放视频的buffer。示例代码如下:

//播放器监听 onInfo 事件
mAliPlayer.setOnInfoListener(infoBean -> {
    //监听 buffer
    if (infoBean.getCode() == InfoCode.BufferedPosition) {
        long buffer = infoBean.getExtraValue();
        if(buffer >= 5000){
            //开始预加载
        }
    }
});
//创建MediaLoader对象
MediaLoader mMediaLoader = MediaLoader.getInstance();
//设置监听
mMediaLoader.setOnLoadStatusListener(new MediaLoader.OnLoadStatusListener() {
    @Override
    public void onError(String s, int i, String s1) {
        //加载失败后,加载下一个URL
        loadNext();
    }

    @Override
    public void onCompleted(String s) {
        //加载完成后,加载下一个URL
        loadNext();
    }

    @Override
    public void onCanceled(String s) {
    }
}

//取消预加载
mMediaLoader.cancel(url);
//开始预加载
mMediaLoader.load(url,duration);

首帧预渲染

使用播放器进行首帧预渲染时,需要首先给播放器设置数据源、渲染View,并调用prepare,当播放器回调onPrepared后立即调用seekTo(0),播放器就会进行首帧渲染。最后,设置滑动播放的时机,并调用start开始播放。示例代码如下:

//播放器设置Surface
mAliPlayer.setSurface(new Surface(surfaceTexture));
//设置prepare监听
mAliPlayer.setOnPreparedListener(() -> {
    //调用seekTo让播放器渲染首帧
    mAliPlayer.seekTo(0);
});

//设置播放源
mAliPlayer.setDataSource(urlSource);
mAliPlayer.prepare();

//选择在合适的时机调用start开始播放
mAliPlayer.start();

iOS端关键实现说明

  1. 创建列表播放器。
    创建列表播放器,并设置相关参数。示例如下:
    //创建播放器(必须)
    self.listPlayer = [[AliListPlayer alloc]init];
    //开启硬解
    self.listPlayer.enableHardwareDecoder = YES;
    //循环播放
    self.listPlayer.loop = YES;
    //自动播放
    self.listPlayer.autoPlay = YES;
    //渲染模式
    self.listPlayer.scalingMode = AVP_SCALINGMODE_SCALEASPECTFIT;
    //代理设置
    self.listPlayer.delegate = self;
    //预加载的清晰度
    self.listPlayer.stsPreloadDefinition = @"FD";
    //显示view
    self.listPlayer.playerView = self.simplePlayScrollView.playView;
    //设置预加载个数。此时会加载3个视频。当前播放的,和上下1个。
    mVideoListPlayer.setPreloadCount(1);
  2. 添加多个播放数据源。
    列表播放器支持URL、点播Vid的数据源(STS方式)。示例如下:
    //使用点播服务的视频源
    [self.listPlayer addVidSource:"点播视频vid" uid:"视频唯一标识"];
    //或者直接使用URL
    [self.listPlayer addUrlSource:"视频URL" uid: "视频唯一标识"];
    重要 在列表播放器内部,视频是通过视频唯一标识区分的。如果添加的视频源的唯一标识一样的话,会导致播放错乱。请确保每次添加数据源的视频唯一标识都不一样。
  3. 开始播放视频。
    播放器提供了以下接口,实现基本的定位播放功能。示例如下:
    //直接跳转到某个视频播放
    [self.listPlayer moveTo:"视频唯一标识"]; //URL源
    [self.listPlayer moveTo:"视频唯一标识" accId:accId accKey:accKey token:token region:region]; //点播Vid源(STS方式)
    //播放上一个视频
    [self.listPlayer moveToPrev:"视频唯一标识"]; //URL源
    [self.listPlayer moveToPrev:"视频唯一标识" accId:accId accKey:accKey token:token region:region]; //点播Vid源(STS方式)
    //播放下一个视频
    [self.listPlayer moveToNext:"视频唯一标识"]; //URL源
    [self.listPlayer moveToNext:"视频唯一标识" accId:accId accKey:accKey token:token region:region]; //点播Vid源(STS方式)
  4. 滑动控制操作。
    1. 点击暂停和播放。示例如下:
      - (void)tap {
          if ([self.delegate respondsToSelector:@selector(AVPSimplePlayScrollViewTapGestureAction:)]) {
              [self.delegate AVPSimplePlayScrollViewTapGestureAction:self];
          }
      }
      - (void)AVPSimplePlayScrollViewTapGestureAction:(AVPSimplePlayScrollView *)simplePlayScrollView {
          if (self.playerStatus == AVPStatusStarted) {
              //如果是播放,则暂停
              [self.listPlayer pause];
          }else if (self.playerStatus == AVPStatusPaused) {
              //如果是暂停,则播放
              [self.listPlayer start];
          }
      }
    2. 滑动控制。
      如果列表滑动速度较快,滑动的位置超过一个,则直接调用moveTo接口列表滑动后的回调中去判定。示例如下:
      - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
          CGFloat indexFloat = scrollView.contentOffset.y/self.frame.size.height;
          NSInteger index = (NSInteger)indexFloat;
          .......
            //往下滑动一格
             if (index - self.currentIndex == 1) {
                  if ([self.delegate respondsToSelector:@selector(AVPSimplePlayScrollView:motoNextAtIndex:)]) {
                      [self.delegate AVPSimplePlayScrollView:self motoNextAtIndex:index];
                  }
              }
              //往上滑动一格
              else if (self.currentIndex - index == 1){
                  if ([self.delegate respondsToSelector:@selector(AVPSimplePlayScrollView:motoPreAtIndex:)]) {
                      [self.delegate AVPSimplePlayScrollView:self motoPreAtIndex:index];
                  }
              }
              //滑动多格
              else {
                  if ([self.delegate respondsToSelector:@selector(AVPSimplePlayScrollView:scrollViewDidEndDeceleratingAtIndex:)]) {
                      [self.delegate AVPSimplePlayScrollView:self scrollViewDidEndDeceleratingAtIndex:index];
                  }
              }
      }
      /**
       滚动事件,移动位置超过一个
       @param simplePlayScrollView simplePlayScrollView
       @param index 移动到第几个
       */
      - (void)AVPSimplePlayScrollView:(AVPSimplePlayScrollView *)simplePlayScrollView scrollViewDidEndDeceleratingAtIndex:(NSInteger)index {
          //找到第几个
          AVPDemoResponseVideoListModel *model = [self findModelFromIndex:index];
          //MoveTo到当前
          [self moveToCurrentModel];
      }
  5. 封面的隐藏时机。
    播放视频时,隐藏列表播放封面隐藏。示例如下:
    -(void)onPlayerEvent:(AliPlayer*)player eventType:(AVPEventType)eventType {
        switch (eventType) {
            case AVPEventPrepareDone: {
            }
                break;
            case AVPEventFirstRenderedStart: {
                //隐藏封面图
                [self.simplePlayScrollView showPlayView];
            }
                break;
            default:
                break;
        }
    }
  6. 销毁播放器。
    播放器使用完后,需要释放播放器,否则会有内存泄漏。示例如下:
    [self.listPlayer stop];
    [self.listPlayer destroy];
    self.listPlayer = nil;