Tutorial: Screen Sharing

Screen Sharing

Function Description

This article mainly introduces how to implement screen sharing function in TRTC Web SDK.

Implementation Process

  1. Start Local Screen Sharing

    const trtcA = TRTC.create();
    await trtcA.enterRoom({
      scene: 'rtc',
      sdkAppId: 140000000, // Fill in your sdkAppId
      userId: 'userA', // Fill in your userId
      userSig: 'userA_sig', // Fill in userSig corresponding to userId
      roomId: 6969
    })
    await trtcA.startScreenShare();
    
  2. Play Remote Screen Sharing

    const trtcB = TRTC.create();
    trtcB.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({ userId, streamType }) => {
      // Main video stream, generally the stream pushed by the camera
      if (streamType === TRTC.TYPE.STREAM_TYPE_MAIN) {
        // 1. Place a div tag with an id of `${userId}_main` on the page to play the main stream in the div tag. The business side can customize the id of the div tag. This is just an example.
        // 2. Play the main video stream
        trtcB.startRemoteVideo({ userId, streamType,  view: `${userId}_main` });
      } else {
        // Sub video stream, generally the stream pushed by screen sharing.
        // 1. Place a div tag with an id of `${userId}_screen` on the page to play the screen sharing in the div tag. The business side can customize the id of the div tag. This is just an example.
        // 2. Play screen sharing
        trtcB.startRemoteVideo({ userId, streamType, view: `${userId}_screen` });
      }
    });
    await trtcB.enterRoom({
      scene: 'rtc',
      sdkAppId: 140000000, // Fill in your sdkAppId
      userId: 'userB', // Fill in your userId
      userSig: 'userB_sig', // Fill in userSig corresponding to userId
      roomId: 6969
    })
    
  3. Start Camera + Screen Sharing at the Same Time

     await trtcA.startLocalVideo();
     await trtcA.startScreenShare();
    
  4. Screen Sharing + System Audio

    System audio collection is supported by Chrome M74+

    • On Windows and Chrome OS, the audio of the entire system can be collected.
    • On Linux and Mac, only the audio of a certain page can be collected.
    • Other Chrome versions, other systems, and other browsers are not supported.
     await trtcA.startScreenShare({ option: { systemAudio: true }});
    

    Check Share audio in the pop-up dialog box, and the system audio will be mixed with the local microphone and published. Other users in the room will receive the TRTC.EVENT.REMOTE_AUDIO_AVALIABLE event.

  5. Stop Screen Sharing

     // Stop screen sharing collection and publishing
     await trtcA.stopScreenShare();
     // Other users in the room will receive the TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE event, and streamType is TRTC.TYPE.STREAM_TYPE_SUB.
     trtcB.on(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, ({ userId, streamType }) => {
        if (streamType === TRTC.TYPE.STREAM_TYPE_SUB) {
        }
     })
    

    In addition, users may also stop screen sharing through the browser's own button, so the screen sharing stream needs to listen for the screen sharing stop event and respond accordingly.

    // Listen for screen sharing stop event
    trtcA.on(TRTC.EVENT.SCREEN_SHARE_STOPPED, () => {
      console.log('screen sharing was stopped');
    });
    

Electron uses TRTC Web SDK for screen sharing

The preferred recommendation is to use the TRTC Electron SDK

Because Electron does not implement the browser-supported WebRTC standard getDisplayMedia interface, if a web page contains WebRTC screen share-related logic, it will not be possible to use the TRTC Web SDK for screen share in Electron as a normal browser. If you only want to use the TRTC Web SDK, please refer to the following solution.

To implement screen share, you need to use Electron's API:desktopCapturer.getSources({ types: ['window', 'screen'] })

  1. The main process main.js listens for the page to finish loading, gets the list of screen share sources via desktopCapturer.getSources, and sends the list of screen share sources to the rendering process.
  2. The rendering process listens to the main process events to get a list of screen share sources.
  3. Create screen share streams and push them through the TRTC Web SDK.
    1. Get the screen-sharing MediaStream from the system API via navigator.mediaDevices.getUserMedia().
    2. Pass the custom captured videoTrack in the option parameter of TRTC.startScreenShare() to push the screen sharing stream.
// 【1】main.js Master process gets list of screen share sources
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) {
      const sources = await desktopCapturer.getSources({
        types: ["window", "screen"],
      });
      win.webContents.send("SEND_SCREEN_SHARE_SOURCES", sources);
    }
  });
}
// 【2】Render process listens to main process events to get a list of screen share sources
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】Rendering process push screen share
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, // screen share source id
          minWidth: 1280,
          maxWidth: 1280,
          minHeight: 720,
          maxHeight: 720
        }
      }
    });
    const trtc = TRTC.create();
    await trtc.enterRoom({
      // ...
    });
    await trtc.startScreenShare({
      option: {
        videoTrack: stream.getVideoTracks()[0],
      }
    })
  } catch (error) {
    console.error('start screen share error = ', error)
  }
}

Precautions

  1. What is the main/sub video stream?
  2. The SDK uses the 1080p parameter configuration by default to collect screen sharing. For details, refer to the interface: startScreenShare

Common Issues

  1. Safari screen sharing error getDisplayMedia must be called from a user gesture handler

    This is because Safari restricts the getDisplayMedia screen capture interface, which must be called within 1 second of the callback function of the user click event.

    Reference: webkit issue.

    // good
    async function onClick() {
      // It is recommended to execute the collection logic first when onClick is executed
      await trtcA.startScreenShare();
      await trtcA.enterRoom({ 
        roomId: 123123,
        sdkAppId: 140000000, // Fill in your sdkAppId
        userId: 'userA', // Fill in your userId
        userSig: 'userA_sig', // Fill in userSig corresponding to userId
      });
    }
    // bad
    async function onClick() {
      await trtcA.enterRoom({ 
        roomId: 123123,
        sdkAppId: 140000000, // Fill in your sdkAppId
        userId: 'userA', // Fill in your userId
        userSig: 'userA_sig', // Fill in userSig corresponding to userId
      });
      // Entering the room may take more than 1s, and the collection may fail
      await trtcA.startScreenShare();
    }
    
  2. macOS Monterey(12.2.1), device permissions need to be requested in the master process

    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}`
      );
    }
    
  3. Mac Chrome screen sharing fails with the error message "NotAllowedError: Permission denied by system" or "NotReadableError: Could not start video source" when screen recording is already authorized. Chrome bug. Solution: Open 【Settings】> Click 【Security & Privacy】> Click 【Privacy】> Click 【Screen Recording】> Turn off Chrome screen recording authorization > Reopen Chrome screen recording authorization > Close Chrome browser > Reopen Chrome browser.

  4. WebRTC screen sharing known issues and workarounds