本文将为您介绍如何通过调用API设置视频编码属性。
功能介绍
在进行视频通话或直播时,开发者可以根据需要设置视频相关配置,如视频采集、编码分辨率、视频帧率、码率、镜像模式、视图模式等。
分辨率:
采集分辨率:指摄像头等采集设备提供的画面分辨率。
编码分辨率:指经过编码处理的画面的分辨率。
码率:指每秒传输的比特(bit)数,单位为 bps(bit per second)。
帧率:单位时间内视频显示帧数的量度单位,测量单位为“每秒显示帧数”(Frame Per Second,fps)。
设置合适的视频分辨率、帧率和码率可以在音视频场景中为用户提供更好的使用体验。选择合适镜像模式与视图模式则可以让开发者提供个性化的视频显示模式。
示例代码
Android端视频常用操作和配置:Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/VideoBasicUsage/VideoBasicUsageActivity.java
。
iOS端视频常用操作和配置:iOS/ARTCExample/BasicUsage/VideoBasicUsage/VideoBasicUsageVC.swift
。
前提条件
在设置视频配置之前,请确保达成以下条件:
功能实现
1.设置相机采集配置
ARTC 提供setCameraCapturerConfiguration
方法设置相机采集配置,包括相机方向、采集帧率。
需要在启动相机前调用,例如在 startPreview 启动预览或者 joinChannel 加入频道(自动启动相机)前。
调用
enableLocalVideo(false)
关闭摄像头采集会释放摄像头资源,此时可以重新设置。
接口定义如下:
/**
* @brief 采集偏好设置
* @param cameraCapturerConfiguration 偏好设置
* - preference:
* - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_PREVIEW} 高清预览,采集优先保证视频预览质量
* - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE} 采集选择最接近推流的分辨率,优先保证设备性能
* - {@link AliRtcCaptureOutputPreference#ALIRTC_CAPTURER_OUTPUT_PREFERENCE_AUTO} 自动调整采集分辨率
* - cameraDirection: 设置采集方向,前置或后置摄像头
* @return
* - 0: 成功
* - 非0: 表示失败
* @note 必须在打开摄像头之前设置,如{@link #startPreview},{@link #joinChannel}之前设置
*/
public abstract int setCameraCapturerConfiguration(AliEngineCameraCapturerConfiguration cameraCapturerConfiguration);
相关配置如下:
参数 | 类型 | 描述 |
preference | 采集偏好。
| |
cameraDirection | 相机方向(前置/后置)。 | |
fps | int | 采集帧率。 默认值为-1,表示使用编码配置的 fps,SDK 内部为 15。 |
cameraCaptureProfile | 指定视频采集的特定分辨率。
| |
textureEncode | int | (仅 Android)是否使用纹理编码。 |
cameraTextureCapture | int | (仅 Android)摄像头是否开启纹理采集。 |
接口调用示例如下:
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)
2.设置视频编码配置
ARTC 提供setVideoEncoderConfiguration
接口用于配置视频编码属性,包含视频分辨率、帧率、码率、关键帧间隔等影响视频质量的参数设置。开发者可以通过设置视频编码属性,控制视频流在不同网络条件下的展示方式。
setVideoEncoderConfiguration
在加入频道前后均可设置,如果在一次通话过程中仅需要设置一次,推荐在加入频道前设置。
相关配置如下:
参数名 | 类型 | 描述 |
dimensions | 视频分辨率,视频分辨率,默认值640x480,最大值1920x1080。 | |
frameRate | int | 视频编码帧率,默认值15, 最大值30。 |
bitrate | int | 视频编码码率(kbps),默认值为 512。设置为0表示由SDK内部根据视频分辨率和码率计算合适的编码码率。 码率设置应根据分辨率和帧率有对应的合理范围,该值设置在合理范围内有效,否则SDK会自动调节码率到有效值。码率与分辨率、帧率对应关系请参考代码注释。 |
keyFrameInterval | int | 关键帧间隔,单位毫秒。默认值0,表示SDK内部控制关键帧间隔。 说明 互动直播场景、需要和小程序互通场景必须设置。 |
forceStrictKeyFrameInterval | boolean | 是否强制编码器严格按照设置的关键帧间隔产生关键帧,默认值false。
|
mirrorMode | 编码视频镜像模式,控制推流视频是否镜像。 说明 设置镜像可以使用 | |
orientationMode | 编码视频旋转模式。
| |
rotationMode | 视频旋转角度(0/90/180/270)。 | |
codecType | 编解码器类型。
| |
encodeCodecType | 视频编码格式(系统默认/ H264/H265)。 | |
seiForceFrontIFrame | int | SEI发送前强制I帧。 -1表示使用默认值,0表示不强制,1表示强制(默认值)。 |
接口调用示例如下:
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)
3.切换相机方向
Android 和 iOS 设备通常具有前置和后置摄像头。SDK 默认使用前置摄像头,如果需要修改,请调用setCameraCaptureConfiguration
接口在开启摄像头前进行设置。如果已经开启摄像头,需要切换摄像头则可以调用switchCamera
接口。
/**
* @brief 切换前后摄像头
* @return
* - 0: 成功
* - 非0: 失败
* @note 只有iOS和android提供这个接口
*/
public abstract int switchCamera();
接口调用示例如下:
Android
mSwitchCameraBtn.setOnClickListener(v -> {
if(mAliRtcEngine != null) {
mAliRtcEngine.switchCamera();
}
});
iOS
@IBAction func onCameraDirectionChanged(_ sender: UISegmentedControl) {
rtcEngine?.switchCamera()
}
4.开关摄像头
在音视频通话中实现摄像头开关功能,ARTC 提供两个接口,muteLocalCamrea
和enableLocalVideo
。
接口 | muteLocalCamera | enableLocalVideo |
实现原理 | 发送数据替换为黑帧。 | 直接停止摄像头硬件采集,释放相关资源。 |
关闭摄像头现象 | 本地预览正常显示,远端用户黑屏。 | 本地预览和远端均停留在最后一帧。 |
特点 |
|
|
4.1. 停止或恢复视频数据发送
ARTC 提供muteLocalCamera
接口实现禁视频功能,在保留视频采集/编码/传输通道运行的前提下,向远端发送全黑视频帧(本地预览保持正常画面)。接口定义如下:
/**
* 是否将停止本地视频数据发送
* @param mute true表示视频数据发送黑帧;false表示恢复正常
* @param track 只支持{@link AliRtcVideoTrack#AliRtcVideoTrackCamera}
* @return
* - 0: 成功
* - 非0: 失败
* @note 发送黑色的视频帧。本地正常预览。采集、编码、发送模块仍然工作,只是视频内容被替换为黑色帧
*/
public abstract int muteLocalCamera(boolean mute, AliRtcVideoTrack track);
/**
* @brief 对端用户发送视频黑帧数据发送通知
* @param uid 执行muteVideo的用户ID
* @param isMute
* - true: 推流黑帧
* - false: 正常推流
* @note 该接口用于对端用户发送视频黑帧数据时的回调
*/
public void onUserVideoMuted(String uid ,boolean isMute){}
代码示例如下:
Android
开关摄像头采集:
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;
}
远端监听回调:
@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
开关摄像头采集:
@IBAction func onVideoMuteSwitched(_ sender: UISwitch) {
if sender.isOn {
// 黑帧
rtcEngine?.muteLocalCamera(false, for: AliRtcVideoTrack.camera)
} else {
rtcEngine?.muteLocalCamera(true, for: AliRtcVideoTrack.camera)
}
}
远端监听回调
extension VideoBasicUsageVC: AliRtcEngineDelegate {
func onUserVideoMuted(_ uid: String, videoMuted isMute: Bool) {
"onUserVideoMuted: user id \(uid) video muted: \(isMute)".printLog()
}
}
4.2. 开关相机采集
通过 enableLocalVideo 接口实现全局性控制本地视频采集设备(如摄像头)的启停状态,直接影响视频数据流的生成与传输。
/**
* @brief 禁用或启用本地视频采集
* @param enabled
* - true : 启用本地视频采集
* - false : 禁用本地视频采集
* @return
* - 0 : 成功
* - < 0 : 失败
* @note 默认为开启状态, 通过监听 {@link AliRtcEngineNotify#onUserVideoEnabled} 获取用户是否禁用或启用本地视频采集状态。
*/
public abstract int enableLocalVideo(boolean enabled);
/**
* @brief 对端用户关闭相机流采集发送通知
* @param uid 执行EnableLocalVideo的用户ID
* @param isEnable
* - true: 打开相机流采集
* - false: 关闭相机流采集
* @note 该接口用于对端用户关闭相机流采集时的回调
*/
public void onUserVideoEnabled(String uid, boolean isEnable){}
代码调用示例:
Android
开关相机
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);
}
}
});
远端监听回调
@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
开关相机采集
@IBAction func onCameraSwitch(_ sender: UISwitch) {
if sender.isOn {
rtcEngine?.enableLocalVideo(true)
} else {
rtcEngine?.enableLocalVideo(false)
}
updateCaptureUIVisibility()
}
远端监听回调
extension VideoBasicUsageVC: AliRtcEngineDelegate {
func onUserVideoEnabled(_ uid: String?, videoEnabled isEnable: Bool) {
"onUserVideoEnabled: user id \(uid ?? "invalid uid") video enable: \(isEnable)".printLog()
}
}
5. 开启或关闭预览
ARTC 提供startPreview
和stopPreview
接口来控制本地预览的启停。
注意:
预览前需要调用
setLocalViewConfig
为本地预览画面设置渲染视图。SDK 加入频道默认会开启预览,如果需要在入会前开启预览可以提前调用
startPreview
。调用关闭预览后,本端预览画面会停留在最后一帧。
开启预览。
代码示例如下:
Android
启动预览:
private void startPreview() {
if (mAliRtcEngine != null) {
ViewGroup.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
// 设置本地视图
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
}
}
}
// 开始预览
mAliRtcEngine.startPreview();
}
}
关闭预览:
mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;
iOS
启动预览:
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()
}
关闭预览:
self.rtcEngine?.stopPreview()
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil
6. 设置镜像模式
ARTC 提供setVideoMirrorMode
接口控制本地视频的预览画面镜像与推流画面镜像行为,支持运行时动态调整,适用于音视频通话、直播等场景。
/**
* @brief 设置预览和推流镜像能力
* @param mirrorMode 设置镜像的模式
* @return
* - 0: 设置成功
* - <0: 设置失败
* - AliRtcErrInner: SDK内部状态错误,需检查是否创建SDK实例成功
*
* @note
* - 此接口在入会前和入会后均可以动态设置,SDK内部会记录状态,并在可以操作预览及编码的时候对视频进行操作;
* - 使用此接口的优先级会高于setLocalViewConfig&setVideoEncoderConfiguration
* - 此接口与setLocalViewConfiguration&setVideoEncoderConfiguration里面的mirror重合,建议只有一个方式
*/
public abstract int setVideoMirrorMode(AliRtcVideoPipelineMirrorMode mirrorMode);
镜像模式如下:
枚举值 | 描述 |
AliRtcVideoPipelineMirrorModeNoMirror | 预览和编码均关闭镜像。 |
AliRtcVideoPipelineMirrorModeBothMirror | 预览和编码均打开镜像(默认)。 |
AliRtcVideoPipelineMirrorModeOnlyPreviewMirror | 仅预览打开镜像。 |
AliRtcVideoPipelineMirrorModeOnlyPublishMirror | 仅推流打开镜像。 |
代码示例如下:
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)
7. 设置渲染视图
在显示本地预览和远端用户画面前,需要调用setLocalViewConfig
或setRemoteViewConig
接口为待显示画面设置渲染视图。
// 设置本地预览显示视图
public abstract int setLocalViewConfig(AliRtcVideoCanvas viewConfig, AliRtcVideoTrack track);
// 设置远端置顶用户显示视图
public abstract int setRemoteViewConfig(AliRtcVideoCanvas canvas, String uid, AliRtcVideoTrack track);
AliRtcVideoCanvas 常用配置如下:
参数 | 类型 | 说明 |
view | View | 显示视图(必需)。 |
renderMode | AliRtcRenderMode | 渲染模式。
|
mirrorMode | AliRtcRenderMirrorMode | 镜像模式。
|
rotationMode | AliRtcRotationMode | 旋转模式(0/90/180/270)。 |
backgroundColor | int | 背景颜色,格式为RGB的Hex,例如0x000000。 |
textureId | int | (仅 Android)支持第三方OpenGL ES纹理显示,纹理ID。 |
textureWidth | int | (仅 Android)支持第三方OpenGL ES纹理显示,纹理宽。 |
textureHeight | int | (仅 Android)支持第三方OpenGL ES纹理显示,纹理高。 |
sharedContext | long | (仅 Android)支持第三方OpenGL ES纹理显示,纹理共享上下文。 |
代码示例如下:
7.1. 设置本地渲染视图
Android
mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
// 获取并设置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;
// 设置本地预览视图
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()
Windows
AliEngineVideoCanvas canvas;
/* windows 窗口句柄 */
canvas.view = mHWnd;
mAliRtcEngine.setLocalViewConfig(canvas, AliEngineVideoTrackCamera);
7.2. 设置远端渲染视图
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()
// 远端用户的流状态
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)
}
}
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);
}
}