Configure and control video

Updated at:
Copy as MD

Configure video capture, encoding, camera control, preview, mirroring, and rendering through the ARTC SDK.

Features

During a video call or interactive streaming, configure capture resolution, encoding resolution, frame rate, bitrate, mirror mode, and render mode.

  • Resolution:

    • Capture resolution: The resolution of the video from a capture device, such as a camera.

    • Encoding resolution: The resolution of the encoded video.

  • Bitrate: The number of bits transmitted per second (bps).

  • Frame rate: The number of frames displayed per second (fps).

Proper resolution, frame rate, and bitrate settings improve video quality. Mirror and render modes control how video is displayed.

Sample code

Basic video usage for Android: Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/VideoBasicUsage/VideoBasicUsageActivity.java.

Basic video usage for iOS: iOS/ARTCExample/BasicUsage/VideoBasicUsage/VideoBasicUsageVC.swift.

Basic video usage for Harmony: Harmony/ARTCExample/entry/src/main/ets/pages/basicusage/VideoBasicUsage.ets.

Prerequisites

Before you configure video settings:

Implementation

1. Set camera capture configuration

Use setCameraCapturerConfiguration to set camera direction and capture frame rate.

Note
  • Call this method before starting the camera, for example, before calling startPreview or joinChannel (which automatically starts the camera).

  • Calling enableLocalVideo(false) to disable camera capture releases the camera resources. You can then reset the configuration.

image

The method is defined as follows:

/**
 * @brief Sets the capture preference.
 * @param cameraCapturerConfiguration The camera capture settings.
 *      - preference:
 *        - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_PREVIEW} Preview priority. Prioritizes video preview quality.
 *        - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE} Performance priority. Prioritizes device performance by selecting a resolution close to that of the published stream.
 *        - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_AUTO} Auto. The SDK automatically adjusts the capture resolution.
 *      - cameraDirection: The camera direction. You can set it to the front or rear camera.
 * @return
 * - 0: Success.
 * - A non-zero value: Failure.
 * @note You must call this method before you start the camera, for example, before you call {@link #startPreview} or {@link #joinChannel}.
 */
public abstract int setCameraCapturerConfiguration(AliEngineCameraCapturerConfiguration cameraCapturerConfiguration);

Parameters:

Parameter

Type

Description

preference

AliRtcCaptureOutputPreference

The capture preference.

  • CAPTURER_OUTPUT_PREFERENCE_AUTO: The SDK automatically adjusts the settings.

  • CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE: Performance priority. The SDK selects capture settings that best match the encoding parameters.

  • CAPTURER_OUTPUT_PREFERENCE_PREVIEW: Preview priority. The SDK selects high-quality camera input parameters to prioritize the preview.

cameraDirection

AliRtcCameraDirection

The camera direction (front or rear).

fps

int

The capture frame rate.

