This topic explains how to implement screen sharing in your Harmony application.
Overview
The screen sharing feature lets users share their screen in real time with others in a channel during video calls or live streaming.
Sample project
ARTC provides an open-source sample project for your reference: Implement screen sharing on Harmony.
Prerequisites
Before you implement screen sharing, ensure you meet the following requirements:
You have a valid Alibaba Cloud account and have created an ARTC application. For instructions, see Create an application. You can obtain the App ID and App Key from the Application Management console.
You have integrated the ARTC SDK into your project and implemented basic real-time audio and video features. For instructions on SDK integration, see Integrate the SDK. To implement audio and video features, see Implement a video call.
Procedure
The following diagram illustrates the API call sequence for implementing screen sharing:
If you only need to push the screen stream in your scenario, you must explicitly call publishLocalVideoStream to stop pushing the camera stream because the SDK pushes the camera stream by default. The process is as follows:
Before joining the channel, call
publishLocalVideoStream(false)to disable camera stream publishing.After joining the channel, call
startScreenShareto start screen capture and publish the screen sharing stream.
mAliRtcEngine.publishLocalVideoStream(false);Publish screen and camera streams
If your scenario requires publishing both the camera stream and the screen sharing stream, follow these steps:
Call
publishLocalVideoStream(true)to enable camera stream publishing. This is the default behavior and can be omitted.After joining the channel, call
startScreenShareto start screen capture and publish the screen sharing stream.
mAliRtcEngine.publishLocalVideoStream(true);Set the screen stream encoder configuration
To customize the encoding properties of the screen sharing video stream, call setScreenShareEncoderConfiguration. You can configure properties such as resolution, frame rate, bitrate, GOP, and video rotation.
If you only need to set the configuration once per session, we recommend calling it before joining the channel.
You can call this method multiple times to update the configuration.
The following table describes the configuration parameters.
Parameter | Description | Default |
| The video resolution. | 0x0. This means the publishing resolution matches the screen capture resolution. The maximum value is 3840x2160. |
| The video frame rate. | The default frame rate is 5. The maximum value is 30. |
| The video encoding bitrate in Kbps. Note: The effective bitrate depends on the resolution and frame rate. If you set a value outside the reasonable range, the SDK automatically adjusts it. | 512 |
| The key frame interval (GOP), in milliseconds (ms). | 0. This means the SDK controls the key frame interval internally. |
| Specifies whether to force the encoder to generate key frames at the exact interval you set. | false.
|
| The rotation of the published stream. | The default value is AliRtcRotationMode_0. You can select 0, 90, 180, or 270 degrees. |
The following code shows an example:
private aliRtcScreenShareConfig: AliRtcVideoEncoderConfiguration | undefined;
this.aliRtcScreenShareConfig = new AliRtcVideoEncoderConfiguration();
this.aliRtcScreenShareConfig.dimensions.width = 720
this.aliRtcScreenShareConfig.dimensions.height = 1080
this.aliRtcScreenShareConfig.frameRate = this.screenShareEncoderConfig.fps;
this.aliRtcScreenShareConfig.bitrate = this.screenShareEncoderConfig.bitrate;
this.aliRtcScreenShareConfig.keyFrameInterval = this.screenShareEncoderConfig.gop;
this.aliRtcScreenShareConfig.forceStrictKeyFrameInterval = this.screenShareEncoderConfig.forceGOP ? 1 : 0;
// Set the screen share encoder configuration
this.rtcEngine.setScreenShareEncoderConfiguration(this.aliRtcScreenShareConfig);View the remote screen stream
When a user starts screen sharing, other users in the channel receive an onRemoteTrackAvailableNotify callback notification. Through this callback, the client can detect real-time changes to a remote user's audio and video streams, including when a camera video stream or a screen sharing video stream is published or stopped. The application can then use the callback parameters to dynamically create or remove corresponding rendering views to display and manage the remote screen sharing content.
The AliRtcVideoTrack enum in the onRemoteTrackAvailableNotify callback indicates the state of the remote video stream and has the following values:
AliRtcVideoTrackNo (0): No video stream. The remote user is not publishing a video stream.AliRtcVideoTrackCamera (1): Camera video stream only.AliRtcVideoTrackScreen (2): Screen sharing video stream only.AliRtcVideoTrackBoth (3): The remote user is publishing both camera and screen sharing video streams.
Your application must check this enum to adjust the UI dynamically, for example, by showing or hiding views for the camera and screen streams.
this.rtcEventListener.onRemoteTrackAvailableNotify((userId: string, audioTrack: AliRtcAudioTrack,
videoTrack: AliRtcVideoTrack) => {
console.info(`Remote A/V stream available: userId=${userId}, audioTrack=${audioTrack}, videoTrack=${videoTrack}`);
// If the video track is Screen or Both, call setRemoteViewConfig to configure the display for the screen sharing stream.
if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackCamera) {
this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
this.removeRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
} else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackScreen) {
this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
this.removeRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
} else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackBoth) {
this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
} else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackNo) {
this.removeAllRemoteVideo(userId);
}
});
// View the remote video
private viewRemoteVideo(uid: string, videoTrack: AliRtcVideoTrack): void {
// Check if the same stream already exists
const existingIndex = this.remoteVideoStreams.findIndex(
s => s.uid === uid && s.videoTrack === videoTrack
);
if (existingIndex >= 0) {
return;
}
// Create a new remote video stream object
const newStream: RemoteVideoStream = {
uid,
videoTrack,
canvas: new AliRtcVideoCanvas(),
xcomponentController: new XComponentController(),
surfaceId: ''
};
// Add to the array and trigger a UI update
this.remoteVideoStreams = [...this.remoteVideoStreams, newStream];
// Immediately try to set up the video
setTimeout(() => {
this.setupRemoteVideo(newStream);
}, 100);
}
// Set up the remote video
private setupRemoteVideo(stream: RemoteVideoStream): void {
if (!this.rtcEngine) {
console.error('ARTC engine not initialized');
return;
}
try {
// Get the surfaceId of the XComponent
stream.surfaceId = stream.xcomponentController.getXComponentSurfaceId();
if (!stream.surfaceId) {
console.warn(`Failed to get surfaceId: ${stream.uid}_${stream.videoTrack}`);
return;
}
// Configure the video canvas
if (!stream.canvas) {
stream.canvas = new AliRtcVideoCanvas();
}
stream.canvas.surfaceId = stream.surfaceId;
stream.canvas.renderMode = AliRtcRenderMode.AliRtcRenderModeAuto;
stream.canvas.mirrorMode = AliRtcRenderMirrorMode.AliRtcRenderMirrorModeAllNoMirror;
// Key step: Set the remote view configuration
this.rtcEngine.setRemoteViewConfig(
stream.canvas,
this.componentController,
stream.uid,
stream.videoTrack
);
// Make sure to subscribe to this video stream
if (stream.videoTrack === AliRtcVideoTrack.AliRtcVideoTrackCamera) {
this.rtcEngine.subscribeRemoteVideoStream(stream.uid, stream.videoTrack, true);
} else if (stream.videoTrack === AliRtcVideoTrack.AliRtcVideoTrackScreen) {
// Special handling for the screen sharing stream
this.rtcEngine.subscribeRemoteVideoStream(stream.uid, stream.videoTrack, true);
console.info(`Subscribed to screen sharing stream: ${stream.uid}`);
}
} catch (error) {
}
}Start screen capture
Call startScreenShare to start capturing the device's screen and publishing the capture to the channel. Configure the following parameter based on your use case:
mode: The screen sharing mode. You can choose to share audio only, video only, or both.
Note: When your application calls this method, the system displays an authorization pop-up asking for permission to capture the screen. The user must grant this permission before screen sharing can begin.
this.rtcEngine.startScreenShare(mode);Stop screen capture
Call stopScreenShare to stop capturing and publishing the screen and to release the associated resources.
this.rtcEngine.stopScreenShare();