Video capture device management is a core feature of Real-Time Communication (RTC). The ARTC SDK provides extensive APIs for managing devices. As implementations vary significantly across platforms, this topic describes the video device management features provided by the RTC engine.
Feature overview
The ARTC SDK gives developers comprehensive control over video capture devices. You can fine-tune camera parameters, such as zoom, exposure, and manual/auto focus modes, on platforms such as iOS and Android to significantly improve video capture quality.
Sample code
Android video capture device management: Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/CameraCommonControl/CameraActivity.java.
iOS video capture device management: iOS/ARTCExample/BasicUsage/CameraCommonSetting/CameraCommonControlVC.swift.
Harmony video capture device management: Harmony/ARTCExample/entry/src/main/ets/pages/basicusage/CameraPage.ets.
Prerequisites
You have an Alibaba Cloud account and have created a Real-Time Communication (RTC) application. For more information, see Create an application. You have obtained an AppID and an AppKey from the ApsaraVideo Live console.
SDK integration and basic feature implementation:
You have integrated the ARTC SDK into your project and implemented basic Real-Time Communication (RTC) features. For more information, see Integrate the SDK and Implement an audio and video call.
The camera has been started, for example, by starting a preview with
startPreviewor joining a channel withjoinChannel.
Implementation
Set camera zoom
The ARTC SDK allows you to control the camera zoom.
API reference
/**
* @brief Sets the camera zoom.
* @param zoom The zoom factor. The value ranges from 1 to the maximum zoom value supported by the camera.
* @return
* - 0: Success.
* - A non-zero value: Failure.
* @note This API is available only on iOS and Android.
*/
public abstract int setCameraZoom(float zoom);
/**
* @brief Gets the maximum zoom factor of the camera.
* @return The maximum zoom factor of the camera.
*/
public abstract float GetCameraMaxZoomFactor();
/**
* @brief Gets the current zoom factor of the camera.
* @return The current zoom factor of the camera.
*/
public abstract float GetCurrentZoom();Examples
Android
// Get zoom information.
private void initZoomSeekBar() {
if (mAliRtcEngine != null) {
zoomSeekBar.setEnabled(true);
// Get the maximum zoom value.
float maxZoom = mAliRtcEngine.GetCameraMaxZoomFactor();
float currZoom = mAliRtcEngine.GetCurrentZoom();
// Set the SeekBar range (from 1.0 to maxZoom, with a step of 0.1).
if(maxZoom >= 1.0) {
int maxProgress = (int)((maxZoom - 1) * 10);
zoomSeekBar.setMax(maxProgress);
int currProgress = (int)((currZoom - 1) * 10);
zoomSeekBar.setProgress(currProgress);
} else{
zoomSeekBar.setEnabled(false);
}
}
}
// Set the zoom.
zoomSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if(mAliRtcEngine != null) {
float newZoom = (float)((i+10) / 10.0);
mAliRtcEngine.setCameraZoom(newZoom);
zoomTextView.setText(String.format("%.1f", newZoom));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});iOS
// Get zoom information.
self.cameraZoomSlider.isEnabled = true
let maxZoom = rtcEngine.getCameraMaxZoomFactor()
let currZoom = rtcEngine.getCurrentZoom()
"Get maxZoom=\(maxZoom), currZoom=\(currZoom)".printLog()
self.cameraZoomSlider.minimumValue = 1.0
if maxZoom > 1.0 {
self.cameraZoomSlider.maximumValue = maxZoom
} else {
self.cameraZoomSlider.isEnabled = false
}
if currZoom >= 1.0 && currZoom <= maxZoom {
self.cameraZoomSlider.value = currZoom
self.cameraZoomValueLabel.text = String(format: "%.1f", self.cameraZoomSlider.value)
}
else {
self.cameraZoomSlider.value = self.cameraZoomSlider.minimumValue
self.cameraZoomValueLabel.text = "\(self.cameraZoomSlider.minimumValue)"
}
// Set the zoom.
@IBOutlet weak var cameraZoomSlider: UISlider!
@IBOutlet weak var cameraZoomValueLabel: UILabel!
@IBAction func onCameraZoomChanged(_ sender: UISlider) {
let currValue = sender.value
self.cameraZoomValueLabel.text = String(format: "%.1f", currValue)
self.rtcEngine?.setCameraZoom(currValue)
}Harmony
private handleZoomChange(value: number): void {
if (!this.rtcEngine) {
return;
}
// Calculate the zoom value based on the progress: zoom = 1.0 + (value / 10.0).
const newZoom = 1.0 + (value / 10.0);
// Clamp the zoom value between 1.0 and 10.0.
const clampedZoom = Math.max(1.0, Math.min(10.0, newZoom));
this.rtcEngine.setCameraZoom(clampedZoom);
this.zoomValue = clampedZoom.toFixed(1);
console.info(`Set zoom: ${clampedZoom}`);
}Set camera exposure
The ARTC SDK allows you to set the camera exposure to control the image brightness.
API reference
/**
* @brief Sets the camera exposure.
* @param exposure The exposure value.
* @return
* - 0: Success.
* - A non-zero value: Failure.
*/
public abstract int SetExposure(float exposure);
/**
* @brief Gets the current camera exposure.
* @return The current camera exposure.
*/
public abstract float GetCurrentExposure();
/**
* @brief Gets the minimum camera exposure.
* @return The minimum camera exposure.
*/
public abstract float GetMinExposure();
/**
* @brief Gets the maximum camera exposure.
* @return The maximum camera exposure.
*/
public abstract float GetMaxExposure();Examples
Android
// Get exposure information.
private void initExposureSeekBar() {
if (mAliRtcEngine != null) {
exposureSeekBar.setEnabled(true);
// Get the maximum exposure value.
float maxExposure = mAliRtcEngine.GetMaxExposure();
// Get the minimum exposure value.
float minExposure = mAliRtcEngine.GetMinExposure();
float currExposure = mAliRtcEngine.GetCurrentExposure();
if(maxExposure > minExposure) {
// Reset the SeekBar range.
int maxProgress = (int)((maxExposure - minExposure) * 10);
exposureSeekBar.setMax(maxProgress);
int currProgress = (int)((currExposure - minExposure) * 10);
exposureSeekBar.setProgress(currProgress);
} else {
exposureSeekBar.setEnabled(false);
}
}
}
// Set the exposure.
exposureSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if(mAliRtcEngine != null) {
float minExposure = mAliRtcEngine.GetMinExposure();
float newExposure = minExposure + (float)(i / 10.0);
mAliRtcEngine.SetExposure(newExposure);
exposureTextView.setText(String.format("% .1f", newExposure));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});iOS
// Get device exposure information.
self.cameraExposureSlider.isEnabled = true
let minExposure = rtcEngine.getMinExposure()
let maxExposure = rtcEngine.getMaxExposure()
let currExposure = rtcEngine.getCurrentExposure()
"Get minExposure=\(minExposure), maxExposure=\(maxExposure), currExposure=\(currExposure)".printLog()
if maxExposure > minExposure {
self.cameraExposureSlider.minimumValue = minExposure
self.cameraExposureSlider.maximumValue = maxExposure
} else {
self.cameraExposureSlider.isEnabled = false
}
if currExposure >= minExposure && currExposure <= maxExposure {
self.cameraExposureSlider.value = currExposure
self.cameraExposureValueLabel.text = String(format: "%.1f", self.cameraExposureSlider.value)
}
else {
self.cameraExposureSlider.value = self.cameraExposureSlider.minimumValue
self.cameraExposureValueLabel.text = "\(self.cameraExposureSlider.minimumValue)"
}
// Set the exposure.
@IBOutlet weak var cameraExposureValueLabel: UILabel!
@IBOutlet weak var cameraExposureSlider: UISlider!
@IBAction func onCameraExposureChanged(_ sender: UISlider) {
let currValue = sender.value
self.cameraExposureValueLabel.text = String(format: "%.1f", currValue)
self.rtcEngine?.setExposure(currValue)
}Harmony
// Handle exposure change.
private handleExposureChange(value: number): void {
if (!this.rtcEngine) {
return;
}
// Conversion formula: exposure value = progress value / 10.
const actualValue = value / 10.0;
// Clamp the exposure value between -12.0 and 12.0.
const clampedExposure = Math.max(-12.0, Math.min(12.0, actualValue));
// Update the state.
this.exposureSliderValue = value;
this.exposureValue = this.formatExposureValue(clampedExposure);
// Call the RTC engine to set the exposure compensation.
try {
this.rtcEngine.setExposure(clampedExposure);
console.info(`Set exposure compensation: ${clampedExposure}`);
} catch (error) {
console.error('Failed to set exposure compensation:', error);
}
}Manually set the exposure point
The ARTC SDK allows you to manually set an exposure point. When a user taps a location on the screen, the camera adjusts its exposure based on the lighting conditions in that area.
When setting an exposure point, you must first call the
isCameraExposurePointSupportedinterface to check for support.The coordinates must be normalized values.
API reference
/**
* @brief Checks whether the camera supports setting a manual exposure point.
* @return
* - true: Supported.
* - false: Not supported.
* @note This API is available only on iOS and Android and is used to check if an exposure point can be set for the current camera.
*/
public abstract boolean isCameraExposurePointSupported();
/**
* @brief Sets the camera exposure point.
* @param x The normalized x-coordinate. The value ranges from 0 to 1.
* @param y The normalized y-coordinate. The value ranges from 0 to 1.
* @return
* - 0: Success.
* - A non-zero value: Failure.
* @note This API is available only on iOS and Android. After you call this method, the camera performs a one-time exposure adjustment at the specified point and maintains that exposure value.
*/
public abstract int setCameraExposurePoint(float x, float y);Examples
Android
// Manually set the exposure point.
mLocalViewGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(@NonNull MotionEvent e) {
// Handle double-tap.
// ...
return true;
}
@Override
public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
// Handle single-tap.
if(mAliRtcEngine != null && mAliRtcEngine.isCameraExposurePointSupported()) {
float[] normalizedCoords = getNormalizedCoordinates(e.getX(), e.getY());
if (normalizedCoords[0] != -1 && normalizedCoords[1] != -1) {
mAliRtcEngine.setCameraExposurePoint(normalizedCoords[0], normalizedCoords[1]);
mCameraExposurePointX.setText(String.format("%.2f", normalizedCoords[0]));
mCameraExposurePointY.setText(String.format("%.2f", normalizedCoords[1]));
}
}
return true;
}
});iOS
@objc func handleSeatViewTap(_ gesture: UITapGestureRecognizer) {
guard let localSeatView = self.localPreviewSeatView else {
return
}
guard let rtcEngine = self.rtcEngine, rtcEngine.isCameraExposurePointSupported() else { return }
let tapPoint = gesture.location(in: localSeatView)
// Convert the tap coordinates to normalized coordinates (0-1 range) of the video frame.
let normalizedX = tapPoint.x / localSeatView.bounds.width
let normalizedY = tapPoint.y / localSeatView.bounds.height
rtcEngine.setCameraExposurePoint(CGPoint(x: normalizedX, y: normalizedY))
self.cameraExposurePointXTextField.text = String(format: "%.2f", normalizedX)
self.cameraExposurePointYTextField.text = String(format: "%.2f", normalizedY)
}Harmony
private handleSingleTap(touchX: number, touchY: number): void {
if (!this.rtcEngine) {
return;
}
// Get normalized coordinates.
const normalizedCoords = this.getNormalizedCoordinates(touchX, touchY);
if (normalizedCoords[0] !== -1 && normalizedCoords[1] !== -1) {
this.rtcEngine.setCameraExposurePoint(normalizedCoords[0], normalizedCoords[1]);
this.exposurePointX = normalizedCoords[0].toFixed(2);
this.exposurePointY = normalizedCoords[1].toFixed(2);
console.info('Set exposure point:', this.exposurePointX, this.exposurePointY);
}
}Manually set the focus point
ARTC provides a feature to set a manual camera focus point. This is similar to the interface for an exposure point, but it focuses on adjusting the camera's focus position. Before using this feature, call isCameraFocusPointSupported to check if it is supported.
After you manually set a focus point, the focus becomes static. It does not continuously track the area or dynamically adjust as the object moves. For dynamic tracking, consider using the auto-focus on faces feature.
API reference
/**
* @brief Checks whether the camera supports manual focus.
* @return
* - true: Supported.
* - false: Not supported.
* @note This API is available only on iOS and Android and is used to check if a focus point can be set for the current camera.
*/
public abstract boolean isCameraFocusPointSupported();
/**
* @brief Sets the manual focus point for the camera.
* @param x The x-coordinate value (normalized).
* @param y The y-coordinate value (normalized).
* @return
* - 0: Success.
* - A non-zero value: Failure.
* @note This API is available only on iOS and Android. After you call this method, the camera performs a one-time focus adjustment at the specified point and maintains that focus value.
*/
public abstract int setCameraFocusPoint(float x, float y);Examples
The following examples show how to set the exposure point on a single tap and the focus point on a double tap.
Android
mLocalViewGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(@NonNull MotionEvent e) {
// Handle double-tap.
if(mAliRtcEngine != null && mAliRtcEngine.isCameraFocusPointSupported()) {
float[] normalizedCoords = getNormalizedCoordinates(e.getX(), e.getY());
if (normalizedCoords[0] != -1 && normalizedCoords[1] != -1) {
mAliRtcEngine.setCameraFocusPoint(normalizedCoords[0], normalizedCoords[1]);
mCameraFocusPointX.setText(String.format("%.2f", normalizedCoords[0]));
mCameraFocusPointY.setText(String.format("%.2f", normalizedCoords[1]));
}
}
return true;
}
@Override
public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
// Handle single-tap.
// ...
return true;
}
});iOS
@objc func handleSeatViewDoubleTap(_ gesture: UITapGestureRecognizer) {
guard let localSeatView = self.localPreviewSeatView else {
return
}
guard let rtcEngine = self.rtcEngine, rtcEngine.isCameraFocusPointSupported() else { return }
let tapPoint = gesture.location(in: localSeatView)
// Convert the tap coordinates to normalized coordinates (0-1 range) of the video frame.
let normalizedX = tapPoint.x / localSeatView.bounds.width
let normalizedY = tapPoint.y / localSeatView.bounds.height
rtcEngine.setCameraFocus(CGPoint(x: normalizedX, y: normalizedY))
self.cameraFocusPointXTextField.text = String(format: "%.2f", normalizedX)
self.cameraFocusPointYTextField.text = String(format: "%.2f", normalizedY)
}Harmony
private handleDoubleTap(touchX: number, touchY: number): void {
if (!this.rtcEngine) {
return;
}
// Get normalized coordinates.
const normalizedCoords = this.getNormalizedCoordinates(touchX, touchY);
if (normalizedCoords[0] !== -1 && normalizedCoords[1] !== -1) {
this.rtcEngine.setCameraFocusPoint(normalizedCoords[0], normalizedCoords[1]);
this.focusPointX = normalizedCoords[0].toFixed(2);
this.focusPointY = normalizedCoords[1].toFixed(2);
console.info('Set focus point:', this.focusPointX, this.focusPointY);
}
}Auto-focus on faces
The ARTC SDK provides an API to configure the auto-focus on faces feature. On supported devices, enabling this feature causes the camera to automatically detect faces in the video frame and adjust the focus to keep them sharp.
Use cases: This feature is useful for face recognition, portrait optimization, and enhancing visual quality in video calls.
Before starting, call isCameraAutoFocusFaceModeSupported to check if the current device supports this feature.
API reference
/**
* @brief Checks whether the camera supports the auto-focus on faces feature.
* @return
* - true: Supported.
* - false: Not supported.
* @note This API is available only on iOS and Android. It returns false if the camera is off.
* If the camera is on and supports both face detection and auto-focus, it returns true.
*/
public abstract boolean isCameraAutoFocusFaceModeSupported();
/**
* @brief Enables or disables the auto-focus on faces feature.
* @param enable
* - true: Enable.
* - false: Disable.
* @return
* - true: Success.
* - false: Failure.
* @note This API is available only on iOS and Android. If {@link AliRtcEngine#isCameraAutoFocusFaceModeSupported} returns true
* and you set enable to true, the camera continuously focuses on the detected faces.
*/
public abstract boolean setCameraAutoFocusFaceModeEnabled(boolean enable);Examples
Android
if (mAliRtcEngine.isCameraAutoFocusFaceModeSupported()) {
mAliRtcEngine.setCameraAutoFocusFaceModeEnabled(isChecked);
}iOS
@IBAction func onCameraAudoFocusSwitch(_ sender: UISwitch) {
if ((self.rtcEngine?.isCameraAutoFocusFaceModeSupported()) != nil) {
self.rtcEngine?.setCameraAutoFocusFaceModeEnabled(sender.isOn)
}
}Toggle the flashlight
The ARTC SDK allows you to turn the camera's flashlight on or off. This is useful for adjusting brightness in low-light conditions, during video recording, or for other special lighting needs.
This feature is available only on iOS and Android and requires a hardware flashlight.
The flashlight is typically available only for the rear camera. Front-facing cameras usually do not have a physical flashlight. Although some devices simulate a flash effect using the screen, this API does not control that function.
API reference
/**
* @brief Turns the camera flashlight on or off.
* @param flash Specifies whether to turn on the flashlight.
* @return
* - 0: Success.
* - A non-zero value: Failure.
* @note This API is available only on iOS and Android. Typically, only the rear camera has a flashlight.
*/
public abstract int setCameraFlash(boolean flash);Examples
Android
mCameraFlashSwitch = findViewById(R.id.camera_flash_switch);
mCameraFlashSwitch.setEnabled(false);
mCameraFlashSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (mAliRtcEngine != null) {
mAliRtcEngine.setCameraFlash(isChecked);
}
});iOS
@IBOutlet weak var cameraFlashSwitch: UISwitch!
@IBAction func onCameraFlashSwitch(_ sender: UISwitch) {
if self.rtcEngine?.getCurrentCameraDirection() == .back {
self.rtcEngine?.setCameraFlash(sender.isOn)
}
}Harmony
private handleFlashChange(value: boolean): void {
if (!this.rtcEngine) {
return;
}
this.rtcEngine.setCameraFlash(value);
}