Tutorial: Enable Video Mixer Plugin

Enable Video Mixer Plugin

Overview

This document provides a detailed guide on how to use the TRTC Web SDK's VideoMixer plugin to composite multiple input sources including camera, screen sharing, text, images, and videos into a single video track for streaming with custom layouts. Developers can flexibly control the layout (position, size, layer zIndex) of each source.

The Workflow of Video Mixer

Prerequisites

  • TRTC Web SDK version >= 5.12.0.

  • Compatibility:

    Browser Compatibility
    PC Chrome
    PC Safari
    PC Firefox
    PC Edge
    Mobile ❌ Mixing not supported yet

Implementation

1. Import and Register Plugin

import { VideoMixer } from 'trtc-sdk-v5/plugins/video-effect/video-mixer';
const trtc = TRTC.create({ plugins: [VideoMixer] });

2. Start Mixing

Start the mixing plugin by calling trtc.startPlugin('VideoMixer', options). You must set the canvas width and height canvasInfo in options, all other parameters are optional.

const options = {
  view: 'local_preview_div', // Preview
  canvasInfo: { width: 1920, height: 1080 },
  camera: [{ // Add a camera input source
      id: 'camera1',
      layout: { x: 0, y: 0, width: 640, height: 480, zIndex: 1 }
    }]
  // ...
}
const { track } = await trtc.startPlugin('VideoMixer', options);
// track is the mixed output MediaStreamTrack, which can be used for streaming

3. Publish After Mixing

The mixing plugin is only responsible for generating the mixed video track. Publishing operation is still controlled by the TRTC SDK. Pass the track returned by startPlugin as a parameter to the startLocalVideo interface to publish.

await trtc.startLocalVideo({ option: { 
  videoTrack: track,
  profile: { width: 1920, height: 1080, bitrate: 2000 } // Set encoding parameters
}});

4. Manage Input Sources (Update and Remove)

Dynamically add, update, or remove mixing input sources by calling trtc.updatePlugin('VideoMixer', options).

Update Rules:

  • Input Source Description: Input sources are divided into: camera, screen, text, image, video. All are arrays, each array element represents an input source, uniquely identified by the id attribute.
  • Update Input Source: Pass the same id and modify other parameters.
  • Add Input Source: Pass a new id object.
  • Remove Input Source: Remove the object from the corresponding input source array.

Camera

Control camera capture and layout.

// Update camera layout with id 'camera1'  
await trtc.updatePlugin('VideoMixer', {
  camera: [
    {
      id: 'camera1',
      profile: { width: 640, height: 480, frameRate: 15 }, // Update camera capture parameters
      layout: { x: 100, y: 0, width: 640, height: 480, zIndex: 1, rotation: 90, mirror: true } // Update camera layout parameters
    }
  ]
});
// Remove all cameras  
await trtc.updatePlugin('VideoMixer', { camera: [] });
// If you only want to temporarily hide the input source on the canvas without physically closing capture, you can set hidden: true in the layout parameter. The same applies to other input sources.

Screen Sharing

// Add screen sharing
await trtc.updatePlugin('VideoMixer', {
  screen: [{
    id: 'screen1',
    profile: { width: 1920, height: 1080, frameRate: 15 }, // Screen sharing capture parameters
    layout: {
      x: 0,
      y: 0,
      width: 1920,
      height: 1080,
      zIndex: 0,      
    }
  }],
  // If the user stops screen sharing via browser button instead of SDK, you can listen through this callback
  onScreenShareStop: (id: string) => console.log(`Screen sharing stopped, id: ${id}`);
});

Text

Add text input sources by passing an object with a unique id in the text parameter array to represent a text source in the mix:

Note: When text content exceeds the layout area, the overflowing part will be clipped. You need to adjust the layout width and height according to text content.

await trtc.updatePlugin('VideoMixer', {
  text: [
    {
      id: 'text1',
      content: 'MultiLine\nTest',
      font: 'bold 60px SimHei',
      color: 'red',
      layout: {
        x: 200,
        y: 300,
        width: 300,
        height: 150,
        zIndex: 6,
      }
    }
  ]
});

Images and Videos

Add images (such as logos) or local video files as mixing input sources.

