Day25 [实作] 一对一视讯通话(5): 切换设备

如果我们的电脑有两个摄影机,就会有切换装置的需求,今天我们就来加入这个功能

  1. 在 index.html 增加两个 select 用来选择设备

    <div>
      <label>切换麦克风:</label>
      <select id="audioSource"></select>
    </div>
    
    <div>
      <label>切换摄影机:</label>
      <select id="videoSource"></select>
    </div>
    
  2. 在 main.js 增加切换设备会使用到的标签

    // 切换设备
    const audioInputSelect = document.querySelector('select#audioSource')
    const videoSelect = document.querySelector('select#videoSource')
    const selectors = [audioInputSelect, videoSelect]
    
  3. 读取设备并将设备加入 select 的选项中

    // 将读取到的设备加入到 select 标签中
    function gotDevices(deviceInfos) {
      // Handles being called several times to update labels. Preserve values.
      const values = selectors.map((select) => select.value)
      selectors.forEach((select) => {
        while (select.firstChild) {
          select.removeChild(select.firstChild)
        }
      })
      for (let i = 0; i !== deviceInfos.length; ++i) {
        const deviceInfo = deviceInfos[i]
        const option = document.createElement('option')
        option.value = deviceInfo.deviceId
        if (deviceInfo.kind === 'audioinput') {
          option.text =
            deviceInfo.label || `microphone ${audioInputSelect.length + 1}`
          audioInputSelect.appendChild(option)
        }else if (deviceInfo.kind === 'videoinput') {
          option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`
          videoSelect.appendChild(option)
        } else {
          console.log('Some other kind of source/device: ', deviceInfo)
        }
      }
      selectors.forEach((select, selectorIndex) => {
        if (
          Array.prototype.slice
            .call(select.childNodes)
            .some((n) => n.value === values[selectorIndex])
        ) {
          select.value = values[selectorIndex]
        }
      })
    }
    
    // 读取设备
    navigator.mediaDevices
      .enumerateDevices()
      .then(gotDevices)
      .catch((err) => {
        console.error('Error happens:', err)
      })
    
  4. 选取设备後切换标签并将切换後的媒体传送给对方,这边使用 replaceTrack 去替换 track 不需要重新做协商

    audioInputSelect.onchange = () => {
      switchDevice(true)
    }
    videoSelect.onchange = () => {
      switchDevice(false)
    }
    
    async function switchDevice(isAudio) {
      if (!peerConn) return
      const audioSource = audioInputSelect.value
      const videoSource = videoSelect.value
      const constraints = {
        audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
        video: { deviceId: videoSource ? { exact: videoSource } : undefined },
      }
      const stream = await navigator.mediaDevices.getUserMedia(constraints)
      let track = stream[isAudio ? 'getAudioTracks' : 'getVideoTracks']()[0]
      let sender = peerConn.getSenders().find(function (s) {
        return s.track.kind == track.kind
      })
      console.log('found sender:', sender)
      sender.replaceTrack(track)
    
      localStream = stream
      localVideo.srcObject = stream
    }
    

<<:  Proxmox VE 建立排程备份及制订保留策略

>>:  [Day 26] 应用一:威利在哪里?

Day5 State vs Props

前言 State跟Props这两个东西其实不会很难,却很重要,基本上你在写React的日子里都会一直...

[Lesson20] ButterKnife

ButterKnife可以让我们在宣告元件时之後不用再打findViewById这行,可以帮忙省下宣...

Day23 CC: Tweaked 升级版的电脑

我围绕在「基本版」电脑已经二十多天了 ... 一开始先试着熟悉 Lua 语法,接着玩转电脑周边设备,...

Day 9 - [Zenbo开发系列] 06-安装DDE语料到Zenbo

今天使用的范例出自高焕堂老师的书《AI机器人、蓝芽与Android整合开发技术》,需要完整程序码请参...

[C#] 取得证交所台股价格的 3 种实用方法(附范例下载)

想要在网路上取得台股最新的股价有许多种方式,其中一种免费的方式就是直接向证交所网站取得股价资料,这次...