Default value: -1, which means the SDK uses the fps from the encoding configuration (the SDK's internal default is 15).

cameraCaptureProfile

AliRtcCameraCaptureProfile

The video capture resolution.

  • Default: Follows the encoder settings.

  • 1080p.

textureEncode

int

(Android only) Specifies whether to enable texture encoding.

cameraTextureCapture

int

(Android only) Specifies whether to enable texture capture for the camera.

Sample code:

Android

AliRtcEngine.AliEngineCameraCapturerConfiguration config = new AliRtcEngine.AliEngineCameraCapturerConfiguration();
config.preference = AliRtcEngine.AliRtcCaptureOutputPreference.ALIRTC_CAPTURER_OUTPUT_PREFERENCE_AUTO;
config.cameraCaptureProfile = AliRtcEngine.AliRtcCameraCaptureProfile.ALIRTC_CAMERA_CAPTURER_PROFILE_DEFAULT;
config.cameraDirection = AliRtcEngine.AliRtcCameraDirection.CAMERA_FRONT;
config.fps = 30;
mAliRtcEngine.setCameraCapturerConfiguration(config);

iOS

let cameraCaptureConfig: AliRtcCameraCapturerConfiguration = AliRtcCameraCapturerConfiguration()
cameraCaptureConfig.preference = .auto
cameraCaptureConfig.cameraCaptureProfile = .profileDefault
cameraCaptureConfig.cameraDirection = .front
cameraCaptureConfig.fps = 30
engine.setCameraCapturerConfiguration(cameraCaptureConfig)

Harmony

const cameraConfig = new AliRtcCameraCaptureConfiguration();
cameraConfig.preference = AliRtcCaptureOutputPreference.AliRtcCaptureOutputPreferenceAuto;
cameraConfig.cameraDirection = AliRtcCameraDirection.AliRtcCameraDirectionFront;
cameraConfig.cameraCaptureProfile = AliRtcCameraCaptureProfile.AliRtcCameraCaptureProfileDefault;
cameraConfig.fps = 30;
this.rtcEngine?.setCameraCaptureConfiguration(cameraConfig);

macOS

AliRtcCameraCapturerConfiguration *configuration = [[AliRtcCameraCapturerConfiguration alloc] init];
configuration.preference = (AliRtcCaptureOutputPreference)outputBox.indexOfSelectedItem;
configuration.cameraCaptureProfile = AliRtcCameraCaptureProfileDefault;
configuration.fps = 15;
[self.engine setCameraCapturerConfiguration:configuration];

Windows

AliEngineCameraCapturerConfiguration config ;
config.preference = AliEngineCaptureOutputPreferencePerformance;
config.cameraCaptureProfile = AliEngineCameraCaptureProfileDefault;
config.fps = 30;
mAliRtcEngine->setCameraCapturerConfiguration(config);

2. Set video encoder configuration

image

Use setVideoEncoderConfiguration to set video resolution, frame rate, bitrate, and keyframe interval.

Note

Call setVideoEncoderConfiguration before or after joining a channel. If called only once per call, call it before joining.

Parameters:

Parameter

Type

Description

dimensions

AliRtcVideoDimensions

The video resolution. Default: 640x480. Maximum: 1920x1080.

frameRate

int

The video encoding frame rate. Default: 15. Maximum: 30.

bitrate

int

The video bitrate in kbps. Default: 512. Set to 0 for auto-calculation based on resolution and frame rate.

If the bitrate falls outside the recommended range, the SDK adjusts it automatically. Recommended ranges are in the sample code comments.

keyFrameInterval

int

The keyframe interval in milliseconds. Default: 0 (SDK-controlled).

Note

This parameter is required for interactive streaming and for interoperability with a mini program.

forceStrictKeyFrameInterval

boolean

Whether to force strict keyframe generation at the set interval. Default: false.

  • false: The encoder responds to keyframe requests, for example, when another user joins the channel. The keyframe interval may not strictly match the set value.

  • true: The encoder does not respond to other keyframe requests and strictly generates keyframes at the set interval. This may increase the time-to-first-frame for subscribers.

mirrorMode

AliRtcVideoEncoderMirrorMode

The mirror mode for the encoded video.

Note

You can also use the setVideoMirrorMode method to set the mirror mode.

orientationMode

AliRtcVideoEncoderOrientationMode

The orientation mode of the encoded video.

  • Adaptive: The orientation of the output video matches that of the captured video.

  • FixedLandscape: Fixed landscape orientation.

  • FixedPortrait: Fixed portrait orientation.

rotationMode

AliRtcRotationMode

The rotation angle of the encoded video. Valid values are 0, 90, 180, and 270.

codecType

AliRtcVideoCodecType

The codec type.

  • default: Uses the device's default setting.

  • software: Uses software encoding.

  • hardware: Uses hardware encoding.

  • hardwareTexture: Uses hardware texture encoding.

encodeCodecType

AliRtcVideoEncodeCodecType

The video encoding format. Supported formats: system default, H.264, and H.265.

seiForceFrontIFrame

int

Whether to force an I-frame before sending an SEI message.

Valid values: -1 (default, equivalent to 1), 0 (do not force), and 1 (force).

Sample code:

Android

AliRtcEngine.AliRtcVideoEncoderConfiguration aliRtcVideoEncoderConfiguration = new AliRtcEngine.AliRtcVideoEncoderConfiguration();
aliRtcVideoEncoderConfiguration.dimensions = new AliRtcEngine.AliRtcVideoDimensions(720, 1280);
aliRtcVideoEncoderConfiguration.frameRate = 20;
aliRtcVideoEncoderConfiguration.bitrate = 1200;
aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000;
aliRtcVideoEncoderConfiguration.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive;
mAliRtcEngine.setVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);

iOS

let config = AliRtcVideoEncoderConfiguration()
config.dimensions = CGSize(width: 720, height: 1280)
config.frameRate = 20
config.bitrate = 1200
config.keyFrameInterval = 2000
config.orientationMode = AliRtcVideoEncoderOrientationMode.adaptive
engine.setVideoEncoderConfiguration(config)

