Electron 使用 TRTC Web SDK 进行屏幕分享

Electron 使用 TRTC Web SDK 进行屏幕分享

本文主要介绍 Electron 中使用 TRTC Web SDK 时,如何进行屏幕分享。

简介

Electron 可以方便的创建窗口,加载网页进行使用。因为 Electron 未实现浏览器支持的 WebRTC 标准的 getDisplayMedia 接口,所以如果网页中包含了 WebRTC 屏幕分享相关的逻辑,将无法在 Electron 中像普通浏览器一样正常使用 TRTC Web SDK 进行屏幕分享。

问题在于 Electron 未实现浏览器支持的 WebRTC 的 getDisplayMedia 接口,导致无法直接在 Electron 中使用 TRTC Web SDK 进行屏幕分享。

在 Electron 中使用 TRTC Web SDK 如何进行屏幕分享呢?

方案

优先建议使用 TRTC Electron SDK,如果只想使用 TRTC Web SDK,方案如下。

实现屏幕分享,需要使用到 Electron 的 API:desktopCapturer.getSources({ types: ['window', 'screen'] })

  1. 主进程 main.js 监听页面加载完成后,通过 desktopCapturer.getSources 获取屏幕分享源列表,并将屏幕分享源列表发送给渲染进程
  2. 渲染进程监听主进程事件,从而获取屏幕分享源列表
  3. 创建屏幕分享流并通过 TRTC Web SDK 进行推流
    1. 通过 navigator.mediaDevices.getUserMedia() 从系统 API 获取屏幕分享的 MediaStream
    2. 通过 TRTC.createStream() 的自定义采集功能,从屏幕流拿到的 videoTrack 创建要发布的屏幕分享流
    3. 推屏幕分享流
// 【1】main.js 主进程获取屏幕分享源列表
const { app, BrowserWindow, desktopCapturer, systemPreferences } = require('electron');
function createWindow () {
  const win = new BrowserWindow({
    // ……
  });
  win.loadFile('./src/index.html');
  win.webContents.on('did-finish-load', async () => {
    if (win) {
      desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
        win.webContents.send('SEND_SCREEN_SHARE_SOURCES', sources);
      });
    }
  });
}
// 【2】渲染进程监听主进程事件拿到屏幕分享源列表
const { ipcRenderer } = require('electron');
let shareSourceList = [];
ipcRenderer.on('SEND_SCREEN_SHARE_SOURCES', async (event, sources) => {
  const selectContainer = window.document.getElementById('screen-share-select');
  shareSourceList = sources;
  sources.forEach(obj => {
    const optionElement = document.createElement('option');
    optionElement.innerText = `${obj.name}`;
    selectContainer.appendChild(optionElement);
  });
})
// 【3】渲染进程推屏幕分享
async function startScreenShare() {
  const selectContainer = document.getElementById('screen-share-select');
  const selectValue = selectContainer.options[selectContainer.selectedIndex].value;
  const [ source ] = shareSourceList.filter(obj => obj.name === `${selectValue}`);
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: false,
      video: {
        mandatory: {
          chromeMediaSource: 'desktop',
          chromeMediaSourceId: source.id, // 屏幕分享源 id
          minWidth: 1280,
          maxWidth: 1280,
          minHeight: 720,
          maxHeight: 720
        }
      }
    });
    const shareClient = TRTC.createClient({
      // ……
    });
    const shareStream = TRTC.createStream({
      videoSource: stream.getVideoTracks()[0]
    });
    await shareStream.initialize();
    await shareClient.join({ roomId: parseInt(roomId) });
    shareStream && await shareClient.publish(shareStream);
  } catch (error) {
    console.error('start screen share error = ', error)
  }
}

常见问题

  • macOS Monterey(12.2.1), 需在主进程中请求设备权限
    async function checkAndApplyDeviceAccessPrivilege() {
      const cameraPrivilege = systemPreferences.getMediaAccessStatus('camera');
      console.log(
        `checkAndApplyDeviceAccessPrivilege before apply cameraPrivilege: ${cameraPrivilege}`
      );
      if (cameraPrivilege !== 'granted') {
        await systemPreferences.askForMediaAccess('camera');
      }
      const micPrivilege = systemPreferences.getMediaAccessStatus('microphone');
      console.log(
        `checkAndApplyDeviceAccessPrivilege before apply micPrivilege: ${micPrivilege}`
      );
      if (micPrivilege !== 'granted') {
        await systemPreferences.askForMediaAccess('microphone');
      }
      const screenPrivilege = systemPreferences.getMediaAccessStatus('screen');
      console.log(
        `checkAndApplyDeviceAccessPrivilege before apply screenPrivilege: ${screenPrivilege}`
      );
    }