SDK开发
Xp2p录像功能列表

Xp2p录像功能列表

在此章节,我们将介绍Xp2p实时视频的各个功能,以及如何使用它们。

  • 该章节的代码中,将用this.player来引用播放器的实例,app代表小程序实例
  • 本章节只介绍具体功能点,不介绍如何初始化。初始化流程请参考 历史录像流程
  • wxml中的播放器配置如下
<iot-p2p-playback-player id="{{playerId}}" 
  class="player" 
  deviceInfo="{{XP2PDeviceInfo}}" 
  xp2pInfo="{{xp2pInfo}}" 
  streamChannel="{{1}}" 
  muted="{{muted}}" 
  videoInfo="{{videoInfo}}" 
  bind:timeupdate="onTimeUpdate"
  progressInterval="{{3000}}"
  bind:playstart="onPlayStateEvent"
  bind:playpause="onPlayStateEvent"
  bind:playresume="onPlayStateEvent"
  bind:playstop="onPlayStateEvent"
  bind:playend="onPlayStateEvent">
      <view wx:if="{{isFullScreen}}" 
      slot="inner" 
      class="player-fullscreen-tools">
          <view class="player-fullscreen-tools__tool" 
          bind:tap="switchFullScreen">退出全屏</view>
      </view>
</iot-p2p-playback-player>
  • this.data中的配置如下
data: {
    playerId: 'p2p-player',
    muted: true,
    XP2PDeviceInfo: null,
    videoInfo: {
        startTime: '',
        endTime: ''
    },
    currentPlayTime: {
        time: 0,
        timeText: ''
    },
    activeDateList: [],
    currentDate: '',
    recordVideos: [],
 
    isFullScreen: false,
    speed: 1,
 
    isPlaySuccess: false,
    isPlayLoading: false,
    isPlayError: false,
    isPaused: false,
    isPlayerFull: false
}

1. 切换静音模式

switchMute() {
  this.setData({
    muted: !this.data.muted
  })
}

2. 切换全屏模式

switchFullScreen() {
  if (!this.data.isFullScreen) {
      player.requestFullScreen({
          direction: 90,
      });
  } else {
      player.exitFullScreen();
  }
}

3. 查询SD卡状态

getSdcardStatus() {
  app.imcamWx.getSdcardStatus({
    sn: 'xxx' // 此处填入实际SN
  }).then(res => {
    if (res.errCode === 0) {
      const sdcardInfo = res.sdcardInfo
      if (!sdcardInfo.isHaveSdcard) return wx.showToast({
          title: '设备未插入sd卡',
      })
      else if (sdcardInfo.sdcardNeedFormat) return wx.showToast({
          title: 'SD卡状态异常,需要格式化',
      })
      else wx.showToast({
          title: 'SD卡状态正常',
      })
    }
  })
}

4. 获取历史视频日期列表

  • 通过sendUserCommand方法来获取
  • topic固定传'history'
  • data中mode固定传0,begin_time固定传0,end_time传当前时间戳
  • 如果res返回code === 3,则表示观看人数达到上限
getActiveDateList() {
  app.xp2pManager
      .sendUserCommand(this.data.XP2PDeviceInfo.deviceId, {
          cmd: {
              topic: `history`,
              data: {
                  mode: 0,
                  begin_time: 0,
                  end_time: Math.round(new Date().valueOf() / 1000)
              }
          },
      })
      .then(res => {
        if (res?.code === 3) return wx.showToast({
            title: '观看人数达到上限',
        })
        const dateList = res?.data?.value?.data
        if (!dateList || dateList.length === 0) return wx.showToast({
            title: '没有历史视频',
        })
        const activeDateList = dateList.map(item => {
            return {
                date: new Date(item * 1000).toLocaleDateString('en-CA', {
                    year: 'numeric',
                    month: '2-digit',
                    day: '2-digit'
                }),
            }
        })
        this.setData({
          activeDateList
        })
      })
}

5. 获取指定日期的历史视频列表

  • 通过sendUserCommand方法来获取
  • topic固定传'history'
  • data中mode固定传1,begin_time固定传0,end_time传指定日期的时间戳
  • 如果res返回code === 3,则表示观看人数达到上限
getVideosFromDate(date) {
  const today = new Date(date)
  today.setUTCHours(0)
  today.setUTCMinutes(0)
  today.setUTCSeconds(0)
  today.setUTCMilliseconds(0)
  const todayValue = Math.round(today.valueOf() / 1000) // 当天的00:00时间戳
  const tomorrow = new Date(today.valueOf() + 24 * 60 * 60 * 1000)
  const tomorrowValue = Math.round(tomorrow.valueOf() / 1000) // 第二天的00:00时间戳
  app.xp2pManager
      .sendUserCommand(this.data.XP2PDeviceInfo.deviceId, {
          cmd: {
              topic: `history`,
              data: {
                  mode: 1,
                  begin_time: todayValue,
                  end_time: tomorrowValue
              }
          },
      })
      .then(async res => {
        if (res?.code === 3) return wx.showToast({
            title: '观看人数达到上限', 
        })
        const data = res.data
        const parseRes = await app.imcamWx.parseRecordVideoListData({ // 调用API来解析数据
            data: res.data,
            todayValue
        })
        if (parseRes.errCode === 0) {
            const recordVideos = parseRes.recordVideos
            this.setData({
                recordVideos,
                currentDate: date
            })
        } else {
            wx.showToast({
                title: parseRes.errMsg
            })
        }
      })
}