Harmony

const encoderConfig = new AliRtcVideoEncoderConfiguration();
const dimensions = new AliRtcVideoDimensions();
dimensions.width = 720;
dimensions.height = 1280;
encoderConfig.dimensions = dimensions;
encoderConfig.bitrate = 1200;
encoderConfig.frameRate = 20;
encoderConfig.orientationMode = AliRtcVideoEncoderOrientationMode.AliRtcVideoEncoderOrientationModeAdaptive;
encoderConfig.rotationMode = AliRtcRotationMode.AliRtcRotationMode_0;
// Call the RTC engine to set the configuration.
this.rtcEngine?.setVideoEncoderConfiguration(encoderConfig);

macOS

AliRtcVideoEncoderConfiguration *config = [[AliRtcVideoEncoderConfiguration alloc] init];

config.dimensions = CGSizeMake(720, 1280);
config.frameRate = 20;
config.bitrate = 1200
config.keyFrameInterval = 2000
config.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive;

[self.engine setVideoEncoderConfiguration:config];

Windows

AliEngineVideoEncoderConfiguration aliRtcVideoEncoderConfiguration ;
aliRtcVideoEncoderConfiguration.dimensions = AliRtcVideoDimensions(720, 1280);
aliRtcVideoEncoderConfiguration.frameRate = AliEngineFrameRateFps20;
aliRtcVideoEncoderConfiguration.bitrate = 1200;
aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000;
aliRtcVideoEncoderConfiguration.orientationMode = AliEngineVideoEncoderOrientationModeAdaptive;
mAliRtcEngine->SetVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);

3. Switch cameras

The SDK uses the front camera by default. Call setCameraCaptureConfiguration before starting capture to specify a different camera, or switchCamera to switch at runtime.

/**
 * @brief Switches between the front and rear cameras.
 * @return
 * - `0`: Success
 * - Non-zero: Failure
 * @note This API is available on iOS and Android only.
 */
public abstract int switchCamera();

Sample code:

Android

mSwitchCameraBtn.setOnClickListener(v -> {
    if(mAliRtcEngine != null) {
        mAliRtcEngine.switchCamera();
    }
});

iOS

@IBAction func onCameraDirectionChanged(_ sender: UISegmentedControl) {
    rtcEngine?.switchCamera()
}

Harmony

this.rtcEngine.switchCamera();

4. Control the camera

Two APIs control the camera: muteLocalCamera and enableLocalVideo.

API

muteLocalCamera

enableLocalVideo

How it works

Sends black frames instead of video.

Stops camera capture and releases device resources.

Behavior

The local preview remains active, but remote users see a black screen.

Local preview and remote video freeze on the last frame.

Characteristics

  • Maintains the video stream. No interruption for remote users.

  • Fast switching with low latency. Ideal for frequent video toggling.

  • Releases camera resources.

  • Slower switching with higher latency. Ideal for extended camera-off periods, such as audio-only calls.

4.1. Mute or unmute local video

image

muteLocalCamera keeps the video capture, encoding, and transmission pipeline active but sends black frames to remote users. The local preview is unaffected.

/**
 * @brief Mutes or unmutes the local video stream.
 * @param mute true: Sends black frames. false: Resumes sending normal video frames.
 * @param track The video track to mute. Only {@link AliRtcVideoTrack#AliRtcVideoTrackCamera} is supported.
 * @return
 * - 0: Success.
 * - A non-zero value: Failure.
 * @note This method sends black frames while the local preview remains unaffected. The video capture pipeline continues to run.
 */
public abstract int muteLocalCamera(boolean mute, AliRtcVideoTrack track);
/**
 * @brief Occurs when a remote user mutes or unmutes their video stream.
 * @param uid The ID of the user who called muteLocalCamera.
 * @param isMute
 * - true: The user is sending black frames.
 * - false: The user is sending normal video frames.
 */
public void onUserVideoMuted(String uid ,boolean isMute){}

Android

Mute or unmute the video stream:

if(!isMutedCamera) {
    mAliRtcEngine.muteLocalCamera(true, AliRtcEngine.AliRtcVideoTrack.AliRtcVideoTrackCamera);
    mPublishVideoBtn.setText(R.string.resume_pub_video);
    isMutedCamera = true;
} else {
    mAliRtcEngine.muteLocalCamera(false, AliRtcEngine.AliRtcVideoTrack.AliRtcVideoTrackCamera);
    mPublishVideoBtn.setText(R.string.stop_pub_video);
    isMutedCamera = false;
}