await trtc.updatePlugin('VideoMixer', {
  image: [{ 
    id: 'img1', 
    url: './image.png',
    layout: { x: 0, y: 500, width: 800, height: 400, zIndex: 4 }
  }],
  video: [{ 
    id: 'video1', 
    url: './video.mp4',
    layout: { x: 0, y: 0, width: 1000, height: 500, zIndex: 5 }
  }]
});

5. Stop Mixing

Remove all input sources and stop mixing:

await trtc.stopPlugin('VideoMixer');

Error Handling

The error handling strategy of the mixing plugin is designed to ensure that errors in some input sources do not affect the mixing of other normal input sources.

Scenario Interface Behavior Error Information Return Method
Partial Success, Partial Failure Interface does not throw errors (try...catch cannot catch) Failure information is notified through result.failedDetails in the return value MixParseResult
All Failed or Critical Error Interface throws error (try...catch can catch) Error object carries failure information, such as error?.data?.failedDetails

Business logic example:

try {
  const { result } = await trtc.updatePlugin('VideoMixer', {
    camera: [/* ... */],
    screen: [/* ... */],
    text: [/* ... */],
    image: [/* ... */],
    video: [/* ... */],
  });
  // Scenario 1: Error handling for partial failures
  result.failedDetails.forEach(({id, error}: {id: string, error: any}) => {
    console.error(`${id} mix failed`, error);
  })
  console.log(result.successOptions) // Successfully applied options for this update
} catch (error: any) {
  // Scenario 2: Error catching for all failures (catching thrown errors)  
  console.error(error);
  error?.data?.failedDetails.forEach(({ id, error }: { id: string, error: any }) => {
    console.error(`${id} mix failed`, error);
  })  
}

Detailed Explanation of successOptions

This return value informs the business side of the parameters actually applied by the mixing underlying layer after this update. When some updates fail, successOptions will include the successfully updated parts and the failed parts that retain the old configuration, making it convenient for the business side to synchronize UI or data status.

API Reference

trtc.startPlugin('VideoMixer', options)

Used to start the mixing plugin and initialize canvas and input sources.

Options

Name Type Required Description
view string | HTMLElement | null Optional HTMLElement instance or Id for mixed video preview. If not passed or passed as null, the mixed video will not be played.
canvasInfo CanvasInfo Required Parameters for setting the mixing canvas
camera CameraSource[] Optional Parameters for controlling camera input sources
screen ScreenSource[] Optional Parameters for controlling screen sharing input sources
text TextSource[] Optional Parameters for controlling text input sources
image ImageSource[] Optional Parameters for controlling image input sources
video VideoSource[] Optional Parameters for controlling video input sources
onScreenShareStop (id: string) => {} Optional Callback invoked when mixed screen sharing stops. May be used when user cancels screen sharing via browser button instead of SDK. The callback parameter is the id of the stopped screen capture

Returns: Promise<MixParseResult>

MixParseResult

Name Type Description
track MediaStreamTrack Mixed output video track
result object Information about the success or failure of the parameters passed in this time, structured as follows:
Name Type Description
successOptions Options Successfully executed options for this update
failedDetails MixFailedDetail[] Detailed information about failed updates

MixFailedDetail

Name Type Description
id string Id of the failed input source
error Error Reason for the failure

trtc.updatePlugin('VideoMixer', options)

Update mixing input sources and layout. The options structure is the same as startPlugin, canvasInfo is not required when updating.

trtc.stopPlugin('VideoMixer')

Stop mixing.

Core Parameter Structures

CanvasInfo

Name Type Required Description
canvasColor string | CanvasGradient | CanvasPattern Optional Mixing background color, refer to canvas fillStyle attribute
width number Required Mixing canvas width
height number Required Mixing canvas height
frameRate number Optional Manually specify mixing frame rate. Generally it's best to let the mixing internally calculate the frame rate automatically, manually specifying may result in performance loss

CameraSource

Name Type Required Description
id string Required Unique id identifying the input source
layout LayerOption Required Layout parameters
cameraId string Optional Camera id for capture, specify which camera to capture
videoTrack MediaStreamTrack Optional Custom captured videoTrack
profile VideoProfile Optional Video capture parameters. Default: 480p_2.

ScreenSource

