EasyDarwin / EasyPlayer.js

EasyPlayer.js H5播放器,是一款免费的能够同时支持HTTP、RTMP、HTTP-FLV、HLS(m3u8)直播与点播等多种协议,支持H.264、H.265、AAC等多种音视频编码格式,支持mse、asm、wasm等多种解码方式,支持Windows、Linux、Android、iOS全平台终端的H5播放器。EasyPlayer.js H5 Player support HTTP/RTMP/HTTP-FLV/HLS(m3u8) live streaming & vod streaming,support H.264/H.265/AAC video & audio codec,support mse/asm/wasm decode mode,support Windows/Linux/Android/iOS platform,EasyPlayer.js uses leading-edge technology.
http://www.tsingsee.com/
1.66k stars 313 forks source link

未来会考虑加入支持播放器快照截图吗? #51

Open zypy333 opened 3 years ago

zypy333 commented 3 years ago

will snapshot be spported in the future?

Likeadust commented 3 years ago

暂时还没计划

guygubaby commented 3 years ago

查了资料有3种办法可以从一个webgl上面截图,但是我们没法修改源码,经过不断尝试,我发现一种很hacky的办法,成功率几乎100%

大致流程

  1. find out the canvas in player elem
  2. loop until reach max_retry_count (10) or capture image successed
  3. get blob (url)
// 获取截取到的pixels
const generatePixelsFromWebgl = webGlCanvas => {
  if (!webGlCanvas) return
  const gl = webGlCanvas.getContext('webgl', { preserveDrawingBuffer: true })
  const { drawingBufferHeight: height, drawingBufferWidth: width } = gl
  const pixels = new Uint8Array(4 * width * height)
  gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
  return pixels
}
// 判断像素是不是空的
const judgeWetherCaptureSuccess = pixels => {
  if (!pixels) return false
  return !pixels.subarray(0, 128).every(pixel => pixel === 0) // 如果全部是0就是失败
}
// 等到下一帧绘制之前
const waitToNextFrame = () => {
  return new Promise(resolve => {
    requestAnimationFrame(resolve)
  })
}

/**
 * 该方法参数可以自行修改
 * @param {easy player 实例} player
 * @param {当前播放的设备名称,只是用来命名截图得到的图片} deviceName
 */
export const setH265PlayerCover = async function(player, deviceName) {
  let MAX_CAPTURE_RETRY_COUNT = 10
  const IMAGE_FORMAT = 'jpeg'

  return new Promise(async (resolve, reject) => {
    const playerUI = player?.playerUI
    if (!playerUI) return reject(new Error('player ui is null'))

    let isSuccess = false
    let canvas = null

    while (MAX_CAPTURE_RETRY_COUNT > 0 && !isSuccess) {
      // 还有尝试次数并且没有成功就再次执行
      MAX_CAPTURE_RETRY_COUNT -= 1

      await waitToNextFrame()

      canvas = playerUI.querySelector('canvas')
      if (!canvas) {
        return reject(new Error('cavans is null'))
      }
      const pixels = generatePixelsFromWebgl(canvas)
      isSuccess = judgeWetherCaptureSuccess(pixels)
    }

    if (isSuccess) {
      canvas.toBlob(
        async blob => {
          const snapFileName = `${deviceName}-${Date.now()}.${IMAGE_FORMAT}`
          const url = URL.createObjectURL(blob)
          const res = {url, name: snapFileName}
          resolve(res)
        },
        `image/${IMAGE_FORMAT}`,
        0.75
      )
    } else {
      reject(new Error('reach max retry count'))
    }
  })
}

@zypy333