Listen for the remote callback:

@Override
public void onUserVideoMuted(String uid ,boolean isMute){
    handler.post(new Runnable() {
        @Override
        public void run() {
            ToastHelper.showToast(VideoBasicUsageActivity.this, "remote user uid:" + uid + " camera mute:" + isMute, Toast.LENGTH_SHORT);
        }
    });
}

iOS

Mute or unmute the video stream:

@IBAction func onVideoMuteSwitched(_ sender: UISwitch) {
    if sender.isOn {
        // Send black frames.
        rtcEngine?.muteLocalCamera(true, for: AliRtcVideoTrack.camera)
    } else {
        // Resume sending normal video frames.
        rtcEngine?.muteLocalCamera(false, for: AliRtcVideoTrack.camera)
    }
}

Listen for the remote callback:

extension VideoBasicUsageVC: AliRtcEngineDelegate {
    func onUserVideoMuted(_ uid: String, videoMuted isMute: Bool) {
        "onUserVideoMuted: user id \(uid) video muted: \(isMute)".printLog()
    }
}

Harmony

Mute or unmute the video stream:

private toggleVideoSending(): void {
  if (this.rtcEngine) {
    if (!this.isMutedCamera) {
      this.rtcEngine.muteLocalCamera(true, AliRtcVideoTrack.AliRtcVideoTrackCamera);
      this.isMutedCamera = true;
    } else {
      this.rtcEngine.muteLocalCamera(false, AliRtcVideoTrack.AliRtcVideoTrackCamera);
      this.isMutedCamera = false;
    }
  }
}

Listen for the remote callback:

listener.onUserVideoMuted((uid : string, isMute : boolean) => {
  console.info(`User ${uid}'s video has been ${isMute ? 'muted' : 'unmuted'}`);
});

macOS

Mute or unmute the video stream:

[self.engine muteLocalCamera:self.muteLocalCamera forTrack:AliRtcVideoTrackCamera];

Listen for the remote callback:

- (void)onUserVideoMuted:(NSString *_Nullable)uid videoMuted:(BOOL)isMute {
    NSLog(@"uid=%@ mute:%d", uid, isMute );
}

Windows

Mute or unmute the video stream:

if ( mMuteLocalCamera == TRUE) {
    mAliRtcEngine->MuteLocalCamera(TRUE, AliRtcVideoTrackCamera);
} else {
    mAliRtcEngine->MuteLocalCamera(FALSE, AliRtcVideoTrackCamera); 
}

Listen for the remote callback:

void CTutorialDlg::OnUserVideoMuted(const char* uid, bool isMute)
{
    // Find the corresponding user view to update its status.
	PartUserDlg *partUserView = mRemotePartDlgs[uid];
	if (nullptr != partUserView)
	{
		partUserView->muteStream = isMute;
		updateRemoteParticipants();
	}
}

4.2. Enable or disable video capture

image

enableLocalVideo controls the camera. Disabling it stops capture and transmission, releasing the device.

/**
 * @brief Enables or disables local video capture.
 * @param enabled
 * - true: Enables local video capture.
 * - false: Disables local video capture.
 * @return
 * - 0: Success.
 * - < 0: Failure.
 * @note This feature is enabled by default. Listen for the {@link AliRtcEngineNotify#onUserVideoEnabled} callback to be notified of changes to a remote user's video capture status.
 */
public abstract int enableLocalVideo(boolean enabled);
/**
 * @brief Occurs when a remote user enables or disables their local video capture.
 * @param uid The ID of the user who called enableLocalVideo.
 * @param isEnable
 * - true: The user enabled local video capture.
 * - false: The user disabled local video capture.
 */
public void onUserVideoEnabled(String uid, boolean isEnable){}

Android

Enable or disable video capture:

mCameraSwitchBtn = findViewById(R.id.camera_control_btn);
mCameraSwitchBtn.setOnClickListener(v -> {
    if(mAliRtcEngine != null) {
        if(isEnableCamera) {
            mAliRtcEngine.enableLocalVideo(false);
            isEnableCamera = false;
            mCameraSwitchBtn.setText(R.string.camera_on);
        } else {
            mAliRtcEngine.enableLocalVideo(true);
            isEnableCamera = true;
            mCameraSwitchBtn.setText(R.string.camera_off);
        }
    }
});

Listen for the remote callback:

