本文档用于实现从零开始搭建直播问答服务,包括使用直播推流并下发题目、app和appserver交互完成答题的详细过程。



步骤一:准备工作

  1. 开通阿里云 视频直播服务
  2. 登录视频直播控制台,单击 域名管理 > 管理,获取推流和拉流地址。详情参见 直播推流

  3. 提出工单,申请开通在视频直播的推流时添加SEI信号的功能,并附上相关的直播域名和appname信息。
    说明 由于鉴权功能默认为开启状态,您须使用 鉴权URL 才能进行推流,避免被盗链的风险。详情参见 直播鉴权

步骤二:开始直播推流

在获取推流地址后,可进行推流。如用OBS进行推流,参见 OBS推流工具

说明
  • 开启云端添加SEI功能,务必在推流地址中加上参数wm=1
  • 推流开始之后,在“流管理”中查看拉流地址时,您会看到除了目标推流地址之外,还会显示存在一个带有“_sei-copy”字符串的推流地址,请忽略即可。您还是从自行配置的推流地址中去获取拉流地址。

步骤三:答题播放器接入

阿里云播放器SDK支持SEI信号的解析,您可以直接集成使用(参见 SDK下载 )。

  • Android
    • 系统支持

      播放器SDK支持Android4.0以上。手机芯片要求armv7或arm64架构。

    • 运行环境

      推荐开发者使用 Android Studio 作为自己的开发工具,本开发文档也是基于 Android Studio开发环境下进行编写的。

    • 如何导入
      1. 将如下aar添加到libs文件夹中。

      2. 在app的gradle中添加对aar的引用。

    • 播放器使用

      在需要使用播放器SDK的activity里面添加如下方法:

      //初始化播放器(只需调用一次即可,建议在application中初始化)
      AliVcMediaPlayer.init(getApplicationContext());
      
      //创建播放器的实例
      mPlayer = new AliVcMediaPlayer(this, mSurfaceView);
      mPlayer.setSEIDataListener(new MediaPlayer.MediaPlayerSEIDataListener() {
      @Override
      public void onSEI_userUnregisteredData(String data) {
      //这里接收到直播流中的用户数据,解析数据,见下【步骤五:播放器解析题目信息】
             parserData(data);
      }
      });
      mPlayer.setMaxBufferDuration(3 * 1000);
      mPlayer.prepareAndPlay(liveUrl);//准备并且播放
      

      此外,andriod播放器更详细的使用方式介绍请见 基础播放器;andriod播放器更多功能和接口参见 接口文档

  • iOS
    • 系统支持

      播放器SDK支持iOS8.0以上。

    • 运行环境

      建议使用XCode8.0以上版本进行编译。

    • 如何导入
      播放器SDK支持普通导入和Cocoapods两种导入方式,选择一种即可。
      1. 普通导入
        需要导入AliyunPlayerSDK.framework、AliyunLanguageSource.bundle到工程中。
        
        打开工程,选中目标target,依次选择 General > Embededed Binaries , 单击 + 号,单击 Add Other…,基础播放器仅需导入下载好的AliyunPlayerSDK.framework到项目中,并勾选copy选项。

        头文件引入:

        设置 Build Settings > Build Options > Enable BitcodeNO

      2. Cocoapods导入
        添加如下语句加入你的Podfile文件中。
        pod 'AliyunPlayer_iOS/AliyunPlayerSDK'
        


        执行pod install或者pod update后集成SDK到项目工程。
        pod install :每次你编辑你的Podfile(添加、移除、更新)的时候使用
        

        运行项目名.xcworkspace文件打开工程

    • 播放器使用
      -(AliVcMediaPlayer *)mediaPlayer{
      if (!_mediaPlayer) {
          //1.创建播放器
          _mediaPlayer = [[AliVcMediaPlayer alloc] init];
      }
      return _mediaPlayer;
      } 
      -(void)player{
      
      self.contentView.frame = CGRectMake(0, 0, self.view.width, self.view.height-64);
      [self.view addSubview:self.contentView];
      [self.mediaPlayer create:self.contentView];
      self.mediaPlayer.scalingMode = scalingModeAspectFit;
      self.mediaPlayer.circlePlay = YES;
      self.mediaPlayer.dropBufferDuration = 3000;
      self.mediaPlayer.printLog = YES;
      AliVcMovieErrorCode err = [self.mediaPlayer prepareToPlay:[NSURL URLWithString:self.playUrl]];
      //5.判定错误,是否可以播放。
      if (err != ALIVC_SUCCESS) {
          NSLog(@"chinese-播放失败");
      }else{
          [self.mediaPlayer play];
      }
      }
      

      此外,iOS 播放器更详细的使用方式参见 基础播放器;iOS播放器更多功能和接口参见 接口文档

步骤四:题目信息下发(从服务端)

当主持人准备播报题目时,现场工作人员需要调用AppServer的接口下发题目。下发题目的接口中包含以下参数:
  • liveId 表示直播场次。此ID代表了唯一一场直播。
  • questionId 表示需要下发题目的ID。
  • expiredSeconds 答题失效时间(秒)。即多少秒内答题有效。