Name Type Required Description
id string Required Unique id identifying the input source
layout LayerOption Required Layout parameters
profile ScreenShareProfile Optional Screen capture parameters. Default: 1080p.
captureElement HTMLElement Optional Capture a specific HTMLElement on the current page, supports Chrome 104+.
preferDisplaySurface 'current-tab' | 'tab' | 'window' | 'monitor' Optional Controls the display priority of different types of capture in the screen sharing selection box. Default is monitor, i.e., screen capture is prioritized in the screen sharing selection box. If set to 'current-tab', the selection box will only show the current page. Supports Chrome 94+.

TextSource

Name Type Required Description
id string Required Unique id identifying the input source
layout LayerOption Required Layout parameters
content string Required Text content
color string | CanvasGradient | CanvasPattern Optional Text color, refer to canvas fillStyle attribute
font string Optional Font, refer to canvas font attribute

ImageSource

Name Type Required Description
id string Required Unique id identifying the input source
layout LayerOption Required Layout parameters
url string Required Image url

VideoSource

Name Type Required Description
id string Required Unique id identifying the input source
layout LayerOption Required Layout parameters
url string Required Video url

LayerOption

Name Type Required Description
x number Required Horizontal coordinate relative to the upper left corner of the canvas, can be negative
y number Required Vertical coordinate relative to the upper left corner of the canvas, can be negative
width number Required Layout width of input source, must be > 0
height number Required Layout height of input source, must be > 0
zIndex number Required Input source layer level, each input source's layer level should not be duplicated
fillMode 'contain' | 'cover' | 'fill' Optional Mode for filling the layout with the image, camera defaults to cover, other input sources default to contain, refer to CSS object-fit attribute.
mirror boolean Optional Whether to mirror
rotation 0 | 90 | 180 | 270 Optional Image rotation angle
hidden boolean Optional Whether to temporarily hide the input source on the canvas. Use case: don't want to physically close camera/screen, temporarily hide input source without destroying resources

Best Practices and FAQs

1. Frame Rate and Performance Optimization

  • Frame Rate Settings:
    • Not Recommended: manually setting canvasInfo.frameRate.
    • Recommended: let the plugin automatically calculate internally: when camera or screen sharing exists, take the maximum frame rate of the two; otherwise fixed at 15 fps. Manual specification may cause performance loss.
  • Performance Considerations:
    • The larger the canvas resolution and the more mixing input sources, the greater the rendering overhead. The business side should determine appropriate canvas parameters and number of input sources based on user device performance.

2. How to Set Resolution and Bitrate?

  • Resolution is the canvas width and height
  • Bitrate is not controlled by the mixing plugin, it's set by the business side when publishing through startLocalVideo/updateLocalVideo.

3. Why Are Input Source Parameters in Array Form? How to Update Only One Type of Input Source in the Array?

  • Each type of input source is updated in full. The purpose is to support batch updates and deletions (by comparing arrays passed before and after).

  • Each input source parameter is optional. For example, if you only want to update camera, only pass the camera array when calling updatePlugin, and don't pass arrays for other types of input sources, or pass them together but keep the original parameters. Example:

    await trtc.startPlugin('VideoMixer', { // Assume camera and screen are already mixed
      camera: [/* config */],
      screen: [/* config */]
    });
    

    At this point, if you only want to update camera and not screen, you can pass only camera without passing screen

    await trtc.updatePlugin('VideoMixer', {
      camera: [/* newConfig */], // Only pass camera array
    });
    

4. Given an Array, How to Update Only One Input Source?

For example, to update a camera with id = 1, only modify the parameters of the element with id = 1 in the camera array passed in. Although other elements don't need to be updated, their parameters need to be passed in as is.

5. Why Is the Bitrate Set When Capturing Camera and Screen Invalid?

The bitrate set when capturing camera and screen will be ignored. The uplink bitrate is set by the interface (start/updateLocalVideo) called when the business side publishes.

6. Why Doesn't Rotation Happen Around the Center?

The principle of mixing rotation is to rotate the image and then draw it while keeping the upper left corner of the image at the coordinates passed in by layout. If you want to achieve center rotation, the business side needs to adjust the coordinates accordingly.

7. If User Stops Screen Sharing via Browser Button Instead of SDK During Mixing Screen Sharing, How Can the Business Side Detect This?

The onScreenShareStop callback is provided in the parameters of startPlugin. Usage example:

await trtc.startPlugin('VideoMixer', {
  // ...
  onScreenShareStop: (id: string) => {
    console.log(`screen share stop, id: ${id}`);
  }
});