@Override
public void onUserVideoEnabled(String uid, boolean isEnable) {
    handler.post(new Runnable() {
        @Override
        public void run() {
            ToastHelper.showToast(VideoBasicUsageActivity.this, "remote user uid:" + uid + " camera enable:" + isEnable, Toast.LENGTH_SHORT);
        }
    });
}

iOS

Enable or disable video capture:

@IBAction func onCameraSwitch(_ sender: UISwitch) {
    if sender.isOn {
        rtcEngine?.enableLocalVideo(true)
    } else {
        rtcEngine?.enableLocalVideo(false)
    }
    updateCaptureUIVisibility()
}

Listen for the remote callback:

extension VideoBasicUsageVC: AliRtcEngineDelegate {
    func onUserVideoEnabled(_ uid: String?, videoEnabled isEnable: Bool) {
        "onUserVideoEnabled: user id \(uid ?? "invalid uid") video enable: \(isEnable)".printLog()
    }
}

Harmony

Enable or disable video capture:

private toggleCamera(): void {
  if (this.rtcEngine) {
    if (this.isEnableCamera) {
      this.rtcEngine.enableLocalVideo(false);
      this.isEnableCamera = false;
    } else {
      this.rtcEngine.enableLocalVideo(true);
      this.isEnableCamera = true;

    }
  }
}

Listen for the remote callback:

listener.onUserVideoEnabled((uid : string, isEnable : boolean) => {
  console.info(`User ${uid}'s video has been ${isEnable ? 'enabled' : 'disabled'}`);
});

macOS

Enable or disable video capture:

[self.engine enableLocalVideo:isOpen];

Listen for the remote callback:

- (void)onUserVideoEnabled:(NSString *_Nullable)uid videoEnabled:(BOOL)isEnable) {
    NSLog(@"uid=%@ enabled:%d", uid, isEnable );
}

Windows

Enable or disable video capture:

mbLocalVideo = !mbLocalVideo;
mAliRtcEngine->EnableLocalVideo(mbLocalVideo);

Listen for the remote callback:

void CTutorialDlg::OnUserVideoEnabled(const char* uid, bool isEnable) {
    // Find the corresponding user view to update its status.
	PartUserDlg *partUserView = mRemotePartDlgs[uid];
	if (nullptr != partUserView)
	{
		partUserView->enableVideo = isEnable;
		updateRemoteParticipants();
	}
}

5. Start or stop preview

Use startPreview and stopPreview to control the local preview.

Note:

  • You must call setLocalViewConfig to set a render view before you start the preview.

  • By default, the SDK starts the preview when you join a channel. If you need to start the preview before joining the channel, call startPreview.

  • After stopping the preview, the local view freezes on the last frame.

API call sequence

image

Sample code:

Android

Start the preview:

private void startPreview() {
    if (mAliRtcEngine != null) {
        ViewGroup.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

        // Set the local view.
        if (mLocalVideoCanvas == null) {
            mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
            SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoBasicUsageActivity.this);
            if (localSurfaceView != null) {
                localSurfaceView.setZOrderOnTop(true);
                localSurfaceView.setZOrderMediaOverlay(true);
                fl_local.addView(localSurfaceView, layoutParams);
                mLocalVideoCanvas.view = localSurfaceView;
                try {
                    mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
                } catch (Exception e) {
                    e.printStackTrace(); // Handle potential exceptions
                }
            }
        }
        // Start the preview.
        mAliRtcEngine.startPreview();
    }
}

Stop the preview:

mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;

iOS

Start the preview:

func startPreview() {
    let seatView = self.createSeatView(uid: self.userId)
    
    let canvas = AliVideoCanvas()
    canvas.view = seatView.canvasView
    canvas.renderMode = .auto
    canvas.mirrorMode = .onlyFrontCameraPreviewEnabled
    canvas.rotationMode = ._0
    
    self.rtcEngine?.setLocalViewConfig(canvas, for: AliRtcVideoTrack.camera)
    self.rtcEngine?.startPreview()
}

Stop the preview:

self.rtcEngine?.stopPreview()
self.rtcEngine?.setLocalViewConfig(nil, for: AliRtcVideoTrack.camera)
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil

Harmony

Start the preview:

if (!this.aliRtcVideoCanvas) {
  this.aliRtcVideoCanvas = new AliRtcVideoCanvas();
}

this.aliRtcVideoCanvas.surfaceId = this.localSurfaceId;
this.aliRtcVideoCanvas.renderMode = AliRtcRenderMode.AliRtcRenderModeAuto;
this.aliRtcVideoCanvas.mirrorMode = AliRtcRenderMirrorMode.AliRtcRenderMirrorModeAllMirror;