AppServer在接收到请求后完成以下动作:
  • 根据liveId查到直播流信息,从而调用视频云OpenAPI的接口【详见直播答题方案中的OpenAPI接口定义】,往直播流中添加自定义的SEI信号(里面包含questionId信息);

  • 初始化答题session。一个答题session包含:题目内容、答题失效绝对时间戳、各选项选择人数。

OpenAPI调用示例
https://live.aliyuncs.com?Action=AddTrancodeSEI&Domain=test101.cdnpe.com&App=xxx&Stream=xxx&Text=hahaha&Pattern=frame&Repeat=300&Delay=0&<公共请求参数>
说明 在直播答题这种场景下,参数 Pattern建议取值 frameRepeat建议为3倍的关键帧间隔(示例中为 300)。这样设置可以保证在3个关键帧间隔内,即便发生丢帧,只要播放器收到1帧,即可取得SEI信息。由于SEI在多帧内重复,所以播放器需要做SEI去重的工作。
注意事项
  • 由于推流会产生固定的延时,而现场人员是通过云端服务插入SEI信号,有可能插入SEI的画面比真实插入时间早一点。因此现场人员在活动前需要预先测量出推流延时,从而在插入SEI信号时加入一个固定延迟。
  • 测量推流延时的方法,可以在画面上摆一个秒表。假设在T0时间插入了SEI,而播放器在播放到Ts时间的时钟画面时检测到了SEI,那么推流延时就等于T0 - TsTs < T0)。

    现场主持人在12:00:00时提出问题,假设现场到阿里云的推流延时有1秒,那么现场工作人员应在12:00:01时调用添加SEI的OpenAPI(或者在12:00:00时调用,但是OpenAPI里面的delay参数设置为1000),这样正好保证SEI插入的是12:00:00主持人提出问题时的画面。

  • 客户端在收到SEI信号后,需要向服务器获取问题,这会产生一定的通信耗时。因此在下发题目时,可酌情加大expiredSeconds,比如在原有答题失效时间上+1秒。

步骤五:播放器解析SEI信息

播放器会监听SEI信号的回调,通过播放器的SEI信号回调接口,可以获取到下发过来的SEI信息。
  • Android解析SEI信息
    1. 设置回调
      setSEIDataListener
      public void setSEIDataListener(MediaPlayerSEIDataListener
                  listener)
    2. 监听SEI回调

      MediaPlayer.MediaPlayerSEIDataListenerpublic void onSEIdata(String data)

  • iOS解析SEI信息
    1. 添加监听通知接口
      //直播答题接收的消息
      [[NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(onSeiData:)
                                                  name:AliVcMediaPlayerSeiDataNotification object:self.mediaPlayer];
    2. 处理监听通知中SEI数据:
      - (void)onSeiData:(NSNotification *)notification{   
      NSDictionary* dict = [notification userInfo];
      if (dict) {
         NSString* seiData = [dict objectForKey:@"seiData"];
         if (seiData) {
             NSLog(@"sei data is %@",seiData);
      }

步骤六:APP获取完整题目并展示

APP在获取到SEI中携带的questionId后,向AppServer请求对应的题目内容(请求中包含liveIdquestionId)。

AppServer收到请求后,返回题目内容和剩余答题秒数 remainSeconds。剩余答题秒数通过以下方式计算:
剩余答题秒数 = 答题失效绝对时间戳 - 当前绝对时间戳

APP收到响应后,显示题目,并根据remainSeconds开始倒计时。

步骤七:APP答题计时并提交答案

  • APP显示题目,并根据appServer返回的remainSeconds字段来进行倒计时。如果用户上次答题正确,则可选择选项答题。如果已经淘汰,则不能答题。

  • 当用户点击选项,则用户不能更改选项,同时APP需要向AppSever上报用户选择的答案(请求中包含liveIdquestionIdanswerIduserId)。

  • 当用户没有选择,则不许要上报用户的选择答案,答题结果为失败,下次不能答题。

  • 上报答案之后,APP需要记录下来作为用户的答题结果,下次答题需要根据上次答题的结果判断用户是否能够答题。

步骤八:答题服务收集答案

AppServer收到APP上报的答案,需要做如下处理:

  • 根据上报答案请求中的liveIdquestionId,查找答题session是否存在。若不存在则拒绝。
  • 根据当前时间是否超过答题失效绝对时间,判断答题是否超时,超时则答题无效。
    说明 考虑到APP上传答案存在一定的网络延时,在判断答题是否超时时,可宽限一定时间,如2秒。
  • 统计答题人数:对应选项的答题人数+1。

步骤九:答题结果下发

当主持人宣布答题结束,准备公布答题结果时,现场工作人员需要调用AppServer的接口下发答题结果(请求中包含liveIdquestionId)。

同样地,此时AppServer调用视频云OpenAPI的接口,往直播流中插入SEI信号(包含questionId信息),告知APP可以来请求答题结果。

步骤十:APP展示答题结果,进入下一轮

  • APP播放器获取到显示答题汇总的SEI信息之后,这个时候APP端需要区分是答题SEI信号还是答题汇总SEI信号,区分是答题汇总SEI后,向APPServer请求答题汇总(包括liveIdquestionId)。

  • APP在接收到答题汇总之后,显示汇总对话框,将是否答对以及各个答案的答题人数显示出来。

  • 当显示到一定时间后,隐藏汇总对话框,可以进入下一轮答题。