6. 播放指定视频文件

  • 该方法传参的video为recordVideos中的video对象
  • 如果是第一次播放,则直接设置videoInfo
  • 如果不是第一次播放,则发送sendUserCommand
changeVideo(video) {
  if (!this.data.videoInfo.startTime) { // 若没有startTime,说明是第一次播放
    this.setData({
      videoInfo: video
    })
  }else { // 若不是第一次播放,则不要替换videoInfo,而是发送userData来通知设备切换流
    // 需要判断是否因为onTimeUpdate触发了暂停,如果是则恢复播放
    if (this.isPlayEnd) {
      this.isPlayEnd = false
      this.player().resume()
    }
 
 
    app.xp2pManager.sendUserCommand(this.data.XP2PDeviceInfo.deviceId, {
        cmd: {
            topic: `history`,
            data: {
                mode: 2,
                begin_time: video.startTime,
            }
        }
    })
  }
  this.setData({
    currentPlayTime: {
      time: video.startTime,
      timeText: new Date(video.startTime * 1000).toLocaleTimeString('en-GB', {
                    hour: '2-digit',
                    minute: '2-digit',
                    second: '2-digit',
                })
    }
  })
}

7. 更新播放时间

  • 通过ontimeUpdate来更新播放时间
  • 在wxml中可通过progressInterval设置从设备端拿播放时间的频率。建议不要设置太频繁,最低设置1000毫秒比较好
onTimeUpdate({ // 设备回调的当前视频播放的时间戳
    detail
}) {
    const currentTime = detail.currentTime
    if (currentTime === 0) {  // 返回0代表从这往后已经没有视频文件了
        wx.showToast({
            title: '当天录像已播放完毕',
        })
        this.player.pause() // 暂停播放。如果不暂停的话,xp2p播放器会自动重新载入流,会不符合预期
        // 如果此处引入了暂停播放,则在changeVideo处需要做继续播放的处理
        this.isPlayEnd = true
    } else {
        const timeStamp = new Date(currentTime * 1000)
        this.setData({
            currentPlayTime: {  // 更新播放时间。主要用于UI显示
                time: currentTime,
                timeText: new Date(timeStamp).toLocaleTimeString('en-GB', {
                              hour: '2-digit',
                              minute: '2-digit',
                              second: '2-digit',
                          })
            }
        })
    }
}

8. 切换播放速度

  • 通过sendUserCommand来切换播放速度
  • topic固定传'speed'
  • data中的value固定传1或者2。代表1倍/2倍
switchSpeed(){
    const speed = this.data.speed
    this.setData({
        speed: speed === 1 ? 2 : 1
    })
    app.xp2pManager.sendUserCommand(this.data.XP2PDeviceInfo.deviceId, {
        cmd: {
            topic: `speed`,
            data: {
                value: speed
            }
        }
    })
}

9. 截屏

snapShot() {
  this.player.snapshot()
    .then(res => {
      const filePath = res.tempImagePath
      wx.saveImageToPhotosAlbum({
        filePath,
        success: () => {
          wx.showToast({
            title: '截图已保存至手机相册'
          })
        }
      })
    })
}

10. 录屏

11. 错误处理

1. 播放器出错时的重试
  • 通过onPlayStateEvent来监听播放状态
  • 调用player.retry()来重试
onPlayStateEvent({
    type,
    detail
}) {
  console.log(`历史播放器状态变更。类型:${type}${detail ? '详情:' + JSON.stringify(detail) : ''}`);
  if (type === 'playstart') {
    this.setData({
      isPlaySuccess: false,
      isPlayError: false,
      isPlayLoading: true,
      isPlayerFull: false,
      isPaused: false
    })
  } else if (type === 'playsuccess') {
    this.setData({
      isPlaySuccess: true,
      isPlayError: false,
      isPlayLoading: false,
      isPlayerFull: false,
      isPaused: false
    })
  } else if (type === 'playpause') {
    this.setData({
      isPlaySuccess: true,
      isPlayError: false,
      isPlayLoading: false,
      isPlayerFull: false,
      isPaused: true
    })
  } else if (type === 'playresume') {
    this.setData({
      isPlaySuccess: false,
      isPlayError: false,
      isPlayLoading: true,
      isPlayerFull: false,
      isPaused: false
    })
  } else if (type === 'playstop' || type === 'playend') {
    this.setData({
      isPlaySuccess: false,
      isPlayError: false,
      isPlayLoading: false,
      isPlayerFull: false,
      isPaused: false
    })
  } else if(type === 'playerror') {
    this.setData({
      isPlaySuccess: false,
      isPlayError: true,
      isPlayLoading: false,
      isPlayerFull: false,
      isPaused: false
    });
  }
}
 
retry() {
  this.player.retry()
}
2. 开始P2P通道时判断通道是否已满
  • 请在每次p2p通道建立成功后调用本方法,避免出现通道已满的情况
  • 固定用法,使用sendUserCommand
  • topic固定传'channel_full'
  • data固定传空
checkIsChannelFull() {
  app.xp2pManager
      .sendUserCommand(this.data.XP2PDeviceInfo.deviceId, {
          cmd: {
              topic: `channel_full`,
              data: {},
          },
      })
      .then(res => {
        if (res[0]?.status === '405' || res[0]?.status === 405) {
          this.setData({
            isPlaySuccess: false,
            isPlayError: false,
            isPlayLoading: false,
            isPlayerFull: true,
            isPaused: false
          })
          wx.showToast({
            title: '通道已满,无法继续播放',
          })
        }
      })
}
© 2024 Cylan