this.rtcEngine.setLocalViewConfig(
  this.aliRtcVideoCanvas,
  this.componentController,
  AliRtcVideoTrack.AliRtcVideoTrackCamera
);

this.rtcEngine.startPreview();

Stop the preview:

this.rtcEngine.stopPreview();

Mac

Start the preview:

NSView * newView = [self createSeatView:uid];

AliVideoCanvas *canvas = [[AliVideoCanvas alloc] init];
canvas.renderMode = AliRtcRenderModeAuto;
canvas.view = newView;
canvas.mirrorMode = AliRtcRenderMirrorModeAllDisabled;
canvas.rotation = AliRtcRotationMode_0;

[self.engine setLocalViewConfig:canvas forTrack:AliRtcVideoTrackCamera];
[self.engine startPreview];

Stop the preview:

[self.engine stopPreview];
[self.engine setLocalViewConfig:nil, AliRtcVideoTrackCamera];
[self.engine leaveChannel];
[self.engine destroy];
_engine = nil ;

Windows

Start the preview:

AliEngineVideoCanvas canvas;
canvas.renderMode = AliEngineRenderModeAuto;
canvas.rotation = AliEngineRotationMode_0;
canvas.displayView = hLocalWnd;
mAliRtcEngine->SetLocalViewConfig(canvas, AliEngineVideoTrackCamera);
mAliRtcEngine->StartPreview();

Stop the preview:

mAliRtcEngine->StopPreview();
mAliRtcEngine->SetLocalViewConfig(nullptr, AliEngineVideoTrackCamera);
mAliRtcEngine->LeaveChannel();
mAliRtcEngine->Destroy();
mAliRtcEngine = nullptr;

6. Set the mirror mode

setVideoMirrorMode controls mirroring for the local preview and published stream. Call it at runtime during video calls or live streaming.

/**
 * @brief Sets the mirror mode for the local video preview and the published stream.
 * @param mirrorMode The mirror mode to set.
 * @return
 * - 0: Success.
 * - <0: Failure.
 *  - AliRtcErrInner: An internal SDK state error occurred. Verify that the SDK instance was created successfully.
 *
 * @note
 * - You can call this method to change the mirror mode at runtime, either before or after you join a channel. The SDK records the state and applies the setting when the preview and encoding are active.
 * - This method overrides the mirror settings configured in `setLocalViewConfig` and `setVideoEncoderConfiguration`.
 * - This method's settings duplicate those in `setLocalViewConfig` and `setVideoEncoderConfiguration`. Use only one method to configure mirroring.
 */
public abstract int setVideoMirrorMode(AliRtcVideoPipelineMirrorMode mirrorMode);

Mirror modes:

Enum value

Description

AliRtcVideoPipelineMirrorModeNoMirror

Mirroring is disabled for both preview and encoding.

AliRtcVideoPipelineMirrorModeBothMirror

Enables mirroring for both the preview and encoding (default).

AliRtcVideoPipelineMirrorModeOnlyPreviewMirror

Enables mirroring for the preview only.

AliRtcVideoPipelineMirrorModeOnlyPublishMirror

Enables mirroring for the published stream only.

Sample code:

Android

mMirrorSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
        if(mAliRtcEngine != null) {
            AliRtcEngine.AliRtcVideoPipelineMirrorMode mirrorMode = AliRtcEngine.AliRtcVideoPipelineMirrorMode.values()[position];
            mAliRtcEngine.setVideoMirrorMode(mirrorMode);
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> adapterView) {

    }
});

iOS

let mirrorMode: AliRtcVideoPipelineMirrorMode = {
    switch row {
    case 0: return .bothMirror
    case 1: return .noMirror
    case 2: return .onlyPreviewMirror
    case 3: return .onlyPublishMirror
    default: return .bothMirror
    }
}()
self.rtcEngine?.setVideoMirrorMode(mirrorMode)

Harmony

const mirrorMode = AliRtcVideoPipelineMirrorMode.AliRtcVideoPipelineMirrorModeBothMirror;
this.rtcEngine.setVideoMirrorMode(mirrorMode);

macOS

[self.engine setVideoMirrorMode:AliRtcVideoPipelineMirrorModeOnlyPreviewMirror];

Windows

mAliRtcEngine->setVideoMirrorMode(AliEngineVideoPipelineMirrorModeOnlyPreviewMirror);

