Skip to content

web获取媒体流

1. 录屏和截图

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webRTC</title>
</head>
<body>
  <!-- 录屏 -->
  <video id="localVideo" autoplay playsinline muted></video>
  <!-- 截图列表 -->
  <div id="imgContainer"></div>
  <button id="screenshotBtn" style="position: fixed;right: 0;top: 50%;">截图</button>

  <script>
    const constraints = {
      audio: true,
      video: { width: 1280, height: 720 }
    }

    getLocalStream(constraints)


    // 获取本地音视频流
    async function getLocalStream(constraints) {
      // 获取媒体流
      const stream = await navigator.mediaDevices.getUserMedia(constraints)
      // 将媒体流设置到 video 标签上播放
      playLocalStream(stream)
    }

    // 播放本地视频流
    function playLocalStream(stream) {
      const videoEl = document.getElementById('localVideo')
      videoEl.srcObject = stream
    }

  </script>
  <script>
    var imgList = []
    var screenshotBtn = document.getElementById('screenshotBtn')
    screenshotBtn.addEventListener('click', generatePhotos)
    // 拍照
    function generatePhotos () {
      // 添加滤镜
      var filterList = [
        'blur(5px)', // 模糊
        'brightness(0.5)', // 亮度
        'contrast(200%)', // 对比度
        'grayscale(100%)', // 灰度
        'hue-rotate(90deg)', // 色相旋转
        'invert(100%)', // 反色
        'opacity(90%)', // 透明度
        'saturate(200%)', // 饱和度
        'saturate(20%)', // 饱和度
        'sepia(100%)', // 褐色
        'drop-shadow(4px 4px 8px blue)', // 阴影
      ]
      var videoEl = document.getElementById('localVideo')
      var canvas = document.createElement('canvas')
      canvas.width = videoEl.videoWidth
      canvas.height = videoEl.videoHeight
      var ctx = canvas.getContext('2d')
      // 原图
      ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height)
      imgList.push(canvas.toDataURL('image/png'))
      for (let i = 0; i < filterList.length; i++) {
        ctx.filter = filterList[i]
        ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height)
        imgList.push(canvas.toDataURL('image/png'))
      }
      insertImgs(imgList)
    }

    function insertImgs(list) {
      const container = document.createDocumentFragment
      var imgContainer  = document.getElementById('imgContainer'); 
      var fragment = document.createDocumentFragment();

      list.forEach((img) => {
          var imgEl = document.createElement('img');
          imgEl.src = img;
          fragment.appendChild(imgEl);
      });

      imgContainer.appendChild(fragment)
    }
  </script>
</body>
</html>