7. Set the render view

Call setLocalViewConfig or setRemoteViewConfig to set a render view before displaying video.

// Sets the render view for the local preview.
public abstract int setLocalViewConfig(AliRtcVideoCanvas viewConfig, AliRtcVideoTrack track);
// Sets the render view for a specified remote user.
public abstract int setRemoteViewConfig(AliRtcVideoCanvas canvas, String uid, AliRtcVideoTrack track);

AliRtcVideoCanvas parameters:

Parameter

Type

Description

view

View

The display view (required).

renderMode

AliRtcRenderMode

The render mode.

  • Auto: The SDK automatically selects the render mode.

  • Stretch: Stretches the video to fill the view. If the video and view aspect ratios differ, the video is distorted.

  • Fill: Scales the video proportionally to fit the view. If the video and view aspect ratios differ, the SDK adds black bars to the remaining space.

  • Clip: Scales the video proportionally to fill the view. If the video and view aspect ratios differ, the SDK crops the video to fit.

mirrorMode

AliRtcRenderMirrorMode

The mirror mode.

  • Enables mirroring for the local preview of the front camera only.

  • Enables mirroring for all video views (local and remote).

  • Disables all mirroring.

rotationMode

AliRtcRotationMode

The rotation mode. Valid values: 0, 90, 180, and 270 degrees.

backgroundColor

int

The background color in hexadecimal RGB format, for example, 0x000000.

textureId

int

(Android only) The texture ID, used for displaying a third-party OpenGL ES texture.

textureWidth

int

(Android only) The width of the texture.

textureHeight

int

(Android only) The height of the texture.

sharedContext

long

(Android only) The shared OpenGL ES context for the texture.

Sample code:

7.1. Set the local render view

Android

mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
// Get and set the SurfaceView.
SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
localSurfaceView.setZOrderOnTop(true);
localSurfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_local = findViewById(R.id.fl_local);
fl_local.addView(localSurfaceView, layoutParams);
mLocalVideoCanvas.view = localSurfaceView;
// Set the local preview view.
mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
mAliRtcEngine.startPreview();

iOS

let videoView = self.createVideoView(uid: self.userId)

let canvas = AliVideoCanvas()
canvas.view = videoView.canvasView
canvas.renderMode = .auto
canvas.mirrorMode = .onlyFrontCameraPreviewEnabled
canvas.rotationMode = ._0

self.rtcEngine?.setLocalViewConfig(canvas, for: AliRtcVideoTrack.camera)
self.rtcEngine?.startPreview()

macOS

NSView * newView = [self createSeatView:uid];

AliVideoCanvas *canvas = [[AliVideoCanvas alloc] init];
canvas.renderMode = AliRtcRenderModeAuto;
canvas.view = newView;
canvas.mirrorMode = AliRtcRenderMirrorModeAllDisabled;
canvas.rotation = AliRtcRotationMode_0;

[self.engine setLocalViewConfig:canvas forTrack:AliRtcVideoTrackCamera];
[self.engine startPreview];

Windows

AliEngineVideoCanvas canvas;
/* Window handle */
canvas.view = mHWnd;
canvas.mirrorMode = AliEngineRenderMirrorModeAllNoMirror;
mAliRtcEngine->setLocalViewConfig(canvas, AliEngineVideoTrackCamera);

Harmony

// Create or reuse the canvas.
if (!this.aliRtcVideoCanvas) {
  this.aliRtcVideoCanvas = new AliRtcVideoCanvas();
}

this.aliRtcVideoCanvas.surfaceId = this.localSurfaceId;
this.aliRtcVideoCanvas.renderMode = AliRtcRenderMode.AliRtcRenderModeAuto;
this.aliRtcVideoCanvas.mirrorMode = AliRtcRenderMirrorMode.AliRtcRenderMirrorModeAllMirror;

this.rtcEngine.setLocalViewConfig(
  this.aliRtcVideoCanvas,
  this.componentController,
  AliRtcVideoTrack.AliRtcVideoTrackCamera
);

this.rtcEngine.startPreview();

7.2. Set the remote render view

Android

@Override
public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
    handler.post(new Runnable() {
        @Override
        public void run() {
            if(videoTrack == AliRtcVideoTrackCamera) {
                SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
                surfaceView.setZOrderMediaOverlay(true);
                FrameLayout fl_remote = findViewById(R.id.fl_remote);
                if (fl_remote == null) {
                    return;
                }
                fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
                remoteVideoCanvas.view = surfaceView;
                mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
            } else if(videoTrack == AliRtcVideoTrackNo) {
                FrameLayout fl_remote = findViewById(R.id.fl_remote);
                fl_remote.removeAllViews();
                mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
            }
        }
    });
}

iOS

func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack, videoTrack: AliRtcVideoTrack) {
    "onRemoteTrackAvailableNotify uid: \(uid) audioTrack: \(audioTrack)  videoTrack: \(videoTrack)".printLog()
    // Handles the availability of a remote user's stream.
    if audioTrack != .no {
        let videoView = self.videoViewList.first { $0.uidLabel.text == uid }
        if videoView == nil {
            _ = self.createVideoView(uid: uid)
        }
    }
    if videoTrack != .no {
        var videoView = self.videoViewList.first { $0.uidLabel.text == uid }
        if videoView == nil {
            videoView = self.createVideoView(uid: uid)
        }

        let canvas = AliVideoCanvas()
        canvas.view = videoView!.canvasView
        canvas.renderMode = .auto
        canvas.mirrorMode = .onlyFrontCameraPreviewEnabled
        canvas.rotationMode = ._0
        self.rtcEngine?.setRemoteViewConfig(canvas, uid: uid, for: AliRtcVideoTrack.camera)
    }
    else {
        self.rtcEngine?.setRemoteViewConfig(nil, uid: uid, for: AliRtcVideoTrack.camera)
    }

    if audioTrack == .no && videoTrack == .no {
        self.removeVideoView(uid: uid)
        self.rtcEngine?.setRemoteViewConfig(nil, uid: uid, for: AliRtcVideoTrack.camera)
    }
}

macOS

- (void)onRemoteTrackAvailableNotify:(NSString *)uid audioTrack:(AliRtcAudioTrack)audioTrack videoTrack:(AliRtcVideoTrack)videoTrack {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
            NSLog(@"Remote user stream information callback");
            
            __weak typeof(self)weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                __strong typeof(weakSelf)strongSelf = self;
                if (videoTrack == AliRtcVideoTrackNo) {
                    //                [self removeRemoteViewWithUid:uid];
                } else if(videoTrack == AliRtcVideoTrackCamera) {
                    [strongSelf addRemoteViewWithUid:uid videoTrack:AliRtcVideoTrackCamera];
                } else if(videoTrack == AliRtcVideoTrackScreen) {
                    [strongSelf addRemoteViewWithUid:uid videoTrack:AliRtcVideoTrackScreen];
                } else if(videoTrack == AliRtcVideoTrackBoth) {
                    [strongSelf addRemoteViewWithUid:uid videoTrack:AliRtcVideoTrackBoth];
                }
            });
        
    });

}

Windows

virtual void OnRemoteTrackAvailableNotify(const char *uid, AliEngineAudioTrack audioTrack, AliEngineVideoTrack videoTrack) {
    AliEngineVideoCanvas remote_canvas;
    if (videoTrack == AliEngineVideoTrackCamera
        || videoTrack == AliEngineVideoTrackBoth) {
        RECT rect;
        ::GetWindowRect(mHWnd, &rect);
        remote_canvas.displayView = remoteView;
        remote_canvas.renderMode = AliEngineRenderModeAuto;
        mAliRtcEngine->SetRemoteViewConfig(remote_canvas,uid,AliEngineVideoTrackCamera);
    } else {
        mAliRtcEngine->SetRemoteViewConfig(remote_canvas, uid, AliEngineVideoTrackCamera);
    }
}

Harmony

// Remote audio/video stream availability notification
listener.onRemoteTrackAvailableNotify((userId: string, audioTrack: AliRtcAudioTrack,
  videoTrack: AliRtcVideoTrack) => {
  console.info(`Remote audio/video stream available: userId=${userId}, videoTrack=${videoTrack}`);

  // Configure the remote view based on the video stream type.
  if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackCamera) {
    // Camera stream is available.
    this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
    this.removeRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
  } else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackScreen) {
    // Screen sharing stream is available.
    this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
    this.removeRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
  } else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackBoth) {
    // Dual streams are available (camera + screen sharing).
    this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackCamera);
    this.viewRemoteVideo(userId, AliRtcVideoTrack.AliRtcVideoTrackScreen);
  } else if (videoTrack === AliRtcVideoTrack.AliRtcVideoTrackNo) {
    // No video stream.
    this.removeAllRemoteVideo(userId);
  }
});