2. 录屏

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webRTC</title>
</head>
<body>
  <h2>录屏</h2>
  <video id="screenVideo" autoplay playsinline></video>
  <button id="recorderStartBtn" style="position: fixed;right: 0;top: 46%;">开始录制</button>
  <button id="recorderStopBtn" style="position: fixed;right: 0;top: 48%;">停止录制</button>
  <button id="recorderPauseBtn" style="position: fixed;right: 0;top: 52%;">暂停录制</button>
  <button id="recorderResumeBtn" style="position: fixed;right: 0;top: 54%;">继续录制</button>
  
  
  <script>
    // 录屏

    // 获取视频流
    var localStream
    shareScreen()
    
    async function shareScreen() {
      localStream =  await navigator.mediaDevices.getDisplayMedia({
        audio: true,
        video: true,
      })
      // 播放录屏视频流
      playStream(localStream)
    }

    // 在视频标签中播放视频流
    function playStream(stream) {
      const video = document.querySelector('#screenVideo')
      video.srcObject = stream
    }

    
  </script>

  <script>
    // 录屏保存

    // 这里我们使用 MediaRecorder 来进行录制,它是一个用于录制媒体流的 API,它可以将媒体流中的数据进行录制,然后将录制的数据保存成一个文件。
    // 由于 MediaRecorder api 的对 mimeType 参数的支持是有限的。所以我们需要通过 MediaRecorder.isTypeSupported 来判断当前浏览器是否支持我们需要的 mimeType
    function getSupportedMimeTypes() {
      const media = 'video'
      // 常用的视频格式
      const types = [
        'webm',
        'mp4',
        'ogg',
        'mov',
        'avi',
        'wmv',
        'flv',
        'mkv',
        'ts',
        'x-matroska',
      ]
      // 常用的视频编码
      const codecs = ['vp9', 'vp9.0', 'vp8', 'vp8.0', 'avc1', 'av1', 'h265', 'h264']
      // 支持的媒体类型
      const supported = []
      const isSupported = MediaRecorder.isTypeSupported
      // 遍历判断所有的媒体类型
      types.forEach((type) => {
        const mimeType = `${media}/${type}`
        codecs.forEach((codec) =>
          [
            `${mimeType};codecs=${codec}`,
            `${mimeType};codecs=${codec.toUpperCase()}`,
          ].forEach((variation) => {
            if (isSupported(variation)) supported.push(variation)
          }),
        )
        if (isSupported(mimeType)) supported.push(mimeType)
      })
      return supported
    }

    console.log(getSupportedMimeTypes())

    function getRecorder () {
      const kbps = 1024
      const Mbps = kbps * kbps
      const options = {
        audioBitsPerSecond: 128000,
        videoBitsPerSecond: 2500000,
        mimeType: 'video/webm; codecs="vp8,opus"',
      }
      // https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder
      return new MediaRecorder(localStream, options)
    }

    var mediaRecorder = getRecorder()

    // 开始录制
    var recorderStartBtn = document.getElementById('recorderStartBtn')
    recorderStartBtn.addEventListener('click', handleRecorderStart)
    // 暂停录制
    var recorderPauseBtn = document.getElementById('recorderPauseBtn')
    recorderPauseBtn.addEventListener('click', handleRecorderPause)
    // 继续录制
    var recorderResumeBtn = document.getElementById('recorderResumeBtn')
    recorderResumeBtn.addEventListener('click', handleRecorderResume)
    // 停止录制
    var recorderStopBtn = document.getElementById('recorderStopBtn')
    recorderStopBtn.addEventListener('click', handleRecorderStop)

    // 录制媒体流
    function handleRecorderStart() {
      // 开始录制媒体,这个方法调用时可以通过给timeslice参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块,而不是以默认的方式录制一个非常大的整块内容。
      mediaRecorder.start()
      // 调用它用来处理 dataavailable 事件,该事件可用于获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象.)
      mediaRecorder.ondataavailable = (e) => {
        // 将录制的数据合并成一个 Blob 对象
        // const blob = new Blob([e.data], { type: e.data.type })
        // 🌸重点是这个地方,我们不要把获取到的 e.data.type设置成 blob 的 type,而是直接改成 mp4
        const blob = new Blob([e.data], { type: 'video/mp4' })
        downloadBlob(blob)
      }
      mediaRecorder.onstart = (e) => {
        console.log("开始录制", e)
      }
      mediaRecorder.onstop = (e) => {
        console.log("停止录制", e)
      }
      mediaRecorder.onpause = (e) => {
        console.log("暂停录制", e)
      }
      mediaRecorder.onresume = (e) => {
        console.log("继续录制", e)
      }
      
    }
    
    function handleRecorderPause() {
      mediaRecorder.pause()
    }
    
    function handleRecorderResume() {
      mediaRecorder.resume()
    }
    
    function handleRecorderStop() {
      mediaRecorder.stop()
    }


    // 下载 Blob
    function downloadBlob(blob) {
      // 将 Blob 对象转换成一个 URL 地址
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      // 设置 a 标签的 href 属性为刚刚生成的 URL 地址
      a.href = url
      // 设置 a 标签的 download 属性为文件名
      a.download = `${new Date().getTime()}.${blob.type.split('/')[1]}`
      // 模拟点击 a 标签
      a.click()
      // 释放 URL 地址
      URL.revokeObjectURL(url)
    }

  </script>
</body>
</html>

3. 保存(下载)录像(录屏)流到本地

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webRTC</title>
</head>
<body>
  <h1>录像</h1>
  <video id="cameraVideo" autoplay playsinline muted></video>
  <button id="recorderStartBtn" style="position: fixed;right: 0;top: 52%;">开始录制</button>
  <button id="recorderPauseBtn" style="position: fixed;right: 0;top: 54%;">暂停录制</button>
  <button id="recorderResumeBtn" style="position: fixed;right: 0;top: 56%;">继续录制</button>
  <button id="recorderStopBtn" style="position: fixed;right: 0;top: 58%;">停止录制</button>

  <script>
    // 录像
    const constraints = {
      audio: true,
      video: { width: 1280, height: 720 }
    }

    var cameraStream
    getLocalStream(constraints)


    // 获取本地音视频流
    async function getLocalStream(constraints) {
      // 获取媒体流
      cameraStream = await navigator.mediaDevices.getUserMedia(constraints)
      // 将媒体流设置到 video 标签上播放
      playLocalStream(cameraStream)
    }

    // 播放本地视频流
    function playLocalStream(stream) {
      const videoEl = document.getElementById('cameraVideo')
      videoEl.srcObject = stream
    }

  </script>

  <script>
    // 录屏保存
    // 这里我们使用 MediaRecorder 来进行录制,它是一个用于录制媒体流的 API,它可以将媒体流中的数据进行录制,然后将录制的数据保存成一个文件。
    // 由于 MediaRecorder api 的对 mimeType 参数的支持是有限的。所以我们需要通过 MediaRecorder.isTypeSupported 来判断当前浏览器是否支持我们需要的 mimeType

    function getSupportedMimeTypes() {
      const media = 'video'
      // 常用的视频格式
      const types = [
        'webm',
        'mp4',
        'ogg',
        'mov',
        'avi',
        'wmv',
        'flv',
        'mkv',
        'ts',
        'x-matroska',
      ]
      // 常用的视频编码
      const codecs = ['vp9', 'vp9.0', 'vp8', 'vp8.0', 'avc1', 'av1', 'h265', 'h264']
      // 支持的媒体类型
      const supported = []
      const isSupported = MediaRecorder.isTypeSupported
      // 遍历判断所有的媒体类型
      types.forEach((type) => {
        const mimeType = `${media}/${type}`
        codecs.forEach((codec) =>
          [
            `${mimeType};codecs=${codec}`,
            `${mimeType};codecs=${codec.toUpperCase()}`,
          ].forEach((variation) => {
            if (isSupported(variation)) supported.push(variation)
          }),
        )
        if (isSupported(mimeType)) supported.push(mimeType)
      })
      return supported
    }

    console.log(getSupportedMimeTypes())

    var mediaRecorder
    function getRecorder () {
      const kbps = 1024
      const Mbps = kbps * kbps
      const options = {
        audioBitsPerSecond: 128000,
        videoBitsPerSecond: 2500000,
        mimeType: 'video/webm; codecs="vp8,opus"',
      }
      // https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder
      return new MediaRecorder(cameraStream, options)
    }

    // 开始录制
    var recorderStartBtn = document.getElementById('recorderStartBtn')
    recorderStartBtn.addEventListener('click', handleRecorderStart)
    // 暂停录制
    var recorderPauseBtn = document.getElementById('recorderPauseBtn')
    recorderPauseBtn.addEventListener('click', handleRecorderPause)
    // 继续录制
    var recorderResumeBtn = document.getElementById('recorderResumeBtn')
    recorderResumeBtn.addEventListener('click', handleRecorderResume)
    // 停止录制
    var recorderStopBtn = document.getElementById('recorderStopBtn')
    recorderStopBtn.addEventListener('click', handleRecorderStop)

    // 录制媒体流
    function handleRecorderStart() {
      mediaRecorder = getRecorder()
      // 开始录制媒体,这个方法调用时可以通过给timeslice参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块,而不是以默认的方式录制一个非常大的整块内容。
      mediaRecorder.start()
      // 调用它用来处理 dataavailable 事件,该事件可用于获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象.)
      mediaRecorder.ondataavailable = (e) => {
        // 将录制的数据合并成一个 Blob 对象
        // const blob = new Blob([e.data], { type: e.data.type })
        // 🌸重点是这个地方,我们不要把获取到的 e.data.type设置成 blob 的 type,而是直接改成 mp4
        const blob = new Blob([e.data], { type: 'video/mp4' })
        downloadBlob(blob)
      }
      mediaRecorder.onstart = (e) => {
        console.log("开始录制", e)
      }
      mediaRecorder.onstop = (e) => {
        console.log("停止录制", e)
      }
      mediaRecorder.onpause = (e) => {
        console.log("暂停录制", e)
      }
      mediaRecorder.onresume = (e) => {
        console.log("继续录制", e)
      }
      
    }
    
    function handleRecorderPause() {
      mediaRecorder.pause()
    }
    
    function handleRecorderResume() {
      mediaRecorder.resume()
    }
    
    function handleRecorderStop() {
      mediaRecorder.stop()
    }


    // 下载 Blob
    function downloadBlob(blob) {
      // 将 Blob 对象转换成一个 URL 地址
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      // 设置 a 标签的 href 属性为刚刚生成的 URL 地址
      a.href = url
      // 设置 a 标签的 download 属性为文件名
      a.download = `${new Date().getTime()}.${blob.type.split('/')[1]}`
      // 模拟点击 a 标签
      a.click()
      // 释放 URL 地址
      URL.revokeObjectURL(url)
    }

  </script>
</body>
</html>