diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaEngine.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaEngine.h
new file mode 100644
index 0000000..70c8579
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaEngine.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "AgoraBase.h"
+#include "IAgoraMediaEngine.h"
+
+namespace agora {
+namespace media {
+namespace ext {
+
+class IMediaEngine {
+  // ----------------------------- 👇🏻new API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: MediaEngine_unregisterAudioFrameObserver
+   * @source: virtual int registerAudioFrameObserver(IAudioFrameObserver* observer) = 0;
+   */
+  virtual int unregisterAudioFrameObserver(IAudioFrameObserver *observer) = 0;
+
+  /**
+   * @iris_api_id: MediaEngine_unregisterVideoFrameObserver
+   * @source: virtual int registerVideoFrameObserver(IVideoFrameObserver* observer) = 0;
+   */
+  virtual int unregisterVideoFrameObserver(IVideoFrameObserver *observer) = 0;
+
+  /**
+   * @iris_api_id: MediaEngine_unregisterVideoEncodedFrameObserver
+   * @source: virtual int registerVideoEncodedFrameObserver(IVideoEncodedFrameObserver* observer) = 0;
+   */
+  virtual int
+  unregisterVideoEncodedFrameObserver(IVideoEncodedFrameObserver *observer) = 0;
+
+  /**
+   * @iris_api_id: MediaEngine_unregisterFaceInfoObserver
+   * @source: virtual int registerFaceInfoObserver(IFaceInfoObserver* observer) = 0;
+   */
+  virtual int unregisterFaceInfoObserver(IFaceInfoObserver* observer) = 0;
+
+  // ----------------------------- 👆🏻new API👆🏻 -----------------------------
+};
+
+} // namespace ext
+} // namespace media
+} // namespace agora
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaPlayer.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaPlayer.h
new file mode 100644
index 0000000..ae81020
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMediaPlayer.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "IAgoraMediaPlayer.h"
+
+namespace agora {
+namespace rtc {
+
+// Put this class to the agora::rtc namespaces to avoid unecessary namespace trimming(trim the `ext` namespace).
+class IMediaPlayerVideoFrameObserver {
+  virtual void onFrame(const agora::media::base::VideoFrame *frame) = 0;
+};
+
+namespace ext {
+
+class IMediaPlayer {
+  // ----------------------------- 👇🏻overload API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: MediaPlayer_setPlayerOption_4d05d29
+   * @source: virtual int setPlayerOption(const char* key, int value) = 0;
+   */
+  virtual int setPlayerOptionInInt(const char *key, int value) = 0;
+
+  /**
+   * @iris_api_id: MediaPlayer_setPlayerOption_ccad422
+   * @source: virtual int setPlayerOption(const char* key, char* value) = 0;
+   */
+  virtual int setPlayerOptionInString(const char *key, const char *value) = 0;
+
+  /**
+   * @iris_api_id: MediaPlayer_registerAudioFrameObserver_a5b510b
+   * @source: virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer, RAW_AUDIO_FRAME_OP_MODE_TYPE mode) = 0;
+   */
+  virtual int
+  registerAudioFrameObserver(media::IAudioPcmFrameSink* observer,
+                             RAW_AUDIO_FRAME_OP_MODE_TYPE mode = RAW_AUDIO_FRAME_OP_MODE_TYPE::RAW_AUDIO_FRAME_OP_MODE_READ_ONLY) = 0;
+
+  // ----------------------------- 👆🏻overload API👆🏻 -----------------------------
+
+  // ----------------------------- 👇🏻rename API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: MediaPlayer_registerVideoFrameObserver_833bd8d
+   * @source: virtual int registerVideoFrameObserver(media::base::IVideoFrameObserver* observer) = 0;
+   */
+  virtual int
+  registerVideoFrameObserver(IMediaPlayerVideoFrameObserver *observer) = 0;
+
+  /**
+   * @iris_api_id: MediaPlayer_unregisterVideoFrameObserver_5165d4c
+   * @source: virtual int unregisterVideoFrameObserver(agora::media::base::IVideoFrameObserver* observer) = 0;
+   */
+  virtual int
+  unregisterVideoFrameObserver(IMediaPlayerVideoFrameObserver *observer) = 0;
+
+  // ----------------------------- 👆🏻rename API👆🏻 -----------------------------
+};
+
+} // namespace ext
+} // namespace rtc
+} // namespace agora
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMusicContentCenter.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMusicContentCenter.h
new file mode 100644
index 0000000..8bbf740
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraMusicContentCenter.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "IAgoraMediaPlayer.h"
+
+namespace agora {
+namespace rtc {
+namespace ext {
+
+class IMusicPlayer : public IMediaPlayer {
+  // ----------------------------- 👇🏻overload API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: MusicPlayer_open_303b92e
+   * @source: virtual int open(int64_t songCode, int64_t startPos = 0) = 0;
+   * @generate_iris_api_id: true
+   */
+  virtual int openWithSongCode(int64_t songCode, int64_t startPos = 0) = 0;
+
+  // ----------------------------- 👆🏻overload API👆🏻 -----------------------------
+};
+
+class IMusicContentCenter
+{
+  /**
+   * @iris_api_id: MusicContentCenter_preload_d3baeab
+   * @source: virtual int preload(agora::util::AString& requestId, int64_t songCode) = 0;
+   */
+  virtual int preload(agora::util::AString& requestId, int64_t songCode) = 0;
+};
+} // namespace ext
+} // namespace rtc
+} // namespace agora
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngine.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngine.h
new file mode 100644
index 0000000..39fa9d1
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngine.h
@@ -0,0 +1,338 @@
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "IAgoraMediaEngine.h"
+#include "IAgoraMediaRecorder.h"
+#include "IAgoraMusicContentCenter.h"
+#include "IAgoraRtcEngine.h"
+#include "IAgoraSpatialAudio.h"
+#include "IAudioDeviceManager.h"
+#include "IAgoraH265Transcoder.h"
+
+namespace agora {
+namespace rtc {
+namespace ext {
+
+// virtual const char* getVersion(int* build) = 0;
+struct SDKBuildInfo {
+  int build;
+  const char *version;
+};
+
+// IVideoDeviceCollection
+// virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH],
+//                        char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+struct VideoDeviceInfo {
+  const char *deviceId;
+  const char *deviceName;
+};
+
+// IAudioDeviceCollection
+// virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH],
+//                       char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+struct AudioDeviceInfo {
+  const char *deviceId;
+  const char *deviceTypeName;
+  const char *deviceName;
+};
+
+class IRtcEngine {
+  // ----------------------------- 👇🏻overload API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: RtcEngine_joinChannel_cdbb747
+   * @source: virtual int joinChannel(const char* token, const char* channelId, uid_t uid, const ChannelMediaOptions& options) = 0;
+   */
+  virtual int joinChannel(const char *token, const char *channelId, uid_t uid,
+                          const ChannelMediaOptions &options) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_leaveChannel_2c0e3aa
+   * @source: virtual int leaveChannel(const LeaveChannelOptions& options) = 0;
+   */
+  virtual int leaveChannel(const LeaveChannelOptions *options = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setClientRole_b46cc48
+   * @source: virtual int setClientRole(CLIENT_ROLE_TYPE role, const ClientRoleOptions& options) = 0;
+   */
+  virtual int setClientRole(CLIENT_ROLE_TYPE role,
+                            const ClientRoleOptions *options = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startEchoTest_16140d7
+   * @source: virtual int startEchoTest(const EchoTestConfiguration& config) = 0;
+   */
+  virtual int startEchoTest(const EchoTestConfiguration &config) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startPreview_4fd718e
+   * @source: virtual int startPreview(VIDEO_SOURCE_TYPE sourceType) = 0;
+   */
+  virtual int
+  startPreview(VIDEO_SOURCE_TYPE sourceType =
+                   VIDEO_SOURCE_TYPE::VIDEO_SOURCE_CAMERA_PRIMARY) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_stopPreview_4fd718e
+   * @source: virtual int stopPreview(VIDEO_SOURCE_TYPE sourceType) = 0;
+   */
+  virtual int
+  stopPreview(VIDEO_SOURCE_TYPE sourceType =
+                  VIDEO_SOURCE_TYPE::VIDEO_SOURCE_CAMERA_PRIMARY) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setAudioProfile_d944543
+   * @source: virtual int setAudioProfile(AUDIO_PROFILE_TYPE profile, AUDIO_SCENARIO_TYPE scenario) __deprecated = 0;
+   */
+  virtual int
+  setAudioProfile(AUDIO_PROFILE_TYPE profile,
+                  AUDIO_SCENARIO_TYPE scenario =
+                      AUDIO_SCENARIO_TYPE::AUDIO_SCENARIO_DEFAULT) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startAudioRecording_e32bb3b
+   * @source: virtual int startAudioRecording(const AudioRecordingConfiguration& config) = 0;
+   */
+  virtual int
+  startAudioRecording(const AudioRecordingConfiguration &config) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startAudioMixing_1ee1b1e
+   * @source: virtual int startAudioMixing(const char* filePath, bool loopback, int cycle, int startPos) = 0;
+   */
+  virtual int startAudioMixing(const char *filePath, bool loopback, int cycle,
+                               int startPos = 0) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setLocalRenderMode_cfb201b
+   * @source: virtual int setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode, VIDEO_MIRROR_MODE_TYPE mirrorMode) = 0;
+   */
+  virtual int
+  setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode,
+                     VIDEO_MIRROR_MODE_TYPE mirrorMode =
+                         VIDEO_MIRROR_MODE_TYPE::VIDEO_MIRROR_MODE_AUTO) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_enableDualStreamMode_9822d8a
+   * @source: virtual int enableDualStreamMode(bool enabled, const SimulcastStreamConfig& streamConfig) = 0;
+   */
+  virtual int
+  enableDualStreamMode(bool enabled,
+                       const SimulcastStreamConfig *streamConfig = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setDualStreamMode_b3a4f6c
+   * @source: virtual int setDualStreamMode(SIMULCAST_STREAM_MODE mode, const SimulcastStreamConfig& streamConfig) = 0;
+   */
+  virtual int
+  setDualStreamMode(SIMULCAST_STREAM_MODE mode,
+                    const SimulcastStreamConfig *streamConfig = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_createDataStream_5862815
+   * @source: virtual int createDataStream(int* streamId, DataStreamConfig& config) = 0;
+   */
+  virtual int createDataStream(int *streamId,
+                               const DataStreamConfig &config) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_addVideoWatermark_7480410
+   * @source: virtual int addVideoWatermark(const char* watermarkUrl, const WatermarkOptions& options) = 0;
+   */
+  virtual int addVideoWatermark(const char *watermarkUrl,
+                                const WatermarkOptions &options) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_joinChannelWithUserAccount_4685af9
+   * @source: virtual int joinChannelWithUserAccount(const char* token, const char* channelId, const char* userAccount, const ChannelMediaOptions& options) = 0;
+   */
+  virtual int
+  joinChannelWithUserAccount(const char *token, const char *channelId,
+                             const char *userAccount,
+                             const ChannelMediaOptions *options = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_enableExtension_0b60a2c
+   * @source: virtual int enableExtension(const char* provider, const char* extension, const ExtensionInfo& extensionInfo, bool enable = true) = 0;
+   */
+  virtual int enableExtension(const char *provider, const char *extension,
+                              bool enable = true,
+                              agora::media::MEDIA_SOURCE_TYPE type =
+                                  agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setExtensionProperty_520ac55
+   * @source: virtual int setExtensionProperty(const char* provider, const char* extension, const char* key, const char* value, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+   */
+  virtual int setExtensionProperty(const char *provider, const char *extension,
+                                   const char *key, const char *value,
+                                   agora::media::MEDIA_SOURCE_TYPE type =
+                                       agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getExtensionProperty_38c9723
+   * @source: virtual int getExtensionProperty(const char* provider, const char* extension, const char* key, char* value, int buf_len, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+   */
+  virtual int getExtensionProperty(const char *provider, const char *extension,
+                                   const char *key, char *value, int buf_len,
+                                   agora::media::MEDIA_SOURCE_TYPE type =
+                                       agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startScreenCapture_270da41
+   * @source: virtual int startScreenCapture(const ScreenCaptureParameters2& captureParams) = 0;
+   */
+  virtual int
+  startScreenCapture(const ScreenCaptureParameters2 &captureParams) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_startScreenCapture_9ebb320
+   * @source: virtual int startScreenCapture(VIDEO_SOURCE_TYPE sourceType, const ScreenCaptureConfiguration& config) = 0;
+   */
+  virtual int startScreenCaptureBySourceType(
+      VIDEO_SOURCE_TYPE sourceType,
+      const ScreenCaptureConfiguration &config) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_stopScreenCapture
+   * @source: virtual int stopScreenCapture() = 0;
+   */
+  virtual int stopScreenCapture() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_stopScreenCapture_4fd718e
+   * @source: virtual int stopScreenCapture(VIDEO_SOURCE_TYPE sourceType) = 0;
+   */
+  virtual int stopScreenCaptureBySourceType(VIDEO_SOURCE_TYPE sourceType) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_release
+   * @source: AGORA_CPP_API static void release(bool sync = false);
+   */
+  virtual void release(bool sync = false) = 0;
+
+  // ----------------------------- 👆🏻overload API👆🏻 -----------------------------
+
+  // ----------------------------- 👇🏻rename API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: RtcEngine_startPreview
+   * @source: virtual int startPreview() = 0;
+   */
+  virtual int startPreviewWithoutSourceType() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getAudioDeviceManager
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_AUDIO_DEVICE_MANAGER = 1,
+   */
+  virtual IAudioDeviceManager *getAudioDeviceManager() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getVideoDeviceManager
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_VIDEO_DEVICE_MANAGER = 2,
+   */
+  virtual IVideoDeviceManager *getVideoDeviceManager() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getMusicContentCenter
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_MUSIC_CONTENT_CENTER = 15,
+   */
+  virtual IMusicContentCenter *getMusicContentCenter() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getMediaEngine
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_MEDIA_ENGINE = 4,
+   */
+  virtual agora::media::IMediaEngine *getMediaEngine() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getLocalSpatialAudioEngine
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_LOCAL_SPATIAL_AUDIO = 11,
+   */
+  virtual ILocalSpatialAudioEngine *getLocalSpatialAudioEngine() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getH265Transcoder
+   * @source: virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+   * AGORA_IID_H265_TRANSCODER = 16,
+   */
+  virtual IH265Transcoder *getH265Transcoder() = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_sendMetaData
+   * @source: virtual bool onReadyToSendMetadata(Metadata &metadata, VIDEO_SOURCE_TYPE source_type) = 0;
+   */
+  virtual int sendMetaData(const IMetadataObserver::Metadata &metadata,
+                           VIDEO_SOURCE_TYPE source_type) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_setMaxMetadataSize
+   * @source: virtual int getMaxMetadataSize() { return DEFAULT_METADATA_SIZE_IN_BYTE; }
+   */
+  virtual int setMaxMetadataSize(int size) = 0;
+
+  // ----------------------------- 👆🏻rename API👆🏻 -----------------------------
+
+  // ----------------------------- 👇🏻new API👇🏻 -----------------------------
+
+#ifdef __ELECTRON__
+  virtual void destroyRendererByView(view_t view) = 0;
+
+  virtual void destroyRendererByConfig(VIDEO_SOURCE_TYPE sourceType,
+                                       const char *channelId = NULL,
+                                       uid_t uid = 0) = 0;
+#endif
+
+
+  /**
+   * @iris_api_id: RtcEngine_unregisterAudioEncodedFrameObserver
+   * @source: virtual int registerAudioEncodedFrameObserver(const AudioEncodedFrameObserverConfig& config,  IAudioEncodedFrameObserver *observer) = 0;
+   * add for registerAudioEncodedFrameObserver
+   */
+  virtual int
+  unregisterAudioEncodedFrameObserver(IAudioEncodedFrameObserver *observer) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_getNativeHandle
+   * @source: AGORA_API agora::rtc::IRtcEngine* AGORA_CALL createAgoraRtcEngine();
+   * add for createAgoraRtcEngine
+   */
+  virtual intptr_t getNativeHandle() = 0;
+
+  // ----------------------------- 👆🏻new API👆🏻 -----------------------------
+
+  /**
+   * @iris_api_id: RtcEngine_preloadChannel_a0779eb
+   * @source: virtual int preloadChannel(const char* token, const char* channelId, uid_t uid) = 0;
+   */
+  virtual int preloadChannel(const char* token, const char* channelId, uid_t uid) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_preloadChannelWithUserAccount_0e4f59e
+   * @source: virtual int preloadChannel(const char* token, const char* channelId, const char* userAccount) = 0;
+   */
+  virtual int preloadChannelWithUserAccount(const char* token, const char* channelId, const char* userAccount) = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_takeSnapshot_1922dd1
+   * @source: virtual int takeSnapshot(uid_t uid, const char* filePath)  = 0;
+   */
+  virtual int takeSnapshot(uid_t uid, const char* filePath)  = 0;
+
+  /**
+   * @iris_api_id: RtcEngine_takeSnapshot_5669ea6
+   * @source: virtual int takeSnapshot(uid_t uid, const agora::media::SnapshotConfig& config)  = 0;
+   */
+  virtual int takeSnapshotWithConfig(uid_t uid, const agora::media::SnapshotConfig& config)  = 0;
+};
+
+} // namespace ext
+} // namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngineEx.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngineEx.h
new file mode 100644
index 0000000..931aa4d
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAgoraRtcEngineEx.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "IAgoraRtcEngineEx.h"
+
+namespace agora {
+namespace rtc {
+namespace ext {
+
+class IRtcEngineEx {
+  // ----------------------------- 👇🏻overload API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: RtcEngineEx_leaveChannelEx_b03ee9a
+   * @source: virtual int leaveChannelEx(const RtcConnection& connection, const LeaveChannelOptions& options) = 0;
+   */
+  virtual int leaveChannelEx(const RtcConnection &connection,
+                             const LeaveChannelOptions *options = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngineEx_leaveChannelWithUserAccountEx_8bbe372
+   * @source: virtual int leaveChannelWithUserAccountEx(const char* channelId, const char* userAccount, const LeaveChannelOptions& options) = 0;
+   */
+  virtual int leaveChannelWithUserAccountEx(const char* channelId, const char* userAccount, const LeaveChannelOptions* options = NULL) = 0;
+
+  /**
+   * @iris_api_id: RtcEngineEx_takeSnapshotEx_de1c015
+   * @source: virtual int takeSnapshotEx(const RtcConnection& connection, uid_t uid, const char* filePath)  = 0;
+   */
+  virtual int takeSnapshotEx(const RtcConnection& connection, uid_t uid, const char* filePath)  = 0;
+
+  /**
+   * @iris_api_id: RtcEngineEx_takeSnapshotEx_b856417
+   * @source: virtual int takeSnapshotEx(const RtcConnection& connection, uid_t uid, const agora::media::SnapshotConfig& config)  = 0;
+   */
+  virtual int takeSnapshotWithConfigEx(const RtcConnection& connection, uid_t uid, const agora::media::SnapshotConfig& config)  = 0;
+
+  /**
+   * @iris_api_id: RtcEngineEx_createDataStreamEx_9f641b6
+   * @source: virtual int createDataStreamEx(int* streamId, const DataStreamConfig& config, const RtcConnection& connection) = 0;
+   */
+  virtual int createDataStreamEx(int* streamId, const DataStreamConfig& config, const RtcConnection& connection) = 0;
+
+  // ----------------------------- 👆🏻overload API👆🏻 -----------------------------
+};
+
+} // namespace ext
+} // namespace rtc
+} // namespace agora
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/custom_headers/CustomIAudioDeviceManager.h b/headers/rtc_4.3.2.11/custom_headers/CustomIAudioDeviceManager.h
new file mode 100644
index 0000000..b68d3f6
--- /dev/null
+++ b/headers/rtc_4.3.2.11/custom_headers/CustomIAudioDeviceManager.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "CustomIAgoraRtcEngine.h"
+
+namespace agora {
+namespace rtc {
+namespace ext {
+
+class IAudioDeviceManager {
+  // ----------------------------- 👇🏻rename API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: AudioDeviceManager_getPlaybackDefaultDevice
+   * @source: virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+   */
+  virtual AudioDeviceInfo *getPlaybackDefaultDevice() = 0;
+
+  /**
+   * @iris_api_id: AudioDeviceManager_getRecordingDefaultDevice
+   * @source: virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+   */
+  virtual AudioDeviceInfo *getRecordingDefaultDevice() = 0;
+
+  // ----------------------------- 👆🏻rename API👆🏻 -----------------------------
+
+  // ----------------------------- 👇🏻overload API👇🏻 -----------------------------
+
+  /**
+   * @iris_api_id: AudioDeviceManager_getPlaybackDeviceInfo_ed3a96d
+   * @source: virtual int getPlaybackDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0;
+   */
+  virtual AudioDeviceInfo *getPlaybackDeviceInfo() = 0;
+
+  /**
+   * @iris_api_id: AudioDeviceManager_getRecordingDeviceInfo_ed3a96d
+   * @source: virtual int getRecordingDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0;
+   */
+  virtual AudioDeviceInfo *getRecordingDeviceInfo() = 0;
+
+  // ----------------------------- 👆🏻overload API👆🏻 ----------------------------- 
+
+};
+
+} // namespace ext
+} // namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/AgoraBase.h b/headers/rtc_4.3.2.11/include/AgoraBase.h
new file mode 100644
index 0000000..46474ee
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/AgoraBase.h
@@ -0,0 +1,6466 @@
+//
+//  Agora Engine SDK
+//
+//  Created by Sting Feng in 2017-11.
+//  Copyright (c) 2017 Agora.io. All rights reserved.
+//
+
+// This header file is included by both high level and low level APIs,
+#pragma once  // NOLINT(build/header_guard)
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <cassert>
+
+#include "IAgoraParameter.h"
+#include "AgoraMediaBase.h"
+#include "AgoraRefPtr.h"
+#include "AgoraOptional.h"
+
+#define MAX_PATH_260 (260)
+
+#if defined(_WIN32)
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif  // !WIN32_LEAN_AND_MEAN
+#if defined(__aarch64__)
+#include <arm64intr.h>
+#endif
+#include <Windows.h>
+
+#if defined(AGORARTC_EXPORT)
+#define AGORA_API extern "C" __declspec(dllexport)
+#define AGORA_CPP_API __declspec(dllexport)
+#else
+#define AGORA_API extern "C" __declspec(dllimport)
+#define AGORA_CPP_API __declspec(dllimport)
+#endif  // AGORARTC_EXPORT
+
+#define AGORA_CALL __cdecl
+
+#define __deprecated
+
+#elif defined(__APPLE__)
+
+#include <TargetConditionals.h>
+
+#define AGORA_API extern "C" __attribute__((visibility("default")))
+#define AGORA_CPP_API __attribute__((visibility("default")))
+#define AGORA_CALL
+
+#elif defined(__ANDROID__) || defined(__linux__)
+
+#define AGORA_API extern "C" __attribute__((visibility("default")))
+#define AGORA_CPP_API __attribute__((visibility("default")))
+#define AGORA_CALL
+
+#define __deprecated
+
+#else  // !_WIN32 && !__APPLE__ && !(__ANDROID__ || __linux__)
+
+#define AGORA_API extern "C"
+#define AGORA_CPP_API
+#define AGORA_CALL
+
+#define __deprecated
+
+#endif  // _WIN32
+
+#ifndef OPTIONAL_ENUM_SIZE_T
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_ENUM_SIZE_T enum : size_t
+#else
+#define OPTIONAL_ENUM_SIZE_T enum
+#endif
+#endif
+
+#ifndef OPTIONAL_NULLPTR
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_NULLPTR nullptr
+#else
+#define OPTIONAL_NULLPTR NULL
+#endif
+#endif
+
+#define INVALID_DISPLAY_ID (-2)
+
+namespace agora {
+namespace util {
+
+template <class T>
+class AutoPtr {
+ protected:
+  typedef T value_type;
+  typedef T* pointer_type;
+
+ public:
+  explicit AutoPtr(pointer_type p = OPTIONAL_NULLPTR) : ptr_(p) {}
+
+  ~AutoPtr() {
+    if (ptr_) {
+      ptr_->release();
+      ptr_ = OPTIONAL_NULLPTR;
+    }
+  }
+
+  operator bool() const { return (ptr_ != OPTIONAL_NULLPTR); }
+
+  value_type& operator*() const { return *get(); }
+
+  pointer_type operator->() const { return get(); }
+
+  pointer_type get() const { return ptr_; }
+
+  pointer_type release() {
+    pointer_type ret = ptr_;
+    ptr_ = 0;
+    return ret;
+  }
+
+  void reset(pointer_type ptr = OPTIONAL_NULLPTR) {
+    if (ptr != ptr_ && ptr_) {
+      ptr_->release();
+    }
+
+    ptr_ = ptr;
+  }
+
+  template <class C1, class C2>
+  bool queryInterface(C1* c, C2 iid) {
+    pointer_type p = OPTIONAL_NULLPTR;
+    if (c && !c->queryInterface(iid, reinterpret_cast<void**>(&p))) {
+      reset(p);
+    }
+
+    return (p != OPTIONAL_NULLPTR);
+  }
+
+ private:
+  AutoPtr(const AutoPtr&);
+  AutoPtr& operator=(const AutoPtr&);
+
+ private:
+  pointer_type ptr_;
+};
+
+template <class T>
+class CopyableAutoPtr : public AutoPtr<T> {
+  typedef typename AutoPtr<T>::pointer_type pointer_type;
+
+ public:
+  explicit CopyableAutoPtr(pointer_type p = 0) : AutoPtr<T>(p) {}
+  CopyableAutoPtr(const CopyableAutoPtr& rhs) { this->reset(rhs.clone()); }
+  CopyableAutoPtr& operator=(const CopyableAutoPtr& rhs) {
+    if (this != &rhs) this->reset(rhs.clone());
+    return *this;
+  }
+  pointer_type clone() const {
+    if (!this->get()) return OPTIONAL_NULLPTR;
+    return this->get()->clone();
+  }
+};
+
+class IString {
+ public:
+  virtual bool empty() const = 0;
+  virtual const char* c_str() = 0;
+  virtual const char* data() = 0;
+  virtual size_t length() = 0;
+  virtual IString* clone() = 0;
+  virtual void release() = 0;
+  virtual ~IString() {}
+};
+typedef CopyableAutoPtr<IString> AString;
+
+class IIterator {
+ public:
+  virtual void* current() = 0;
+  virtual const void* const_current() const = 0;
+  virtual bool next() = 0;
+  virtual void release() = 0;
+  virtual ~IIterator() {}
+};
+
+class IContainer {
+ public:
+  virtual IIterator* begin() = 0;
+  virtual size_t size() const = 0;
+  virtual void release() = 0;
+  virtual ~IContainer() {}
+};
+
+template <class T>
+class AOutputIterator {
+  IIterator* p;
+
+ public:
+  typedef T value_type;
+  typedef value_type& reference;
+  typedef const value_type& const_reference;
+  typedef value_type* pointer;
+  typedef const value_type* const_pointer;
+  explicit AOutputIterator(IIterator* it = OPTIONAL_NULLPTR) : p(it) {}
+  ~AOutputIterator() {
+    if (p) p->release();
+  }
+  AOutputIterator(const AOutputIterator& rhs) : p(rhs.p) {}
+  AOutputIterator& operator++() {
+    p->next();
+    return *this;
+  }
+  bool operator==(const AOutputIterator& rhs) const {
+    if (p && rhs.p)
+      return p->current() == rhs.p->current();
+    else
+      return valid() == rhs.valid();
+  }
+  bool operator!=(const AOutputIterator& rhs) const { return !this->operator==(rhs); }
+  reference operator*() { return *reinterpret_cast<pointer>(p->current()); }
+  const_reference operator*() const { return *reinterpret_cast<const_pointer>(p->const_current()); }
+  bool valid() const { return p && p->current() != OPTIONAL_NULLPTR; }
+};
+
+template <class T>
+class AList {
+  IContainer* container;
+  bool owner;
+
+ public:
+  typedef T value_type;
+  typedef value_type& reference;
+  typedef const value_type& const_reference;
+  typedef value_type* pointer;
+  typedef const value_type* const_pointer;
+  typedef size_t size_type;
+  typedef AOutputIterator<value_type> iterator;
+  typedef const AOutputIterator<value_type> const_iterator;
+
+ public:
+  AList() : container(OPTIONAL_NULLPTR), owner(false) {}
+  AList(IContainer* c, bool take_ownership) : container(c), owner(take_ownership) {}
+  ~AList() { reset(); }
+  void reset(IContainer* c = OPTIONAL_NULLPTR, bool take_ownership = false) {
+    if (owner && container) container->release();
+    container = c;
+    owner = take_ownership;
+  }
+  iterator begin() { return container ? iterator(container->begin()) : iterator(OPTIONAL_NULLPTR); }
+  iterator end() { return iterator(OPTIONAL_NULLPTR); }
+  size_type size() const { return container ? container->size() : 0; }
+  bool empty() const { return size() == 0; }
+};
+
+}  // namespace util
+
+/**
+ * The channel profile.
+ */
+enum CHANNEL_PROFILE_TYPE {
+  /**
+   * 0: Communication.
+   *
+   * This profile prioritizes smoothness and applies to the one-to-one scenario.
+   */
+  CHANNEL_PROFILE_COMMUNICATION = 0,
+  /**
+   * 1: (Default) Live Broadcast.
+   *
+   * This profile prioritizes supporting a large audience in a live broadcast channel.
+   */
+  CHANNEL_PROFILE_LIVE_BROADCASTING = 1,
+  /**
+   * 2: Gaming.
+   * @deprecated This profile is deprecated.
+   */
+  CHANNEL_PROFILE_GAME __deprecated = 2,
+  /**
+   * 3: Cloud Gaming.
+   *
+   * @deprecated This profile is deprecated.
+   */
+  CHANNEL_PROFILE_CLOUD_GAMING __deprecated = 3,
+
+  /**
+   * 4: Communication 1v1.
+   * @deprecated This profile is deprecated.
+   */
+  CHANNEL_PROFILE_COMMUNICATION_1v1 __deprecated = 4,
+};
+
+/**
+ * The warning codes.
+ */
+enum WARN_CODE_TYPE {
+  /**
+   * 8: The specified view is invalid. To use the video function, you need to specify
+   * a valid view.
+   */
+  WARN_INVALID_VIEW = 8,
+  /**
+   * 16: Fails to initialize the video function, probably due to a lack of
+   * resources. Users fail to see each other, but can still communicate with voice.
+   */
+  WARN_INIT_VIDEO = 16,
+  /**
+   * 20: The request is pending, usually because some module is not ready,
+   * and the SDK postpones processing the request.
+   */
+  WARN_PENDING = 20,
+  /**
+   * 103: No channel resources are available, probably because the server cannot
+   * allocate any channel resource.
+   */
+  WARN_NO_AVAILABLE_CHANNEL = 103,
+  /**
+   * 104: A timeout occurs when looking for the channel. When joining a channel,
+   * the SDK looks up the specified channel. This warning usually occurs when the
+   * network condition is too poor to connect to the server.
+   */
+  WARN_LOOKUP_CHANNEL_TIMEOUT = 104,
+  /**
+   * 105: The server rejects the request to look for the channel. The server
+   * cannot process this request or the request is illegal.
+   */
+  WARN_LOOKUP_CHANNEL_REJECTED = 105,
+  /**
+   * 106: A timeout occurs when opening the channel. Once the specific channel
+   * is found, the SDK opens the channel. This warning usually occurs when the
+   * network condition is too poor to connect to the server.
+   */
+  WARN_OPEN_CHANNEL_TIMEOUT = 106,
+  /**
+   * 107: The server rejects the request to open the channel. The server
+   * cannot process this request or the request is illegal.
+   */
+  WARN_OPEN_CHANNEL_REJECTED = 107,
+
+  // sdk: 100~1000
+  /**
+   * 111: A timeout occurs when switching the live video.
+   */
+  WARN_SWITCH_LIVE_VIDEO_TIMEOUT = 111,
+  /**
+   * 118: A timeout occurs when setting the user role.
+   */
+  WARN_SET_CLIENT_ROLE_TIMEOUT = 118,
+  /**
+   * 121: The ticket to open the channel is invalid.
+   */
+  WARN_OPEN_CHANNEL_INVALID_TICKET = 121,
+  /**
+   * 122: The SDK is trying connecting to another server.
+   */
+  WARN_OPEN_CHANNEL_TRY_NEXT_VOS = 122,
+  /**
+   * 131: The channel connection cannot be recovered.
+   */
+  WARN_CHANNEL_CONNECTION_UNRECOVERABLE = 131,
+  /**
+   * 132: The SDK connection IP has changed.
+   */
+  WARN_CHANNEL_CONNECTION_IP_CHANGED = 132,
+  /**
+   * 133: The SDK connection port has changed.
+   */
+  WARN_CHANNEL_CONNECTION_PORT_CHANGED = 133,
+  /** 134: The socket error occurs, try to rejoin channel.
+   */
+  WARN_CHANNEL_SOCKET_ERROR = 134,
+  /**
+   * 701: An error occurs when opening the file for audio mixing.
+   */
+  WARN_AUDIO_MIXING_OPEN_ERROR = 701,
+  /**
+   * 1014: Audio Device Module: An exception occurs in the playback device.
+   */
+  WARN_ADM_RUNTIME_PLAYOUT_WARNING = 1014,
+  /**
+   * 1016: Audio Device Module: A warning occurs in the recording device.
+   */
+  WARN_ADM_RUNTIME_RECORDING_WARNING = 1016,
+  /**
+   * 1019: Audio Device Module: No valid audio data is collected.
+   */
+  WARN_ADM_RECORD_AUDIO_SILENCE = 1019,
+  /**
+   * 1020: Audio Device Module: The playback device fails to start.
+   */
+  WARN_ADM_PLAYOUT_MALFUNCTION = 1020,
+  /**
+   * 1021: Audio Device Module: The recording device fails to start.
+   */
+  WARN_ADM_RECORD_MALFUNCTION = 1021,
+  /**
+   * 1031: Audio Device Module: The recorded audio volume is too low.
+   */
+  WARN_ADM_RECORD_AUDIO_LOWLEVEL = 1031,
+  /**
+   * 1032: Audio Device Module: The playback audio volume is too low.
+   */
+  WARN_ADM_PLAYOUT_AUDIO_LOWLEVEL = 1032,
+  /**
+   * 1040: Audio device module: An exception occurs with the audio drive.
+   * Choose one of the following solutions:
+   * - Disable or re-enable the audio device.
+   * - Re-enable your device.
+   * - Update the sound card drive.
+   */
+  WARN_ADM_WINDOWS_NO_DATA_READY_EVENT = 1040,
+  /**
+   * 1051: Audio Device Module: The SDK detects howling.
+   */
+  WARN_APM_HOWLING = 1051,
+  /**
+   * 1052: Audio Device Module: The audio device is in a glitching state.
+   */
+  WARN_ADM_GLITCH_STATE = 1052,
+  /**
+   * 1053: Audio Device Module: The settings are improper.
+   */
+  WARN_ADM_IMPROPER_SETTINGS = 1053,
+  /**
+   * 1322: No recording device.
+   */
+  WARN_ADM_WIN_CORE_NO_RECORDING_DEVICE = 1322,
+  /**
+   * 1323: Audio device module: No available playback device.
+   * You can try plugging in the audio device.
+   */
+  WARN_ADM_WIN_CORE_NO_PLAYOUT_DEVICE = 1323,
+  /**
+   * 1324: Audio device module: The capture device is released improperly.
+   * Choose one of the following solutions:
+   * - Disable or re-enable the audio device.
+   * - Re-enable your audio device.
+   * - Update the sound card drive.
+   */
+  WARN_ADM_WIN_CORE_IMPROPER_CAPTURE_RELEASE = 1324,
+};
+
+/**
+ * The error codes.
+ */
+enum ERROR_CODE_TYPE {
+  /**
+   * 0: No error occurs.
+   */
+  ERR_OK = 0,
+  // 1~1000
+  /**
+   * 1: A general error occurs (no specified reason).
+   */
+  ERR_FAILED = 1,
+  /**
+   * 2: The argument is invalid. For example, the specific channel name
+   * includes illegal characters.
+   */
+  ERR_INVALID_ARGUMENT = 2,
+  /**
+   * 3: The SDK module is not ready. Choose one of the following solutions:
+   * - Check the audio device.
+   * - Check the completeness of the app.
+   * - Reinitialize the RTC engine.
+   */
+  ERR_NOT_READY = 3,
+  /**
+   * 4: The SDK does not support this function.
+   */
+  ERR_NOT_SUPPORTED = 4,
+  /**
+   * 5: The request is rejected.
+   */
+  ERR_REFUSED = 5,
+  /**
+   * 6: The buffer size is not big enough to store the returned data.
+   */
+  ERR_BUFFER_TOO_SMALL = 6,
+  /**
+   * 7: The SDK is not initialized before calling this method.
+   */
+  ERR_NOT_INITIALIZED = 7,
+  /**
+   * 8: The state is invalid.
+   */
+  ERR_INVALID_STATE = 8,
+  /**
+   * 9: No permission. This is for internal use only, and does
+   * not return to the app through any method or callback.
+   */
+  ERR_NO_PERMISSION = 9,
+  /**
+   * 10: An API timeout occurs. Some API methods require the SDK to return the
+   * execution result, and this error occurs if the request takes too long
+   * (more than 10 seconds) for the SDK to process.
+   */
+  ERR_TIMEDOUT = 10,
+  /**
+   * 11: The request is cancelled. This is for internal use only,
+   * and does not return to the app through any method or callback.
+   */
+  ERR_CANCELED = 11,
+  /**
+   * 12: The method is called too often. This is for internal use
+   * only, and does not return to the app through any method or
+   * callback.
+   */
+  ERR_TOO_OFTEN = 12,
+  /**
+   * 13: The SDK fails to bind to the network socket. This is for internal
+   * use only, and does not return to the app through any method or
+   * callback.
+   */
+  ERR_BIND_SOCKET = 13,
+  /**
+   * 14: The network is unavailable. This is for internal use only, and
+   * does not return to the app through any method or callback.
+   */
+  ERR_NET_DOWN = 14,
+  /**
+   * 17: The request to join the channel is rejected. This error usually occurs
+   * when the user is already in the channel, and still calls the method to join
+   * the channel, for example, \ref agora::rtc::IRtcEngine::joinChannel "joinChannel()".
+   */
+  ERR_JOIN_CHANNEL_REJECTED = 17,
+  /**
+   * 18: The request to leave the channel is rejected. This error usually
+   * occurs when the user has already left the channel, and still calls the
+   * method to leave the channel, for example, \ref agora::rtc::IRtcEngine::leaveChannel
+   * "leaveChannel".
+   */
+  ERR_LEAVE_CHANNEL_REJECTED = 18,
+  /**
+   * 19: The resources have been occupied and cannot be reused.
+   */
+  ERR_ALREADY_IN_USE = 19,
+  /**
+   * 20: The SDK gives up the request due to too many requests. This is for
+   * internal use only, and does not return to the app through any method or callback.
+   */
+  ERR_ABORTED = 20,
+  /**
+   * 21: On Windows, specific firewall settings can cause the SDK to fail to
+   * initialize and crash.
+   */
+  ERR_INIT_NET_ENGINE = 21,
+  /**
+   * 22: The app uses too much of the system resource and the SDK
+   * fails to allocate any resource.
+   */
+  ERR_RESOURCE_LIMITED = 22,
+  /**
+   * 101: The App ID is invalid, usually because the data format of the App ID is incorrect.
+   *
+   * Solution: Check the data format of your App ID. Ensure that you use the correct App ID to initialize the Agora service.
+   */
+  ERR_INVALID_APP_ID = 101,
+  /**
+   * 102: The specified channel name is invalid. Please try to rejoin the
+   * channel with a valid channel name.
+   */
+  ERR_INVALID_CHANNEL_NAME = 102,
+  /**
+   * 103: Fails to get server resources in the specified region. Please try to
+   * specify another region when calling \ref agora::rtc::IRtcEngine::initialize
+   *  "initialize".
+   */
+  ERR_NO_SERVER_RESOURCES = 103,
+  /**
+   * 109: The token has expired, usually for the following reasons:
+   * - Timeout for token authorization: Once a token is generated, you must use it to access the
+   * Agora service within 24 hours. Otherwise, the token times out and you can no longer use it.
+   * - The token privilege expires: To generate a token, you need to set a timestamp for the token
+   * privilege to expire. For example, If you set it as seven days, the token expires seven days after
+   * its usage. In that case, you can no longer access the Agora service. The users cannot make calls,
+   * or are kicked out of the channel.
+   *
+   * Solution: Regardless of whether token authorization times out or the token privilege expires,
+   * you need to generate a new token on your server, and try to join the channel.
+   */
+  ERR_TOKEN_EXPIRED = 109,
+  /**
+   * 110: The token is invalid, usually for one of the following reasons:
+   * - Did not provide a token when joining a channel in a situation where the project has enabled the
+   * App Certificate.
+   * - Tried to join a channel with a token in a situation where the project has not enabled the App
+   * Certificate.
+   * - The App ID, user ID and channel name that you use to generate the token on the server do not match
+   * those that you use when joining a channel.
+   *
+   * Solution:
+   * - Before joining a channel, check whether your project has enabled the App certificate. If yes, you
+   * must provide a token when joining a channel; if no, join a channel without a token.
+   * - When using a token to join a channel, ensure that the App ID, user ID, and channel name that you
+   * use to generate the token is the same as the App ID that you use to initialize the Agora service, and
+   * the user ID and channel name that you use to join the channel.
+   */
+  ERR_INVALID_TOKEN = 110,
+  /**
+   * 111: The internet connection is interrupted. This applies to the Agora Web
+   * SDK only.
+   */
+  ERR_CONNECTION_INTERRUPTED = 111,  // only used in web sdk
+  /**
+   * 112: The internet connection is lost. This applies to the Agora Web SDK
+   * only.
+   */
+  ERR_CONNECTION_LOST = 112,  // only used in web sdk
+  /**
+   * 113: The user is not in the channel when calling the
+   * \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage()" method.
+   */
+  ERR_NOT_IN_CHANNEL = 113,
+  /**
+   * 114: The data size is over 1024 bytes when the user calls the
+   * \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage()" method.
+   */
+  ERR_SIZE_TOO_LARGE = 114,
+  /**
+   * 115: The bitrate of the sent data exceeds the limit of 6 Kbps when the
+   * user calls the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage()".
+   */
+  ERR_BITRATE_LIMIT = 115,
+  /**
+   * 116: Too many data streams (over 5) are created when the user
+   * calls the \ref agora::rtc::IRtcEngine::createDataStream "createDataStream()" method.
+   */
+  ERR_TOO_MANY_DATA_STREAMS = 116,
+  /**
+   * 117: A timeout occurs for the data stream transmission.
+   */
+  ERR_STREAM_MESSAGE_TIMEOUT = 117,
+  /**
+   * 119: Switching the user role fails. Please try to rejoin the channel.
+   */
+  ERR_SET_CLIENT_ROLE_NOT_AUTHORIZED = 119,
+  /**
+   * 120: MediaStream decryption fails. The user may have tried to join the channel with a wrong
+   * password. Check your settings or try rejoining the channel.
+   */
+  ERR_DECRYPTION_FAILED = 120,
+  /**
+   * 121: The user ID is invalid.
+   */
+  ERR_INVALID_USER_ID = 121,
+  /**
+   * 122: DataStream decryption fails. The peer may have tried to join the channel with a wrong
+   * password, or did't enable datastream encryption
+   */
+  ERR_DATASTREAM_DECRYPTION_FAILED = 122,
+  /**
+   * 123: The app is banned by the server.
+   */
+  ERR_CLIENT_IS_BANNED_BY_SERVER = 123,
+  /**
+   * 130: Encryption is enabled when the user calls the
+   * \ref agora::rtc::IRtcEngine::addPublishStreamUrl "addPublishStreamUrl()" method
+   * (CDN live streaming does not support encrypted streams).
+   */
+  ERR_ENCRYPTED_STREAM_NOT_ALLOWED_PUBLISH = 130,
+
+  /**
+   * 131: License credential is invalid
+   */
+  ERR_LICENSE_CREDENTIAL_INVALID = 131,
+
+  /**
+   * 134: The user account is invalid, usually because the data format of the user account is incorrect.
+   */
+  ERR_INVALID_USER_ACCOUNT = 134,
+
+  /** 157: The necessary dynamical library is not integrated. For example, if you call
+   * the \ref agora::rtc::IRtcEngine::enableDeepLearningDenoise "enableDeepLearningDenoise" but do not integrate the dynamical
+   * library for the deep-learning noise reduction into your project, the SDK reports this error code.
+   *
+   */
+  ERR_MODULE_NOT_FOUND = 157,
+
+  // Licensing, keep the license error code same as the main version
+  ERR_CERT_RAW = 157,
+  ERR_CERT_JSON_PART = 158,
+  ERR_CERT_JSON_INVAL = 159,
+  ERR_CERT_JSON_NOMEM = 160,
+  ERR_CERT_CUSTOM = 161,
+  ERR_CERT_CREDENTIAL = 162,
+  ERR_CERT_SIGN = 163,
+  ERR_CERT_FAIL = 164,
+  ERR_CERT_BUF = 165,
+  ERR_CERT_NULL = 166,
+  ERR_CERT_DUEDATE = 167,
+  ERR_CERT_REQUEST = 168,
+
+  // PcmSend Error num
+  ERR_PCMSEND_FORMAT = 200,           // unsupport pcm format
+  ERR_PCMSEND_BUFFEROVERFLOW = 201,  // buffer overflow, the pcm send rate too quickly
+
+  /// @cond
+  // signaling: 400~600
+  ERR_LOGIN_ALREADY_LOGIN = 428,
+
+  /// @endcond
+  // 1001~2000
+  /**
+   * 1001: Fails to load the media engine.
+   */
+  ERR_LOAD_MEDIA_ENGINE = 1001,
+  /**
+   * 1005: Audio device module: A general error occurs in the Audio Device Module (no specified
+   * reason). Check if the audio device is used by another app, or try
+   * rejoining the channel.
+   */
+  ERR_ADM_GENERAL_ERROR = 1005,
+  /**
+   * 1008: Audio Device Module: An error occurs in initializing the playback
+   * device.
+   */
+  ERR_ADM_INIT_PLAYOUT = 1008,
+  /**
+   * 1009: Audio Device Module: An error occurs in starting the playback device.
+   */
+  ERR_ADM_START_PLAYOUT = 1009,
+  /**
+   * 1010: Audio Device Module: An error occurs in stopping the playback device.
+   */
+  ERR_ADM_STOP_PLAYOUT = 1010,
+  /**
+   * 1011: Audio Device Module: An error occurs in initializing the recording
+   * device.
+   */
+  ERR_ADM_INIT_RECORDING = 1011,
+  /**
+   * 1012: Audio Device Module: An error occurs in starting the recording device.
+   */
+  ERR_ADM_START_RECORDING = 1012,
+  /**
+   * 1013: Audio Device Module: An error occurs in stopping the recording device.
+   */
+  ERR_ADM_STOP_RECORDING = 1013,
+  /**
+   * 1501: Video Device Module: The camera is not authorized.
+   */
+  ERR_VDM_CAMERA_NOT_AUTHORIZED = 1501,
+};
+
+enum LICENSE_ERROR_TYPE {
+  /**
+   * 1: Invalid license
+  */
+  LICENSE_ERR_INVALID = 1,
+  /**
+   * 2: License expired
+  */
+  LICENSE_ERR_EXPIRE = 2,
+  /**
+   * 3: Exceed license minutes limit
+  */
+  LICENSE_ERR_MINUTES_EXCEED = 3,
+  /**
+   * 4: License use in limited period
+  */
+  LICENSE_ERR_LIMITED_PERIOD = 4,
+  /**
+   * 5: Same license used in different devices at the same time
+  */
+  LICENSE_ERR_DIFF_DEVICES = 5,
+  /**
+   * 99: SDK internal error
+  */
+  LICENSE_ERR_INTERNAL = 99,
+};
+
+/**
+ * The operational permission of the SDK on the audio session.
+ */
+enum AUDIO_SESSION_OPERATION_RESTRICTION {
+  /**
+   * 0: No restriction; the SDK can change the audio session.
+   */
+  AUDIO_SESSION_OPERATION_RESTRICTION_NONE = 0,
+  /**
+   * 1: The SDK cannot change the audio session category.
+   */
+  AUDIO_SESSION_OPERATION_RESTRICTION_SET_CATEGORY = 1,
+  /**
+   * 2: The SDK cannot change the audio session category, mode, or categoryOptions.
+   */
+  AUDIO_SESSION_OPERATION_RESTRICTION_CONFIGURE_SESSION = 1 << 1,
+  /**
+   * 4: The SDK keeps the audio session active when the user leaves the
+   * channel, for example, to play an audio file in the background.
+   */
+  AUDIO_SESSION_OPERATION_RESTRICTION_DEACTIVATE_SESSION = 1 << 2,
+  /**
+   * 128: Completely restricts the operational permission of the SDK on the
+   * audio session; the SDK cannot change the audio session.
+   */
+  AUDIO_SESSION_OPERATION_RESTRICTION_ALL = 1 << 7,
+};
+
+typedef const char* user_id_t;
+typedef void* view_t;
+
+/**
+ * The definition of the UserInfo struct.
+ */
+struct UserInfo {
+  /**
+   * ID of the user.
+   */
+  util::AString userId;
+  /**
+   * Whether the user has enabled audio:
+   * - true: The user has enabled audio.
+   * - false: The user has disabled audio.
+   */
+  bool hasAudio;
+  /**
+   * Whether the user has enabled video:
+   * - true: The user has enabled video.
+   * - false: The user has disabled video.
+   */
+  bool hasVideo;
+
+  UserInfo() : hasAudio(false), hasVideo(false) {}
+};
+
+typedef util::AList<UserInfo> UserList;
+
+// Shared between Agora Service and Rtc Engine
+namespace rtc {
+
+/**
+ * Reasons for a user being offline.
+ */
+enum USER_OFFLINE_REASON_TYPE {
+  /**
+   * 0: The user leaves the current channel.
+   */
+  USER_OFFLINE_QUIT = 0,
+  /**
+   * 1: The SDK times out and the user drops offline because no data packet was received within a certain
+   * period of time. If a user quits the call and the message is not passed to the SDK (due to an
+   * unreliable channel), the SDK assumes that the user drops offline.
+   */
+  USER_OFFLINE_DROPPED = 1,
+  /**
+   * 2: The user switches the client role from the host to the audience.
+   */
+  USER_OFFLINE_BECOME_AUDIENCE = 2,
+};
+
+enum INTERFACE_ID_TYPE {
+  AGORA_IID_AUDIO_DEVICE_MANAGER = 1,
+  AGORA_IID_VIDEO_DEVICE_MANAGER = 2,
+  AGORA_IID_PARAMETER_ENGINE = 3,
+  AGORA_IID_MEDIA_ENGINE = 4,
+  AGORA_IID_AUDIO_ENGINE = 5,
+  AGORA_IID_VIDEO_ENGINE = 6,
+  AGORA_IID_RTC_CONNECTION = 7,
+  AGORA_IID_SIGNALING_ENGINE = 8,
+  AGORA_IID_MEDIA_ENGINE_REGULATOR = 9,
+  AGORA_IID_LOCAL_SPATIAL_AUDIO = 11,
+  AGORA_IID_STATE_SYNC = 13,
+  AGORA_IID_META_SERVICE = 14,
+  AGORA_IID_MUSIC_CONTENT_CENTER = 15,
+  AGORA_IID_H265_TRANSCODER = 16,  
+};
+
+/**
+ * The network quality types.
+ */
+enum QUALITY_TYPE {
+  /**
+   * 0: The network quality is unknown.
+   * @deprecated This member is deprecated.
+   */
+  QUALITY_UNKNOWN __deprecated = 0,
+  /**
+   * 1: The quality is excellent.
+   */
+  QUALITY_EXCELLENT = 1,
+  /**
+   * 2: The quality is quite good, but the bitrate may be slightly
+   * lower than excellent.
+   */
+  QUALITY_GOOD = 2,
+  /**
+   * 3: Users can feel the communication slightly impaired.
+   */
+  QUALITY_POOR = 3,
+  /**
+   * 4: Users cannot communicate smoothly.
+   */
+  QUALITY_BAD = 4,
+  /**
+   * 5: Users can barely communicate.
+   */
+  QUALITY_VBAD = 5,
+  /**
+   * 6: Users cannot communicate at all.
+   */
+  QUALITY_DOWN = 6,
+  /**
+   * 7: (For future use) The network quality cannot be detected.
+   */
+  QUALITY_UNSUPPORTED = 7,
+  /**
+   * 8: Detecting the network quality.
+   */
+  QUALITY_DETECTING = 8,
+};
+
+/**
+ * Content fit modes.
+ */
+enum FIT_MODE_TYPE {
+  /**
+   * 1: Uniformly scale the video until it fills the visible boundaries (cropped).
+   * One dimension of the video may have clipped contents.
+   */
+  MODE_COVER = 1,
+
+  /**
+   * 2: Uniformly scale the video until one of its dimension fits the boundary
+   * (zoomed to fit). Areas that are not filled due to disparity in the aspect
+   * ratio are filled with black.
+   */
+  MODE_CONTAIN = 2,
+};
+
+/**
+ * The rotation information.
+ */
+enum VIDEO_ORIENTATION {
+  /**
+   * 0: Rotate the video by 0 degree clockwise.
+   */
+  VIDEO_ORIENTATION_0 = 0,
+  /**
+   * 90: Rotate the video by 90 degrees clockwise.
+   */
+  VIDEO_ORIENTATION_90 = 90,
+  /**
+   * 180: Rotate the video by 180 degrees clockwise.
+   */
+  VIDEO_ORIENTATION_180 = 180,
+  /**
+   * 270: Rotate the video by 270 degrees clockwise.
+   */
+  VIDEO_ORIENTATION_270 = 270
+};
+
+/**
+ * The video frame rate.
+ */
+enum FRAME_RATE {
+  /**
+   * 1: 1 fps.
+   */
+  FRAME_RATE_FPS_1 = 1,
+  /**
+   * 7: 7 fps.
+   */
+  FRAME_RATE_FPS_7 = 7,
+  /**
+   * 10: 10 fps.
+   */
+  FRAME_RATE_FPS_10 = 10,
+  /**
+   * 15: 15 fps.
+   */
+  FRAME_RATE_FPS_15 = 15,
+  /**
+   * 24: 24 fps.
+   */
+  FRAME_RATE_FPS_24 = 24,
+  /**
+   * 30: 30 fps.
+   */
+  FRAME_RATE_FPS_30 = 30,
+  /**
+   * 60: 60 fps. Applies to Windows and macOS only.
+   */
+  FRAME_RATE_FPS_60 = 60,
+};
+
+enum FRAME_WIDTH {
+  FRAME_WIDTH_960 = 960,
+};
+
+enum FRAME_HEIGHT {
+  FRAME_HEIGHT_540 = 540,
+};
+
+
+/**
+ * Types of the video frame.
+ */
+enum VIDEO_FRAME_TYPE {
+  /** 0: A black frame. */
+  VIDEO_FRAME_TYPE_BLANK_FRAME = 0,
+  /** 3: Key frame. */
+  VIDEO_FRAME_TYPE_KEY_FRAME = 3,
+  /** 4: Delta frame. */
+  VIDEO_FRAME_TYPE_DELTA_FRAME = 4,
+  /** 5: The B frame.*/
+  VIDEO_FRAME_TYPE_B_FRAME = 5,
+  /** 6: A discarded frame. */
+  VIDEO_FRAME_TYPE_DROPPABLE_FRAME = 6,
+  /** Unknown frame. */
+  VIDEO_FRAME_TYPE_UNKNOW
+};
+
+/**
+ * Video output orientation modes.
+ */
+enum ORIENTATION_MODE {
+  /**
+   * 0: The output video always follows the orientation of the captured video. The receiver takes
+   * the rotational information passed on from the video encoder. This mode applies to scenarios
+   * where video orientation can be adjusted on the receiver:
+   * - If the captured video is in landscape mode, the output video is in landscape mode.
+   * - If the captured video is in portrait mode, the output video is in portrait mode.
+   */
+  ORIENTATION_MODE_ADAPTIVE = 0,
+  /**
+   * 1: Landscape mode. In this mode, the SDK always outputs videos in landscape (horizontal) mode.
+   * If the captured video is in portrait mode, the video encoder crops it to fit the output. Applies
+   * to situations where the receiving end cannot process the rotational information. For example,
+   * CDN live streaming.
+   */
+  ORIENTATION_MODE_FIXED_LANDSCAPE = 1,
+  /**
+   * 2: Portrait mode. In this mode, the SDK always outputs video in portrait (portrait) mode. If
+   * the captured video is in landscape mode, the video encoder crops it to fit the output. Applies
+   * to situations where the receiving end cannot process the rotational information. For example,
+   * CDN live streaming.
+   */
+  ORIENTATION_MODE_FIXED_PORTRAIT = 2,
+};
+
+/**
+ * (For future use) Video degradation preferences under limited bandwidth.
+ */
+enum DEGRADATION_PREFERENCE {
+  /**
+   * 0: (Default) Prefers to reduce the video frame rate while maintaining video quality during video
+   * encoding under limited bandwidth. This degradation preference is suitable for scenarios where
+   * video quality is prioritized.
+   * @note In the COMMUNICATION channel profile, the resolution of the video sent may change, so
+   * remote users need to handle this issue.
+   */
+  MAINTAIN_QUALITY = 0,
+  /**
+   * 1: Prefers to reduce the video quality while maintaining the video frame rate during video
+   * encoding under limited bandwidth. This degradation preference is suitable for scenarios where
+   * smoothness is prioritized and video quality is allowed to be reduced.
+   */
+  MAINTAIN_FRAMERATE = 1,
+  /**
+   * 2: Reduces the video frame rate and video quality simultaneously during video encoding under
+   * limited bandwidth. MAINTAIN_BALANCED has a lower reduction than MAINTAIN_QUALITY and MAINTAIN_FRAMERATE,
+   * and this preference is suitable for scenarios where both smoothness and video quality are a
+   * priority.
+   */
+  MAINTAIN_BALANCED = 2,
+  /**
+   * 3: Degrade framerate in order to maintain resolution.
+   */
+  MAINTAIN_RESOLUTION = 3,
+  /**
+   * 4: Disable VQC adjustion.
+   */
+  DISABLED = 100,
+};
+
+/**
+ * The definition of the VideoDimensions struct.
+ */
+struct VideoDimensions {
+  /**
+   * The width of the video, in pixels.
+   */
+  int width;
+  /**
+   * The height of the video, in pixels.
+   */
+  int height;
+  VideoDimensions() : width(640), height(480) {}
+  VideoDimensions(int w, int h) : width(w), height(h) {}
+  bool operator==(const VideoDimensions& rhs) const {
+    return width == rhs.width && height == rhs.height;
+  }
+};
+
+/**
+ * (Recommended) 0: Standard bitrate mode.
+ *
+ * In this mode, the video bitrate is twice the base bitrate.
+ */
+const int STANDARD_BITRATE = 0;
+
+/**
+ * -1: Compatible bitrate mode.
+ *
+ * In this mode, the video bitrate is the same as the base bitrate.. If you choose
+ * this mode in the live-broadcast profile, the video frame rate may be lower
+ * than the set value.
+ */
+const int COMPATIBLE_BITRATE = -1;
+
+/**
+ * -1: (For future use) The default minimum bitrate.
+ */
+const int DEFAULT_MIN_BITRATE = -1;
+
+/**
+ * -2: (For future use) Set minimum bitrate the same as target bitrate.
+ */
+const int DEFAULT_MIN_BITRATE_EQUAL_TO_TARGET_BITRATE = -2;
+
+/**
+ * screen sharing supported capability level.
+ */
+enum SCREEN_CAPTURE_FRAMERATE_CAPABILITY {
+  SCREEN_CAPTURE_FRAMERATE_CAPABILITY_15_FPS = 0,
+  SCREEN_CAPTURE_FRAMERATE_CAPABILITY_30_FPS = 1,
+  SCREEN_CAPTURE_FRAMERATE_CAPABILITY_60_FPS = 2,
+};
+
+/**
+ * Video codec capability levels.
+ */
+enum VIDEO_CODEC_CAPABILITY_LEVEL {
+  /** No specified level */
+  CODEC_CAPABILITY_LEVEL_UNSPECIFIED = -1,
+  /** Only provide basic support for the codec type */
+  CODEC_CAPABILITY_LEVEL_BASIC_SUPPORT = 5,
+  /** Can process 1080p video at a rate of approximately 30 fps. */
+  CODEC_CAPABILITY_LEVEL_1080P30FPS = 10,
+  /** Can process 1080p video at a rate of approximately 60 fps. */
+  CODEC_CAPABILITY_LEVEL_1080P60FPS = 20,
+  /** Can process 4k video at a rate of approximately 30 fps. */
+  CODEC_CAPABILITY_LEVEL_4K60FPS = 30,
+};
+
+/**
+ * The video codec types.
+ */
+enum VIDEO_CODEC_TYPE {
+  VIDEO_CODEC_NONE = 0,
+  /**
+   * 1: Standard VP8.
+   */
+  VIDEO_CODEC_VP8 = 1,
+  /**
+   * 2: Standard H.264.
+   */
+  VIDEO_CODEC_H264 = 2,
+  /**
+   * 3: Standard H.265.
+   */
+  VIDEO_CODEC_H265 = 3,
+  /**
+   * 6: Generic. This type is used for transmitting raw video data, such as encrypted video frames.
+   * The SDK returns this type of video frames in callbacks, and you need to decode and render the frames yourself.
+   */
+  VIDEO_CODEC_GENERIC = 6,
+  /**
+   * 7: Generic H264.
+   */
+  VIDEO_CODEC_GENERIC_H264 = 7,
+  /**
+   * 12: AV1.
+   * @technical preview
+   */
+  VIDEO_CODEC_AV1 = 12,
+  /**
+   * 13: VP9.
+   */
+  VIDEO_CODEC_VP9 = 13,
+  /**
+   * 20: Generic JPEG. This type consumes minimum computing resources and applies to IoT devices.
+   */
+  VIDEO_CODEC_GENERIC_JPEG = 20,
+};
+
+/**
+ * Camera focal length type.
+ */
+enum CAMERA_FOCAL_LENGTH_TYPE {
+  /**
+   * By default, there are no wide-angle and ultra-wide-angle properties.
+   */
+  CAMERA_FOCAL_LENGTH_DEFAULT = 0,
+  /**
+   * Lens with focal length from 24mm to 35mm.
+   */
+  CAMERA_FOCAL_LENGTH_WIDE_ANGLE = 1,
+  /**
+   * Lens with focal length of less than 24mm.
+   */
+  CAMERA_FOCAL_LENGTH_ULTRA_WIDE = 2,
+  /**
+   * Telephoto lens.
+   */
+  CAMERA_FOCAL_LENGTH_TELEPHOTO = 3,
+};
+
+/**
+ * The CC (Congestion Control) mode options.
+ */
+enum TCcMode {
+  /**
+   * Enable CC mode.
+   */
+  CC_ENABLED,
+  /**
+   * Disable CC mode.
+   */
+  CC_DISABLED,
+};
+
+/**
+ * The configuration for creating a local video track with an encoded image sender.
+ */
+struct SenderOptions {
+  /**
+   * Whether to enable CC mode. See #TCcMode.
+   */
+  TCcMode ccMode;
+  /**
+   * The codec type used for the encoded images: \ref agora::rtc::VIDEO_CODEC_TYPE "VIDEO_CODEC_TYPE".
+   */
+  VIDEO_CODEC_TYPE codecType;
+
+  /**
+   * Target bitrate (Kbps) for video encoding.
+   *
+   * Choose one of the following options:
+   *
+   * - \ref agora::rtc::STANDARD_BITRATE "STANDARD_BITRATE": (Recommended) Standard bitrate.
+   *   - Communication profile: The encoding bitrate equals the base bitrate.
+   *   - Live-broadcast profile: The encoding bitrate is twice the base bitrate.
+   * - \ref agora::rtc::COMPATIBLE_BITRATE "COMPATIBLE_BITRATE": Compatible bitrate. The bitrate stays the same
+   * regardless of the profile.
+   *
+   * The Communication profile prioritizes smoothness, while the Live Broadcast
+   * profile prioritizes video quality (requiring a higher bitrate). Agora
+   * recommends setting the bitrate mode as \ref agora::rtc::STANDARD_BITRATE "STANDARD_BITRATE" or simply to
+   * address this difference.
+   *
+   * The following table lists the recommended video encoder configurations,
+   * where the base bitrate applies to the communication profile. Set your
+   * bitrate based on this table. If the bitrate you set is beyond the proper
+   * range, the SDK automatically sets it to within the range.
+
+   | Resolution             | Frame Rate (fps) | Base Bitrate (Kbps, for Communication) | Live Bitrate (Kbps, for Live Broadcast)|
+   |------------------------|------------------|----------------------------------------|----------------------------------------|
+   | 160 &times; 120        | 15               | 65                                     | 130 |
+   | 120 &times; 120        | 15               | 50                                     | 100 |
+   | 320 &times; 180        | 15               | 140                                    | 280 |
+   | 180 &times; 180        | 15               | 100                                    | 200 |
+   | 240 &times; 180        | 15               | 120                                    | 240 |
+   | 320 &times; 240        | 15               | 200                                    | 400 |
+   | 240 &times; 240        | 15               | 140                                    | 280 |
+   | 424 &times; 240        | 15               | 220                                    | 440 |
+   | 640 &times; 360        | 15               | 400                                    | 800 |
+   | 360 &times; 360        | 15               | 260                                    | 520 |
+   | 640 &times; 360        | 30               | 600                                    | 1200 |
+   | 360 &times; 360        | 30               | 400                                    | 800 |
+   | 480 &times; 360        | 15               | 320                                    | 640 |
+   | 480 &times; 360        | 30               | 490                                    | 980 |
+   | 640 &times; 480        | 15               | 500                                    | 1000 |
+   | 480 &times; 480        | 15               | 400                                    | 800 |
+   | 640 &times; 480        | 30               | 750                                    | 1500 |
+   | 480 &times; 480        | 30               | 600                                    | 1200 |
+   | 848 &times; 480        | 15               | 610                                    | 1220 |
+   | 848 &times; 480        | 30               | 930                                    | 1860 |
+   | 640 &times; 480        | 10               | 400                                    | 800 |
+   | 1280 &times; 720       | 15               | 1130                                   | 2260 |
+   | 1280 &times; 720       | 30               | 1710                                   | 3420 |
+   | 960 &times; 720        | 15               | 910                                    | 1820 |
+   | 960 &times; 720        | 30               | 1380                                   | 2760 |
+   | 1920 &times; 1080      | 15               | 2080                                   | 4160 |
+   | 1920 &times; 1080      | 30               | 3150                                   | 6300 |
+   | 1920 &times; 1080      | 60               | 4780                                   | 6500 |
+   | 2560 &times; 1440      | 30               | 4850                                   | 6500 |
+   | 2560 &times; 1440      | 60               | 6500                                   | 6500 |
+   | 3840 &times; 2160      | 30               | 6500                                   | 6500 |
+   | 3840 &times; 2160      | 60               | 6500                                   | 6500 |
+   */
+  int targetBitrate;
+
+  SenderOptions()
+  : ccMode(CC_ENABLED),
+    codecType(VIDEO_CODEC_H265),
+    targetBitrate(6500) {}
+};
+
+/**
+ * Audio codec types.
+ */
+enum AUDIO_CODEC_TYPE {
+  /**
+   * 1: OPUS.
+   */
+  AUDIO_CODEC_OPUS = 1,
+  // kIsac = 2,
+  /**
+   * 3: PCMA.
+   */
+  AUDIO_CODEC_PCMA = 3,
+  /**
+   * 4: PCMU.
+   */
+  AUDIO_CODEC_PCMU = 4,
+  /**
+   * 5: G722.
+   */
+  AUDIO_CODEC_G722 = 5,
+  // kIlbc = 6,
+  /** 7: AAC. */
+  // AUDIO_CODEC_AAC = 7,
+  /**
+   * 8: AAC LC.
+   */
+  AUDIO_CODEC_AACLC = 8,
+  /**
+   * 9: HE AAC.
+   */
+  AUDIO_CODEC_HEAAC = 9,
+  /**
+   * 10: JC1.
+   */
+  AUDIO_CODEC_JC1 = 10,
+  /**
+   * 11: HE-AAC v2.
+   */
+  AUDIO_CODEC_HEAAC2 = 11,
+  /**
+   * 12: LPCNET.
+   */
+  AUDIO_CODEC_LPCNET = 12,
+};
+
+/**
+ * Audio encoding types of the audio encoded frame observer.
+ */
+enum AUDIO_ENCODING_TYPE {
+  /**
+   * AAC encoding format, 16000 Hz sampling rate, bass quality. A file with an audio duration of 10
+   * minutes is approximately 1.2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_16000_LOW = 0x010101,
+  /**
+   * AAC encoding format, 16000 Hz sampling rate, medium sound quality. A file with an audio duration
+   * of 10 minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_16000_MEDIUM = 0x010102,
+  /**
+   * AAC encoding format, 32000 Hz sampling rate, bass quality. A file with an audio duration of 10
+   * minutes is approximately 1.2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_32000_LOW = 0x010201,
+  /**
+   * AAC encoding format, 32000 Hz sampling rate, medium sound quality. A file with an audio duration
+   * of 10 minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_32000_MEDIUM = 0x010202,
+  /**
+   * AAC encoding format, 32000 Hz sampling rate, high sound quality. A file with an audio duration of
+   * 10 minutes is approximately 3.5 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_32000_HIGH = 0x010203,
+  /**
+   * AAC encoding format, 48000 Hz sampling rate, medium sound quality. A file with an audio duration
+   * of 10 minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_48000_MEDIUM = 0x010302,
+  /**
+   * AAC encoding format, 48000 Hz sampling rate, high sound quality. A file with an audio duration
+   * of 10 minutes is approximately 3.5 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_AAC_48000_HIGH = 0x010303,
+  /**
+   * OPUS encoding format, 16000 Hz sampling rate, bass quality. A file with an audio duration of 10
+   * minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_OPUS_16000_LOW = 0x020101,
+  /**
+   * OPUS encoding format, 16000 Hz sampling rate, medium sound quality. A file with an audio duration
+   * of 10 minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_OPUS_16000_MEDIUM = 0x020102,
+  /**
+   * OPUS encoding format, 48000 Hz sampling rate, medium sound quality. A file with an audio duration
+   * of 10 minutes is approximately 2 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_OPUS_48000_MEDIUM = 0x020302,
+  /**
+   * OPUS encoding format, 48000 Hz sampling rate, high sound quality. A file with an audio duration of
+   * 10 minutes is approximately 3.5 MB after encoding.
+   */
+  AUDIO_ENCODING_TYPE_OPUS_48000_HIGH = 0x020303,
+};
+
+/**
+ * The adaptation mode of the watermark.
+ */
+enum WATERMARK_FIT_MODE {
+  /**
+   * Use the `positionInLandscapeMode` and `positionInPortraitMode` values you set in #WatermarkOptions.
+   * The settings in `WatermarkRatio` are invalid.
+   */
+  FIT_MODE_COVER_POSITION,
+  /**
+   * Use the value you set in `WatermarkRatio`. The settings in `positionInLandscapeMode` and `positionInPortraitMode`
+   * in `WatermarkOptions` are invalid.
+   */
+  FIT_MODE_USE_IMAGE_RATIO
+};
+
+/**
+ * The advanced settings of encoded audio frame.
+ */
+struct EncodedAudioFrameAdvancedSettings {
+  EncodedAudioFrameAdvancedSettings()
+    : speech(true),
+      sendEvenIfEmpty(true) {}
+
+  /**
+   * Determines whether the audio source is speech.
+   * - true: (Default) The audio source is speech.
+   * - false: The audio source is not speech.
+   */
+  bool speech;
+  /**
+   * Whether to send the audio frame even when it is empty.
+   * - true: (Default) Send the audio frame even when it is empty.
+   * - false: Do not send the audio frame when it is empty.
+   */
+  bool sendEvenIfEmpty;
+};
+
+/**
+ * The definition of the EncodedAudioFrameInfo struct.
+ */
+struct EncodedAudioFrameInfo {
+  EncodedAudioFrameInfo()
+    : codec(AUDIO_CODEC_AACLC),
+      sampleRateHz(0),
+      samplesPerChannel(0),
+      numberOfChannels(0),
+      captureTimeMs(0) {}
+
+  EncodedAudioFrameInfo(const EncodedAudioFrameInfo& rhs)
+    : codec(rhs.codec),
+      sampleRateHz(rhs.sampleRateHz),
+      samplesPerChannel(rhs.samplesPerChannel),
+      numberOfChannels(rhs.numberOfChannels),
+      advancedSettings(rhs.advancedSettings),
+      captureTimeMs(rhs.captureTimeMs) {}
+  /**
+   * The audio codec: #AUDIO_CODEC_TYPE.
+   */
+  AUDIO_CODEC_TYPE codec;
+  /**
+   * The sample rate (Hz) of the audio frame.
+   */
+  int sampleRateHz;
+  /**
+   * The number of samples per audio channel.
+   *
+   * If this value is not set, it is 1024 for AAC, or 960 for OPUS by default.
+   */
+  int samplesPerChannel;
+  /**
+   * The number of audio channels of the audio frame.
+   */
+  int numberOfChannels;
+  /**
+   * The advanced settings of the audio frame.
+   */
+  EncodedAudioFrameAdvancedSettings advancedSettings;
+
+  /**
+   * This is a input parameter which means the timestamp for capturing the audio frame.
+   */
+  int64_t captureTimeMs;
+};
+/**
+ * The definition of the AudioPcmDataInfo struct.
+ */
+struct AudioPcmDataInfo {
+  AudioPcmDataInfo() : samplesPerChannel(0), channelNum(0), samplesOut(0), elapsedTimeMs(0), ntpTimeMs(0) {}
+
+  AudioPcmDataInfo(const AudioPcmDataInfo& rhs)
+    : samplesPerChannel(rhs.samplesPerChannel),
+      channelNum(rhs.channelNum),
+      samplesOut(rhs.samplesOut),
+      elapsedTimeMs(rhs.elapsedTimeMs),
+      ntpTimeMs(rhs.ntpTimeMs) {}
+
+  /**
+   * The sample count of the PCM data that you expect.
+   */
+  size_t samplesPerChannel;
+
+  int16_t channelNum;
+
+  // Output
+  /**
+   * The number of output samples.
+   */
+  size_t samplesOut;
+  /**
+   * The rendering time (ms).
+   */
+  int64_t elapsedTimeMs;
+  /**
+   * The NTP (Network Time Protocol) timestamp (ms).
+   */
+  int64_t ntpTimeMs;
+};
+/**
+ * Packetization modes. Applies to H.264 only.
+ */
+enum H264PacketizeMode {
+  /**
+   * Non-interleaved mode. See RFC 6184.
+   */
+  NonInterleaved = 0,  // Mode 1 - STAP-A, FU-A is allowed
+  /**
+   * Single NAL unit mode. See RFC 6184.
+   */
+  SingleNalUnit,       // Mode 0 - only single NALU allowed
+};
+
+/**
+ * Video stream types.
+ */
+enum VIDEO_STREAM_TYPE {
+  /**
+   * 0: The high-quality video stream, which has the highest resolution and bitrate.
+   */
+  VIDEO_STREAM_HIGH = 0,
+  /**
+   * 1: The low-quality video stream, which has the lowest resolution and bitrate.
+   */
+  VIDEO_STREAM_LOW = 1,
+  /**
+   * 4: The video stream of layer_1, which has a lower resolution and bitrate than VIDEO_STREAM_HIGH.
+   */
+  VIDEO_STREAM_LAYER_1 = 4,
+  /**
+   * 5: The video stream of layer_2, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_1.
+   */
+  VIDEO_STREAM_LAYER_2 = 5,
+  /**
+   * 6: The video stream of layer_3, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_2.
+   */
+  VIDEO_STREAM_LAYER_3 = 6,
+  /**
+   * 7: The video stream of layer_4, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_3.
+   */
+  VIDEO_STREAM_LAYER_4 = 7,
+  /**
+   * 8: The video stream of layer_5, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_4.
+   */
+  VIDEO_STREAM_LAYER_5 = 8,
+  /**
+   * 9: The video stream of layer_6, which has a lower resolution and bitrate than VIDEO_STREAM_LAYER_5.
+   */
+  VIDEO_STREAM_LAYER_6 = 9,
+
+};
+
+struct VideoSubscriptionOptions {
+    /**
+     * The type of the video stream to subscribe to.
+     *
+     * The default value is `VIDEO_STREAM_HIGH`, which means the high-quality
+     * video stream.
+     */
+    Optional<VIDEO_STREAM_TYPE> type;
+    /**
+     * Whether to subscribe to encoded video data only:
+     * - `true`: Subscribe to encoded video data only.
+     * - `false`: (Default) Subscribe to decoded video data.
+     */
+    Optional<bool> encodedFrameOnly;
+
+    VideoSubscriptionOptions() {}
+};
+
+
+/** The maximum length of the user account.
+ */
+enum MAX_USER_ACCOUNT_LENGTH_TYPE
+{
+  /** The maximum length of the user account is 256 bytes.
+   */
+  MAX_USER_ACCOUNT_LENGTH = 256
+};
+
+/**
+ * The definition of the EncodedVideoFrameInfo struct, which contains the information of the external encoded video frame.
+ */
+struct EncodedVideoFrameInfo {
+  EncodedVideoFrameInfo()
+    : uid(0),
+      codecType(VIDEO_CODEC_H264),
+      width(0),
+      height(0),
+      framesPerSecond(0),
+      frameType(VIDEO_FRAME_TYPE_BLANK_FRAME),
+      rotation(VIDEO_ORIENTATION_0),
+      trackId(0),
+      captureTimeMs(0),
+      decodeTimeMs(0),
+      streamType(VIDEO_STREAM_HIGH),
+      presentationMs(-1) {}
+
+  EncodedVideoFrameInfo(const EncodedVideoFrameInfo& rhs)
+    : uid(rhs.uid),
+      codecType(rhs.codecType),
+      width(rhs.width),
+      height(rhs.height),
+      framesPerSecond(rhs.framesPerSecond),
+      frameType(rhs.frameType),
+      rotation(rhs.rotation),
+      trackId(rhs.trackId),
+      captureTimeMs(rhs.captureTimeMs),
+      decodeTimeMs(rhs.decodeTimeMs),
+      streamType(rhs.streamType),
+      presentationMs(rhs.presentationMs) {}
+
+  EncodedVideoFrameInfo& operator=(const EncodedVideoFrameInfo& rhs) {
+    if (this == &rhs) return *this;
+    uid = rhs.uid;
+    codecType = rhs.codecType;
+    width = rhs.width;
+    height = rhs.height;
+    framesPerSecond = rhs.framesPerSecond;
+    frameType = rhs.frameType;
+    rotation = rhs.rotation;
+    trackId = rhs.trackId;
+    captureTimeMs = rhs.captureTimeMs;
+    decodeTimeMs = rhs.decodeTimeMs;
+    streamType = rhs.streamType;
+    presentationMs = rhs.presentationMs;
+    return *this;
+  }
+
+  /**
+   * ID of the user that pushes the the external encoded video frame..
+   */
+  uid_t uid;
+  /**
+   * The codec type of the local video stream. See #VIDEO_CODEC_TYPE. The default value is `VIDEO_CODEC_H265 (3)`.
+   */
+  VIDEO_CODEC_TYPE codecType;
+  /**
+   * The width (px) of the video frame.
+   */
+  int width;
+  /**
+   * The height (px) of the video frame.
+   */
+  int height;
+  /**
+   * The number of video frames per second.
+   * When this parameter is not 0, you can use it to calculate the Unix timestamp of the external
+   * encoded video frames.
+   */
+  int framesPerSecond;
+  /**
+   * The video frame type: #VIDEO_FRAME_TYPE.
+   */
+  VIDEO_FRAME_TYPE frameType;
+  /**
+   * The rotation information of the video frame: #VIDEO_ORIENTATION.
+   */
+  VIDEO_ORIENTATION rotation;
+  /**
+   * The track ID of the video frame.
+   */
+  int trackId;  // This can be reserved for multiple video tracks, we need to create different ssrc
+                // and additional payload for later implementation.
+  /**
+   * This is a input parameter which means the timestamp for capturing the video.
+   */
+  int64_t captureTimeMs;
+  /**
+   * The timestamp for decoding the video.
+   */
+  int64_t decodeTimeMs;
+  /**
+   * The stream type of video frame.
+   */
+  VIDEO_STREAM_TYPE streamType;
+
+  // @technical preview
+  int64_t presentationMs;
+};
+
+/**
+* Video compression preference.
+*/
+enum COMPRESSION_PREFERENCE {
+  /**
+  * (Default) Low latency is preferred, usually used in real-time communication where low latency is the number one priority.
+  */
+  PREFER_LOW_LATENCY,
+  /**
+  * Prefer quality in sacrifice of a degree of latency, usually around 30ms ~ 150ms, depends target fps
+  */
+  PREFER_QUALITY,
+};
+
+/**
+* The video encoder type preference.
+*/
+enum ENCODING_PREFERENCE {
+  /**
+  *Default .
+   */
+  PREFER_AUTO = -1,
+  /**
+  *  Software encoding.
+  */
+  PREFER_SOFTWARE = 0,
+  /**
+  * Hardware encoding
+   */
+  PREFER_HARDWARE = 1,
+};
+
+/**
+ * The definition of the AdvanceOptions struct.
+ */
+struct AdvanceOptions {
+
+  /**
+   * The video encoder type preference..
+   */
+  ENCODING_PREFERENCE encodingPreference;
+
+  /**
+  * Video compression preference.
+  */
+  COMPRESSION_PREFERENCE compressionPreference;
+
+  AdvanceOptions() : encodingPreference(PREFER_AUTO), 
+                     compressionPreference(PREFER_LOW_LATENCY) {}
+
+  AdvanceOptions(ENCODING_PREFERENCE encoding_preference, 
+                 COMPRESSION_PREFERENCE compression_preference) : 
+                 encodingPreference(encoding_preference),
+                 compressionPreference(compression_preference) {}
+
+  bool operator==(const AdvanceOptions& rhs) const {
+    return encodingPreference == rhs.encodingPreference && 
+           compressionPreference == rhs.compressionPreference;
+  }
+
+};
+
+/**
+ * Video mirror mode types.
+ */
+enum VIDEO_MIRROR_MODE_TYPE {
+  /**
+   * 0: The mirror mode determined by the SDK.
+   */
+  VIDEO_MIRROR_MODE_AUTO = 0,
+  /**
+   * 1: Enable the mirror mode.
+   */
+  VIDEO_MIRROR_MODE_ENABLED = 1,
+  /**
+   * 2: Disable the mirror mode.
+   */
+  VIDEO_MIRROR_MODE_DISABLED = 2,
+};
+
+#if defined(__APPLE__) && TARGET_OS_IOS
+/**
+ * Camera capturer configuration for format type.
+ */
+enum CAMERA_FORMAT_TYPE {
+  /** 0: (Default) NV12. */
+  CAMERA_FORMAT_NV12,
+  /** 1: BGRA. */
+  CAMERA_FORMAT_BGRA,
+};
+#endif
+
+/** Supported codec type bit mask. */
+enum CODEC_CAP_MASK {
+  /** 0: No codec support. */
+  CODEC_CAP_MASK_NONE = 0,
+
+  /** bit 1: Hardware decoder support flag. */
+  CODEC_CAP_MASK_HW_DEC = 1 << 0,
+
+  /** bit 2: Hardware encoder support flag. */
+  CODEC_CAP_MASK_HW_ENC = 1 << 1,
+
+  /** bit 3: Software decoder support flag. */
+  CODEC_CAP_MASK_SW_DEC = 1 << 2,
+
+  /** bit 4: Software encoder support flag. */
+  CODEC_CAP_MASK_SW_ENC = 1 << 3,
+};
+
+struct CodecCapLevels {
+  VIDEO_CODEC_CAPABILITY_LEVEL hwDecodingLevel;
+  VIDEO_CODEC_CAPABILITY_LEVEL swDecodingLevel;
+
+  CodecCapLevels(): hwDecodingLevel(CODEC_CAPABILITY_LEVEL_UNSPECIFIED), swDecodingLevel(CODEC_CAPABILITY_LEVEL_UNSPECIFIED) {}
+};
+
+/** The codec support information. */
+struct CodecCapInfo {
+  /** The codec type: #VIDEO_CODEC_TYPE. */
+  VIDEO_CODEC_TYPE codecType;
+  /** The codec support flag. */
+  int codecCapMask;
+  /** The codec capability level, estimated based on the device hardware.*/
+  CodecCapLevels codecLevels;
+
+  CodecCapInfo(): codecType(VIDEO_CODEC_NONE), codecCapMask(0) {}
+};
+
+/** FocalLengthInfo contains the IDs of the front and rear cameras, along with the wide-angle types. */
+struct FocalLengthInfo {
+  /** The camera direction. */
+  int cameraDirection;
+  /** Camera focal segment type. */
+  CAMERA_FOCAL_LENGTH_TYPE focalLengthType;
+};
+
+/**
+ * The definition of the VideoEncoderConfiguration struct.
+ */
+struct VideoEncoderConfiguration {
+  /**
+   * The video encoder code type: #VIDEO_CODEC_TYPE.
+   */
+  VIDEO_CODEC_TYPE codecType;
+  /**
+   * The video dimension: VideoDimensions.
+   */
+  VideoDimensions dimensions;
+  /**
+   * The frame rate of the video. You can set it manually, or choose one from #FRAME_RATE.
+   */
+  int frameRate;
+  /**
+   * The bitrate (Kbps) of the video.
+   *
+   * Refer to the **Video Bitrate Table** below and set your bitrate. If you set a bitrate beyond the
+   * proper range, the SDK automatically adjusts it to a value within the range. You can also choose
+   * from the following options:
+   *
+   * - #STANDARD_BITRATE: (Recommended) Standard bitrate mode. In this mode, the bitrates differ between
+   * the Live Broadcast and Communication profiles:
+   *   - In the Communication profile, the video bitrate is the same as the base bitrate.
+   *   - In the Live Broadcast profile, the video bitrate is twice the base bitrate.
+   * - #COMPATIBLE_BITRATE: Compatible bitrate mode. The compatible bitrate mode. In this mode, the bitrate
+   * stays the same regardless of the profile. If you choose this mode for the Live Broadcast profile,
+   * the video frame rate may be lower than the set value.
+   *
+   * Agora uses different video codecs for different profiles to optimize the user experience. For example,
+   * the communication profile prioritizes the smoothness while the live-broadcast profile prioritizes the
+   * video quality (a higher bitrate). Therefore, We recommend setting this parameter as #STANDARD_BITRATE.
+   *
+   * | Resolution             | Frame Rate (fps) | Base Bitrate (Kbps) | Live Bitrate (Kbps)|
+   * |------------------------|------------------|---------------------|--------------------|
+   * | 160 * 120              | 15               | 65                  | 110                |
+   * | 120 * 120              | 15               | 50                  | 90                 |
+   * | 320 * 180              | 15               | 140                 | 240                |
+   * | 180 * 180              | 15               | 100                 | 160                |
+   * | 240 * 180              | 15               | 120                 | 200                |
+   * | 320 * 240              | 15               | 200                 | 300                |
+   * | 240 * 240              | 15               | 140                 | 240                |
+   * | 424 * 240              | 15               | 220                 | 370                |
+   * | 640 * 360              | 15               | 400                 | 680                |
+   * | 360 * 360              | 15               | 260                 | 440                |
+   * | 640 * 360              | 30               | 600                 | 1030               |
+   * | 360 * 360              | 30               | 400                 | 670                |
+   * | 480 * 360              | 15               | 320                 | 550                |
+   * | 480 * 360              | 30               | 490                 | 830                |
+   * | 640 * 480              | 15               | 500                 | 750                |
+   * | 480 * 480              | 15               | 400                 | 680                |
+   * | 640 * 480              | 30               | 750                 | 1130               |
+   * | 480 * 480              | 30               | 600                 | 1030               |
+   * | 848 * 480              | 15               | 610                 | 920                |
+   * | 848 * 480              | 30               | 930                 | 1400               |
+   * | 640 * 480              | 10               | 400                 | 600                |
+   * | 960 * 540              | 15               | 750                 | 1100               |
+   * | 960 * 540              | 30               | 1110                | 1670               |
+   * | 1280 * 720             | 15               | 1130                | 1600               |
+   * | 1280 * 720             | 30               | 1710                | 2400               |
+   * | 960 * 720              | 15               | 910                 | 1280               |
+   * | 960 * 720              | 30               | 1380                | 2000               |
+   * | 1920 * 1080            | 15               | 2080                | 2500               |
+   * | 1920 * 1080            | 30               | 3150                | 3780               |
+   * | 1920 * 1080            | 60               | 4780                | 5730               |
+   * | 2560 * 1440            | 30               | 4850                | 4850               |
+   * | 2560 * 1440            | 60               | 7350                | 7350               |
+   * | 3840 * 2160            | 30               | 8910                | 8910               |
+   * | 3840 * 2160            | 60               | 13500               | 13500              |
+   */
+  int bitrate;
+
+  /**
+   * The minimum encoding bitrate (Kbps).
+   *
+   * The Agora SDK automatically adjusts the encoding bitrate to adapt to the
+   * network conditions.
+   *
+   * Using a value greater than the default value forces the video encoder to
+   * output high-quality images but may cause more packet loss and hence
+   * sacrifice the smoothness of the video transmission. That said, unless you
+   * have special requirements for image quality, Agora does not recommend
+   * changing this value.
+   *
+   * @note
+   * This parameter applies to the live-broadcast profile only.
+   */
+  int minBitrate;
+  /**
+   * The video orientation mode: #ORIENTATION_MODE.
+   */
+  ORIENTATION_MODE orientationMode;
+  /**
+   * The video degradation preference under limited bandwidth: #DEGRADATION_PREFERENCE.
+   */
+  DEGRADATION_PREFERENCE degradationPreference;
+
+  /**
+   * The mirror mode is disabled by default
+   * If mirror_type is set to VIDEO_MIRROR_MODE_ENABLED, then the video frame would be mirrored before encoding.
+   */
+  VIDEO_MIRROR_MODE_TYPE mirrorMode;
+
+  /**
+   * The advanced options for the video encoder configuration. See AdvanceOptions.
+   */
+  AdvanceOptions advanceOptions;
+
+  VideoEncoderConfiguration(const VideoDimensions& d, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED)
+    : codecType(VIDEO_CODEC_NONE),
+      dimensions(d),
+      frameRate(f),
+      bitrate(b),
+      minBitrate(DEFAULT_MIN_BITRATE),
+      orientationMode(m),
+      degradationPreference(MAINTAIN_QUALITY),
+      mirrorMode(mirror),
+      advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {}
+  VideoEncoderConfiguration(int width, int height, int f, int b, ORIENTATION_MODE m, VIDEO_MIRROR_MODE_TYPE mirror = VIDEO_MIRROR_MODE_DISABLED)
+    : codecType(VIDEO_CODEC_NONE),
+      dimensions(width, height),
+      frameRate(f),
+      bitrate(b),
+      minBitrate(DEFAULT_MIN_BITRATE),
+      orientationMode(m),
+      degradationPreference(MAINTAIN_QUALITY),
+      mirrorMode(mirror),
+      advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {}
+  VideoEncoderConfiguration(const VideoEncoderConfiguration& config)
+    : codecType(config.codecType),
+      dimensions(config.dimensions),
+      frameRate(config.frameRate),
+      bitrate(config.bitrate),
+      minBitrate(config.minBitrate),
+      orientationMode(config.orientationMode),
+      degradationPreference(config.degradationPreference),
+      mirrorMode(config.mirrorMode),
+      advanceOptions(config.advanceOptions) {}
+  VideoEncoderConfiguration()
+    : codecType(VIDEO_CODEC_NONE),
+      dimensions(FRAME_WIDTH_960, FRAME_HEIGHT_540),
+      frameRate(FRAME_RATE_FPS_15),
+      bitrate(STANDARD_BITRATE),
+      minBitrate(DEFAULT_MIN_BITRATE),
+      orientationMode(ORIENTATION_MODE_ADAPTIVE),
+      degradationPreference(MAINTAIN_QUALITY),
+      mirrorMode(VIDEO_MIRROR_MODE_DISABLED),
+      advanceOptions(PREFER_AUTO, PREFER_LOW_LATENCY) {}
+
+  VideoEncoderConfiguration& operator=(const VideoEncoderConfiguration& rhs) {
+    if (this == &rhs) return *this;
+    codecType = rhs.codecType;
+    dimensions = rhs.dimensions;
+    frameRate = rhs.frameRate;
+    bitrate = rhs.bitrate;
+    minBitrate = rhs.minBitrate;
+    orientationMode = rhs.orientationMode;
+    degradationPreference = rhs.degradationPreference;
+    mirrorMode = rhs.mirrorMode;
+    advanceOptions = rhs.advanceOptions;
+    return *this;
+  }
+};
+
+/**
+ * The configurations for the data stream.
+ */
+struct DataStreamConfig {
+  /**
+   * Whether to synchronize the data packet with the published audio packet.
+   * - `true`: Synchronize the data packet with the audio packet.
+   * - `false`: Do not synchronize the data packet with the audio packet.
+   *
+   * When you set the data packet to synchronize with the audio, then if the data packet delay is
+   * within the audio delay, the SDK triggers the `onStreamMessage` callback when the synchronized
+   * audio packet is played out. Do not set this parameter as true if you need the receiver to receive
+   * the data packet immediately. Agora recommends that you set this parameter to `true` only when you
+   * need to implement specific functions, for example lyric synchronization.
+   */
+  bool syncWithAudio;
+  /**
+   * Whether the SDK guarantees that the receiver receives the data in the sent order.
+   * - `true`: Guarantee that the receiver receives the data in the sent order.
+   * - `false`: Do not guarantee that the receiver receives the data in the sent order.
+   *
+   * Do not set this parameter as `true` if you need the receiver to receive the data packet immediately.
+   */
+  bool ordered;
+};
+
+/**
+ * The definition of SIMULCAST_STREAM_MODE
+ */
+enum SIMULCAST_STREAM_MODE {
+  /*
+  * disable simulcast stream until receive request for enable simulcast stream by other broadcaster
+  */
+  AUTO_SIMULCAST_STREAM = -1,
+  /*
+  * disable simulcast stream
+  */
+  DISABLE_SIMULCAST_STREAM = 0,
+  /*
+  * always enable simulcast stream
+  */
+  ENABLE_SIMULCAST_STREAM = 1,
+};
+
+/**
+ * The configuration of the low-quality video stream.
+ */
+struct SimulcastStreamConfig {
+  /**
+   * The video frame dimension: VideoDimensions. The default value is 160 × 120.
+   */
+  VideoDimensions dimensions;
+  /**
+   * The video bitrate (Kbps), represented by an instantaneous value. The default value of the log level is 5.
+   */
+  int kBitrate;
+  /**
+   * The capture frame rate (fps) of the local video. The default value is 5.
+   */
+  int framerate;
+  SimulcastStreamConfig() : dimensions(160, 120), kBitrate(65), framerate(5) {}
+  SimulcastStreamConfig(const SimulcastStreamConfig& other) : dimensions(other.dimensions), kBitrate(other.kBitrate), framerate(other.framerate) {}
+  bool operator==(const SimulcastStreamConfig& rhs) const {
+    return dimensions == rhs.dimensions && kBitrate == rhs.kBitrate && framerate == rhs.framerate;
+  }
+};
+
+/**
+ * The configuration of the multi-layer video stream.
+ */
+struct SimulcastConfig {
+  /**
+   * The index of multi-layer video stream
+   */
+  enum StreamLayerIndex {
+   /**
+    * 0: video stream index of layer_1
+    */
+   STREAM_LAYER_1 = 0,
+   /**
+    * 1: video stream index of layer_2
+    */
+   STREAM_LAYER_2 = 1,
+   /**
+    * 2: video stream index of layer_3
+    */
+   STREAM_LAYER_3 = 2,
+   /**
+    * 3: video stream index of layer_4
+    */
+   STREAM_LAYER_4 = 3,
+   /**
+    * 4: video stream index of layer_5
+    */
+   STREAM_LAYER_5 = 4,
+   /**
+    * 5: video stream index of layer_6
+    */
+   STREAM_LAYER_6 = 5,
+   /**
+    * 6: video stream index of low
+    */
+   STREAM_LOW = 6,
+   /**
+    * 7: max count of video stream layers
+    */
+   STREAM_LAYER_COUNT_MAX = 7
+  };
+  struct StreamLayerConfig {
+    /**
+     * The video frame dimension. The default value is 0.
+     */
+    VideoDimensions dimensions;
+    /**
+     * The capture frame rate (fps) of the local video. The default value is 0.
+     */
+    int framerate;
+    /**
+     * Whether to enable the corresponding layer of video stream. The default value is false.
+     */
+    bool enable;
+    StreamLayerConfig() : dimensions(0, 0), framerate(0), enable(false) {}
+  };
+
+  /**
+   * The array of StreamLayerConfig, which contains STREAM_LAYER_COUNT_MAX layers of video stream at most.
+   */
+  StreamLayerConfig configs[STREAM_LAYER_COUNT_MAX];
+};
+/**
+ * The location of the target area relative to the screen or window. If you do not set this parameter,
+ * the SDK selects the whole screen or window.
+ */
+struct Rectangle {
+  /**
+   * The horizontal offset from the top-left corner.
+   */
+  int x;
+  /**
+   * The vertical offset from the top-left corner.
+   */
+  int y;
+  /**
+   * The width of the region.
+   */
+  int width;
+  /**
+   * The height of the region.
+   */
+  int height;
+
+  Rectangle() : x(0), y(0), width(0), height(0) {}
+  Rectangle(int xx, int yy, int ww, int hh) : x(xx), y(yy), width(ww), height(hh) {}
+};
+
+/**
+ * The position and size of the watermark on the screen.
+ *
+ * The position and size of the watermark on the screen are determined by `xRatio`, `yRatio`, and `widthRatio`:
+ * - (`xRatio`, `yRatio`) refers to the coordinates of the upper left corner of the watermark, which determines
+ *  the distance from the upper left corner of the watermark to the upper left corner of the screen.
+ * The `widthRatio` determines the width of the watermark.
+ */
+struct WatermarkRatio {
+  /**
+   * The x-coordinate of the upper left corner of the watermark. The horizontal position relative to
+   * the origin, where the upper left corner of the screen is the origin, and the x-coordinate is the
+   * upper left corner of the watermark. The value range is [0.0,1.0], and the default value is 0.
+   */
+  float xRatio;
+  /**
+   * The y-coordinate of the upper left corner of the watermark. The vertical position relative to the
+   * origin, where the upper left corner of the screen is the origin, and the y-coordinate is the upper
+   * left corner of the screen. The value range is [0.0,1.0], and the default value is 0.
+   */
+  float yRatio;
+  /**
+   * The width of the watermark. The SDK calculates the height of the watermark proportionally according
+   * to this parameter value to ensure that the enlarged or reduced watermark image is not distorted.
+   * The value range is [0,1], and the default value is 0, which means no watermark is displayed.
+   */
+  float widthRatio;
+
+  WatermarkRatio() : xRatio(0.0), yRatio(0.0), widthRatio(0.0) {}
+  WatermarkRatio(float x, float y, float width) : xRatio(x), yRatio(y), widthRatio(width) {}
+};
+
+/**
+ * Configurations of the watermark image.
+ */
+struct WatermarkOptions {
+  /**
+   * Whether or not the watermark image is visible in the local video preview:
+   * - true: (Default) The watermark image is visible in preview.
+   * - false: The watermark image is not visible in preview.
+   */
+  bool visibleInPreview;
+  /**
+   * When the adaptation mode of the watermark is `FIT_MODE_COVER_POSITION`, it is used to set the
+   * area of the watermark image in landscape mode. See #FIT_MODE_COVER_POSITION for details.
+   */
+  Rectangle positionInLandscapeMode;
+  /**
+   * When the adaptation mode of the watermark is `FIT_MODE_COVER_POSITION`, it is used to set the
+   * area of the watermark image in portrait mode. See #FIT_MODE_COVER_POSITION for details.
+   */
+  Rectangle positionInPortraitMode;
+  /**
+   * When the watermark adaptation mode is `FIT_MODE_USE_IMAGE_RATIO`, this parameter is used to set
+   * the watermark coordinates. See WatermarkRatio for details.
+   */
+  WatermarkRatio watermarkRatio;
+  /**
+   * The adaptation mode of the watermark. See #WATERMARK_FIT_MODE for details.
+   */
+  WATERMARK_FIT_MODE mode;
+
+  WatermarkOptions()
+    : visibleInPreview(true),
+      positionInLandscapeMode(0, 0, 0, 0),
+      positionInPortraitMode(0, 0, 0, 0),
+      mode(FIT_MODE_COVER_POSITION) {}
+};
+
+/**
+ * The definition of the RtcStats struct.
+ */
+struct RtcStats {
+  /**
+   * The call duration (s), represented by an aggregate value.
+   */
+  unsigned int duration;
+  /**
+   * The total number of bytes transmitted, represented by an aggregate value.
+   */
+  unsigned int txBytes;
+  /**
+   * The total number of bytes received, represented by an aggregate value.
+   */
+  unsigned int rxBytes;
+  /**
+   * The total number of audio bytes sent (bytes), represented by an aggregate value.
+   */
+  unsigned int txAudioBytes;
+  /**
+   * The total number of video bytes sent (bytes), represented by an aggregate value.
+   */
+  unsigned int txVideoBytes;
+  /**
+   * The total number of audio bytes received (bytes), represented by an aggregate value.
+   */
+  unsigned int rxAudioBytes;
+  /**
+   * The total number of video bytes received (bytes), represented by an aggregate value.
+   */
+  unsigned int rxVideoBytes;
+  /**
+   * The transmission bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short txKBitRate;
+  /**
+   * The receiving bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short rxKBitRate;
+  /**
+   * Audio receiving bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short rxAudioKBitRate;
+  /**
+   * The audio transmission bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short txAudioKBitRate;
+  /**
+   * The video receive bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short rxVideoKBitRate;
+  /**
+   * The video transmission bitrate (Kbps), represented by an instantaneous value.
+   */
+  unsigned short txVideoKBitRate;
+  /**
+   * The VOS client-server latency (ms).
+   */
+  unsigned short lastmileDelay;
+  /**
+   * The number of users in the channel.
+   */
+  unsigned int userCount;
+  /**
+   * The app CPU usage (%).
+   * @note
+   * - The value of `cpuAppUsage` is always reported as 0 in the `onLeaveChannel` callback.
+   * - As of Android 8.1, you cannot get the CPU usage from this attribute due to system limitations.
+   */
+  double cpuAppUsage;
+  /**
+   * The system CPU usage (%).
+   *
+   * For Windows, in the multi-kernel environment, this member represents the average CPU usage. The
+   * value = (100 - System Idle Progress in Task Manager)/100.
+   * @note
+   * - The value of `cpuTotalUsage` is always reported as 0 in the `onLeaveChannel` callback.
+   * - As of Android 8.1, you cannot get the CPU usage from this attribute due to system limitations.
+   */
+  double cpuTotalUsage;
+  /**
+   * The round-trip time delay from the client to the local router.
+   * @note On Android, to get `gatewayRtt`, ensure that you add the `android.permission.ACCESS_WIFI_STATE`
+   * permission after `</application>` in the `AndroidManifest.xml` file in your project.
+   */
+  int gatewayRtt;
+  /**
+   * The memory usage ratio of the app (%).
+   * @note This value is for reference only. Due to system limitations, you may not get this value.
+   */
+  double memoryAppUsageRatio;
+  /**
+   * The memory usage ratio of the system (%).
+   * @note This value is for reference only. Due to system limitations, you may not get this value.
+   */
+  double memoryTotalUsageRatio;
+  /**
+   * The memory usage of the app (KB).
+   * @note This value is for reference only. Due to system limitations, you may not get this value.
+   */
+  int memoryAppUsageInKbytes;
+  /**
+   * The time elapsed from the when the app starts connecting to an Agora channel
+   * to when the connection is established. 0 indicates that this member does not apply.
+   */
+  int connectTimeMs;
+  /**
+   * The duration (ms) between the app starting connecting to an Agora channel
+   * and the first audio packet is received. 0 indicates that this member does not apply.
+   */
+  int firstAudioPacketDuration;
+  /**
+   * The duration (ms) between the app starting connecting to an Agora channel
+   * and the first video packet is received. 0 indicates that this member does not apply.
+   */
+  int firstVideoPacketDuration;
+  /**
+   * The duration (ms) between the app starting connecting to an Agora channel
+   * and the first video key frame is received. 0 indicates that this member does not apply.
+   */
+  int firstVideoKeyFramePacketDuration;
+  /**
+   * The number of video packets before the first video key frame is received.
+   * 0 indicates that this member does not apply.
+   */
+  int packetsBeforeFirstKeyFramePacket;
+  /**
+   * The duration (ms) between the last time unmute audio and the first audio packet is received.
+   * 0 indicates that this member does not apply.
+   */
+  int firstAudioPacketDurationAfterUnmute;
+  /**
+   * The duration (ms) between the last time unmute video and the first video packet is received.
+   * 0 indicates that this member does not apply.
+   */
+  int firstVideoPacketDurationAfterUnmute;
+  /**
+   * The duration (ms) between the last time unmute video and the first video key frame is received.
+   * 0 indicates that this member does not apply.
+   */
+  int firstVideoKeyFramePacketDurationAfterUnmute;
+  /**
+   * The duration (ms) between the last time unmute video and the first video key frame is decoded.
+   * 0 indicates that this member does not apply.
+   */
+  int firstVideoKeyFrameDecodedDurationAfterUnmute;
+  /**
+   * The duration (ms) between the last time unmute video and the first video key frame is rendered.
+   * 0 indicates that this member does not apply.
+   */
+  int firstVideoKeyFrameRenderedDurationAfterUnmute;
+  /**
+   * The packet loss rate of sender(broadcaster).
+   */
+  int txPacketLossRate;
+  /**
+   * The packet loss rate of receiver(audience).
+   */
+  int rxPacketLossRate;
+  RtcStats()
+    : duration(0),
+      txBytes(0),
+      rxBytes(0),
+      txAudioBytes(0),
+      txVideoBytes(0),
+      rxAudioBytes(0),
+      rxVideoBytes(0),
+      txKBitRate(0),
+      rxKBitRate(0),
+      rxAudioKBitRate(0),
+      txAudioKBitRate(0),
+      rxVideoKBitRate(0),
+      txVideoKBitRate(0),
+      lastmileDelay(0),
+      userCount(0),
+      cpuAppUsage(0.0),
+      cpuTotalUsage(0.0),
+      gatewayRtt(0),
+      memoryAppUsageRatio(0.0),
+      memoryTotalUsageRatio(0.0),
+      memoryAppUsageInKbytes(0),
+      connectTimeMs(0),
+      firstAudioPacketDuration(0),
+      firstVideoPacketDuration(0),
+      firstVideoKeyFramePacketDuration(0),
+      packetsBeforeFirstKeyFramePacket(0),
+      firstAudioPacketDurationAfterUnmute(0),
+      firstVideoPacketDurationAfterUnmute(0),
+      firstVideoKeyFramePacketDurationAfterUnmute(0),
+      firstVideoKeyFrameDecodedDurationAfterUnmute(0),
+      firstVideoKeyFrameRenderedDurationAfterUnmute(0),
+      txPacketLossRate(0),
+      rxPacketLossRate(0) {}
+};
+
+/**
+ * User role types.
+ */
+enum CLIENT_ROLE_TYPE {
+  /**
+   * 1: Broadcaster. A broadcaster can both send and receive streams.
+   */
+  CLIENT_ROLE_BROADCASTER = 1,
+  /**
+   * 2: Audience. An audience member can only receive streams.
+   */
+  CLIENT_ROLE_AUDIENCE = 2,
+};
+
+/**
+ * Quality change of the local video in terms of target frame rate and target bit rate since last count.
+ */
+enum QUALITY_ADAPT_INDICATION {
+  /**
+   * 0: The quality of the local video stays the same.
+   */
+  ADAPT_NONE = 0,
+  /**
+   * 1: The quality improves because the network bandwidth increases.
+   */
+  ADAPT_UP_BANDWIDTH = 1,
+  /**
+   * 2: The quality worsens because the network bandwidth decreases.
+   */
+  ADAPT_DOWN_BANDWIDTH = 2,
+};
+
+/**
+ * The latency level of an audience member in interactive live streaming. This enum takes effect only
+ * when the user role is set to `CLIENT_ROLE_AUDIENCE`.
+ */
+enum AUDIENCE_LATENCY_LEVEL_TYPE
+{
+  /**
+   * 1: Low latency.
+   */
+  AUDIENCE_LATENCY_LEVEL_LOW_LATENCY = 1,
+  /**
+   * 2: Ultra low latency.
+   */
+  AUDIENCE_LATENCY_LEVEL_ULTRA_LOW_LATENCY = 2,
+};
+
+/**
+ * The detailed options of a user.
+ */
+struct ClientRoleOptions
+{
+  /**
+   * The latency level of an audience member in interactive live streaming. See `AUDIENCE_LATENCY_LEVEL_TYPE`.
+   */
+  AUDIENCE_LATENCY_LEVEL_TYPE audienceLatencyLevel;
+
+  ClientRoleOptions()
+    : audienceLatencyLevel(AUDIENCE_LATENCY_LEVEL_ULTRA_LOW_LATENCY) {}
+};
+
+/**
+ * Quality of experience (QoE) of the local user when receiving a remote audio stream.
+ */
+enum EXPERIENCE_QUALITY_TYPE {
+  /** 0: QoE of the local user is good.  */
+  EXPERIENCE_QUALITY_GOOD = 0,
+  /** 1: QoE of the local user is poor.  */
+  EXPERIENCE_QUALITY_BAD = 1,
+};
+
+/**
+ * Reasons why the QoE of the local user when receiving a remote audio stream is poor.
+ */
+enum EXPERIENCE_POOR_REASON {
+  /**
+   * 0: No reason, indicating good QoE of the local user.
+   */
+  EXPERIENCE_REASON_NONE = 0,
+  /**
+   * 1: The remote user's network quality is poor.
+   */
+  REMOTE_NETWORK_QUALITY_POOR = 1,
+  /**
+   * 2: The local user's network quality is poor.
+   */
+  LOCAL_NETWORK_QUALITY_POOR = 2,
+  /**
+   * 4: The local user's Wi-Fi or mobile network signal is weak.
+   */
+  WIRELESS_SIGNAL_POOR = 4,
+  /**
+   * 8: The local user enables both Wi-Fi and bluetooth, and their signals interfere with each other.
+   * As a result, audio transmission quality is undermined.
+   */
+  WIFI_BLUETOOTH_COEXIST = 8,
+};
+
+/**
+ * Audio AINS mode
+ */
+enum AUDIO_AINS_MODE {
+    /**
+     * AINS mode with soft suppression level.
+     */
+    AINS_MODE_BALANCED = 0,
+    /**
+     * AINS mode with high suppression level.
+     */
+    AINS_MODE_AGGRESSIVE = 1,
+    /**
+     * AINS mode with high suppression level and ultra-low-latency
+     */
+    AINS_MODE_ULTRALOWLATENCY = 2
+};
+
+/**
+ * Audio profile types.
+ */
+enum AUDIO_PROFILE_TYPE {
+  /**
+   * 0: The default audio profile.
+   * - For the Communication profile:
+   *   - Windows: A sample rate of 16 kHz, audio encoding, mono, and a bitrate of up to 16 Kbps.
+   *   - Android/macOS/iOS: A sample rate of 32 kHz, audio encoding, mono, and a bitrate of up to 18 Kbps.
+   * of up to 16 Kbps.
+   * - For the Live-broadcast profile: A sample rate of 48 kHz, music encoding, mono, and a bitrate of up to 64 Kbps.
+   */
+  AUDIO_PROFILE_DEFAULT = 0,
+  /**
+   * 1: A sample rate of 32 kHz, audio encoding, mono, and a bitrate of up to 18 Kbps.
+   */
+  AUDIO_PROFILE_SPEECH_STANDARD = 1,
+  /**
+   * 2: A sample rate of 48 kHz, music encoding, mono, and a bitrate of up to 64 Kbps.
+   */
+  AUDIO_PROFILE_MUSIC_STANDARD = 2,
+  /**
+   * 3: A sample rate of 48 kHz, music encoding, stereo, and a bitrate of up to 80 Kbps.
+   *
+   * To implement stereo audio, you also need to call `setAdvancedAudioOptions` and set `audioProcessingChannels`
+   * to `AUDIO_PROCESSING_STEREO` in `AdvancedAudioOptions`.
+   */
+  AUDIO_PROFILE_MUSIC_STANDARD_STEREO = 3,
+  /**
+   * 4: A sample rate of 48 kHz, music encoding, mono, and a bitrate of up to 96 Kbps.
+   */
+  AUDIO_PROFILE_MUSIC_HIGH_QUALITY = 4,
+  /**
+   * 5: A sample rate of 48 kHz, music encoding, stereo, and a bitrate of up to 128 Kbps.
+   *
+   * To implement stereo audio, you also need to call `setAdvancedAudioOptions` and set `audioProcessingChannels`
+   * to `AUDIO_PROCESSING_STEREO` in `AdvancedAudioOptions`.
+   */
+  AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO = 5,
+  /**
+   * 6: A sample rate of 16 kHz, audio encoding, mono, and Acoustic Echo Cancellation (AES) enabled.
+   */
+  AUDIO_PROFILE_IOT = 6,
+  AUDIO_PROFILE_NUM = 7
+};
+
+/**
+ * The audio scenario.
+ */
+enum AUDIO_SCENARIO_TYPE {
+  /**
+   * 0: Automatic scenario, where the SDK chooses the appropriate audio quality according to the
+   * user role and audio route.
+   */
+  AUDIO_SCENARIO_DEFAULT = 0,
+  /**
+   * 3: (Recommended) The live gaming scenario, which needs to enable gaming
+   * audio effects in the speaker. Choose this scenario to achieve high-fidelity
+   * music playback.
+   */
+  AUDIO_SCENARIO_GAME_STREAMING = 3,
+  /**
+   * 5: The chatroom scenario, which needs to keep recording when setClientRole to audience.
+   * Normally, app developer can also use mute api to achieve the same result,
+   * and we implement this 'non-orthogonal' behavior only to make API backward compatible.
+   */
+  AUDIO_SCENARIO_CHATROOM = 5,
+  /**
+   * 7: Real-time chorus scenario, where users have good network conditions and require ultra-low latency.
+   */
+  AUDIO_SCENARIO_CHORUS = 7,
+  /**
+   * 8: Meeting
+   */
+  AUDIO_SCENARIO_MEETING = 8,
+  /**
+   * 9: The number of enumerations.
+   */
+  AUDIO_SCENARIO_NUM = 9,
+};
+
+/**
+ * The format of the video frame.
+ */
+struct VideoFormat {
+  OPTIONAL_ENUM_SIZE_T {
+    /** The maximum value (px) of the width. */
+    kMaxWidthInPixels = 3840,
+    /** The maximum value (px) of the height. */
+    kMaxHeightInPixels = 2160,
+    /** The maximum value (fps) of the frame rate. */
+    kMaxFps = 60,
+  };
+
+  /**
+   * The width (px) of the video.
+   */
+  int width;   // Number of pixels.
+  /**
+   * The height (px) of the video.
+   */
+  int height;  // Number of pixels.
+  /**
+   * The video frame rate (fps).
+   */
+  int fps;
+  VideoFormat() : width(FRAME_WIDTH_960), height(FRAME_HEIGHT_540), fps(FRAME_RATE_FPS_15) {}
+  VideoFormat(int w, int h, int f) : width(w), height(h), fps(f) {}
+
+  bool operator<(const VideoFormat& fmt) const {
+    if (height != fmt.height) {
+      return height < fmt.height;
+    } else if (width != fmt.width) {
+      return width < fmt.width;
+    } else {
+      return fps < fmt.fps;
+    }
+  }
+  bool operator==(const VideoFormat& fmt) const {
+    return width == fmt.width && height == fmt.height && fps == fmt.fps;
+  }
+  bool operator!=(const VideoFormat& fmt) const {
+    return !operator==(fmt);
+  }
+};
+
+/**
+ * Video content hints.
+ */
+enum VIDEO_CONTENT_HINT {
+  /**
+   * (Default) No content hint. In this case, the SDK balances smoothness with sharpness.
+   */
+  CONTENT_HINT_NONE,
+  /**
+   * Choose this option if you prefer smoothness or when
+   * you are sharing motion-intensive content such as a video clip, movie, or video game.
+   *
+   *
+   */
+  CONTENT_HINT_MOTION,
+  /**
+   * Choose this option if you prefer sharpness or when you are
+   * sharing montionless content such as a picture, PowerPoint slide, ot text.
+   *
+   */
+  CONTENT_HINT_DETAILS
+};
+/**
+ * The screen sharing scenario.
+ */
+enum SCREEN_SCENARIO_TYPE {
+  /**
+   * 1: Document. This scenario prioritizes the video quality of screen sharing and reduces the
+   * latency of the shared video for the receiver. If you share documents, slides, and tables,
+   * you can set this scenario.
+   */
+  SCREEN_SCENARIO_DOCUMENT = 1,
+  /**
+   * 2: Game. This scenario prioritizes the smoothness of screen sharing. If you share games, you
+   * can set this scenario.
+   */
+  SCREEN_SCENARIO_GAMING = 2,
+  /**
+   * 3: Video. This scenario prioritizes the smoothness of screen sharing. If you share movies or
+   * live videos, you can set this scenario.
+   */
+  SCREEN_SCENARIO_VIDEO = 3,
+  /**
+   * 4: Remote control. This scenario prioritizes the video quality of screen sharing and reduces
+   * the latency of the shared video for the receiver. If you share the device desktop being
+   * remotely controlled, you can set this scenario.
+   */
+  SCREEN_SCENARIO_RDC = 4,
+};
+
+
+/**
+ * The video application scenario type.
+ */
+enum VIDEO_APPLICATION_SCENARIO_TYPE {
+  /**
+   * 0: Default Scenario.
+   */
+  APPLICATION_SCENARIO_GENERAL = 0,
+  /**
+   * 1: Meeting Scenario. This scenario is the best QoE practice of meeting application.
+   */
+  APPLICATION_SCENARIO_MEETING = 1,
+};
+
+/**
+ * The video QoE preference type.
+ */
+enum VIDEO_QOE_PREFERENCE_TYPE {
+  /**
+   * 1: Default QoE type, balance the delay, picture quality and fluency.
+   */
+  VIDEO_QOE_PREFERENCE_BALANCE = 1,
+  /**
+   * 2: lower the e2e delay.
+   */
+  VIDEO_QOE_PREFERENCE_DELAY_FIRST = 2,
+  /**
+   * 3: picture quality.
+   */
+  VIDEO_QOE_PREFERENCE_PICTURE_QUALITY_FIRST = 3,
+  /**
+   * 4: more fluency.
+   */
+  VIDEO_QOE_PREFERENCE_FLUENCY_FIRST = 4,
+
+};
+
+/**
+ * The brightness level of the video image captured by the local camera.
+ */
+enum CAPTURE_BRIGHTNESS_LEVEL_TYPE {
+  /** -1: The SDK does not detect the brightness level of the video image.
+   * Wait a few seconds to get the brightness level from `CAPTURE_BRIGHTNESS_LEVEL_TYPE` in the next callback.
+   */
+  CAPTURE_BRIGHTNESS_LEVEL_INVALID = -1,
+  /** 0: The brightness level of the video image is normal.
+   */
+  CAPTURE_BRIGHTNESS_LEVEL_NORMAL = 0,
+  /** 1: The brightness level of the video image is too bright.
+   */
+  CAPTURE_BRIGHTNESS_LEVEL_BRIGHT = 1,
+  /** 2: The brightness level of the video image is too dark.
+   */
+  CAPTURE_BRIGHTNESS_LEVEL_DARK = 2,
+};
+
+enum CAMERA_STABILIZATION_MODE {
+  /** The camera stabilization mode is disabled. 
+  */
+  CAMERA_STABILIZATION_MODE_OFF = -1,
+  /** device choose stabilization mode automatically. 
+  */
+  CAMERA_STABILIZATION_MODE_AUTO = 0,
+  /** stabilization mode level 1. 
+  */
+  CAMERA_STABILIZATION_MODE_LEVEL_1 = 1,
+  /** stabilization mode level 2. 
+  */
+  CAMERA_STABILIZATION_MODE_LEVEL_2 = 2,
+  /** stabilization mode level 3. 
+  */
+  CAMERA_STABILIZATION_MODE_LEVEL_3 = 3,
+  /** The maximum level of the camera stabilization mode.
+   */
+  CAMERA_STABILIZATION_MODE_MAX_LEVEL = CAMERA_STABILIZATION_MODE_LEVEL_3,
+};
+
+/**
+ * Local audio states.
+ */
+enum LOCAL_AUDIO_STREAM_STATE {
+  /**
+   * 0: The local audio is in the initial state.
+   */
+  LOCAL_AUDIO_STREAM_STATE_STOPPED = 0,
+  /**
+   * 1: The capturing device starts successfully.
+   */
+  LOCAL_AUDIO_STREAM_STATE_RECORDING = 1,
+  /**
+   * 2: The first audio frame encodes successfully.
+   */
+  LOCAL_AUDIO_STREAM_STATE_ENCODING = 2,
+  /**
+   * 3: The local audio fails to start.
+   */
+  LOCAL_AUDIO_STREAM_STATE_FAILED = 3
+};
+
+/**
+ * Local audio state error codes.
+ */
+enum LOCAL_AUDIO_STREAM_REASON {
+  /**
+   * 0: The local audio is normal.
+   */
+  LOCAL_AUDIO_STREAM_REASON_OK = 0,
+  /**
+   * 1: No specified reason for the local audio failure. Remind your users to try to rejoin the channel.
+   */
+  LOCAL_AUDIO_STREAM_REASON_FAILURE = 1,
+  /**
+   * 2: No permission to use the local audio device. Remind your users to grant permission.
+   */
+  LOCAL_AUDIO_STREAM_REASON_DEVICE_NO_PERMISSION = 2,
+  /**
+   * 3: (Android and iOS only) The local audio capture device is used. Remind your users to check
+   * whether another application occupies the microphone. Local audio capture automatically resume
+   * after the microphone is idle for about five seconds. You can also try to rejoin the channel
+   * after the microphone is idle.
+   */
+  LOCAL_AUDIO_STREAM_REASON_DEVICE_BUSY = 3,
+  /**
+   * 4: The local audio capture failed.
+   */
+  LOCAL_AUDIO_STREAM_REASON_RECORD_FAILURE = 4,
+  /**
+   * 5: The local audio encoding failed.
+   */
+  LOCAL_AUDIO_STREAM_REASON_ENCODE_FAILURE = 5,
+  /** 6: The SDK cannot find the local audio recording device.
+   */
+  LOCAL_AUDIO_STREAM_REASON_NO_RECORDING_DEVICE = 6,
+  /** 7: The SDK cannot find the local audio playback device.
+   */
+  LOCAL_AUDIO_STREAM_REASON_NO_PLAYOUT_DEVICE = 7,
+  /**
+   * 8: The local audio capturing is interrupted by the system call.
+   */
+  LOCAL_AUDIO_STREAM_REASON_INTERRUPTED = 8,
+  /** 9: An invalid audio capture device ID.
+   */
+  LOCAL_AUDIO_STREAM_REASON_RECORD_INVALID_ID = 9,
+  /** 10: An invalid audio playback device ID.
+   */
+  LOCAL_AUDIO_STREAM_REASON_PLAYOUT_INVALID_ID = 10,
+};
+
+/** Local video state types.
+ */
+enum LOCAL_VIDEO_STREAM_STATE {
+  /**
+   * 0: The local video is in the initial state.
+   */
+  LOCAL_VIDEO_STREAM_STATE_STOPPED = 0,
+  /**
+   * 1: The local video capturing device starts successfully. The SDK also reports this state when
+   * you call `startScreenCaptureByWindowId` to share a maximized window.
+   */
+  LOCAL_VIDEO_STREAM_STATE_CAPTURING = 1,
+  /**
+   * 2: The first video frame is successfully encoded.
+   */
+  LOCAL_VIDEO_STREAM_STATE_ENCODING = 2,
+  /**
+   * 3: Fails to start the local video.
+   */
+  LOCAL_VIDEO_STREAM_STATE_FAILED = 3
+};
+
+/**
+ * Local video state error codes.
+ */
+enum LOCAL_VIDEO_STREAM_REASON {
+  /**
+   * 0: The local video is normal.
+   */
+  LOCAL_VIDEO_STREAM_REASON_OK = 0,
+  /**
+   * 1: No specified reason for the local video failure.
+   */
+  LOCAL_VIDEO_STREAM_REASON_FAILURE = 1,
+  /**
+   * 2: No permission to use the local video capturing device. Remind the user to grant permission
+   * and rejoin the channel.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_NO_PERMISSION = 2,
+  /**
+   * 3: The local video capturing device is in use. Remind the user to check whether another
+   * application occupies the camera.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_BUSY = 3,
+  /**
+   * 4: The local video capture fails. Remind the user to check whether the video capture device
+   * is working properly or the camera is occupied by another application, and then to rejoin the
+   * channel.
+   */
+  LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE = 4,
+  /**
+   * 5: The local video encoder is not supported.
+   */
+  LOCAL_VIDEO_STREAM_REASON_CODEC_NOT_SUPPORT = 5,
+  /**
+   * 6: (iOS only) The app is in the background. Remind the user that video capture cannot be
+   * performed normally when the app is in the background.
+   */
+  LOCAL_VIDEO_STREAM_REASON_CAPTURE_INBACKGROUND = 6,
+  /**
+   * 7: (iOS only) The current application window is running in Slide Over, Split View, or Picture
+   * in Picture mode, and another app is occupying the camera. Remind the user that the application
+   * cannot capture video properly when the app is running in Slide Over, Split View, or Picture in
+   * Picture mode and another app is occupying the camera.
+   */
+  LOCAL_VIDEO_STREAM_REASON_CAPTURE_MULTIPLE_FOREGROUND_APPS = 7,
+  /**
+   * 8: Fails to find a local video capture device. Remind the user to check whether the camera is
+   * connected to the device properly or the camera is working properly, and then to rejoin the
+   * channel.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_NOT_FOUND = 8,
+  /**
+   *  9: (macOS only) The video capture device currently in use is disconnected (such as being
+   * unplugged).
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_DISCONNECTED = 9,
+  /**
+   * 10: (macOS and Windows only) The SDK cannot find the video device in the video device list.
+   * Check whether the ID of the video device is valid.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_INVALID_ID = 10,
+  /**
+   * 14: (Android only) Video capture was interrupted, possibly due to the camera being occupied
+   * or some policy reasons such as background termination.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_INTERRUPT = 14,
+  /**
+   * 15: (Android only) The device may need to be shut down and restarted to restore camera function, 
+   * or there may be a persistent hardware problem.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_FATAL_ERROR = 15,
+  /**
+   * 101: The current video capture device is unavailable due to excessive system pressure.
+   */
+  LOCAL_VIDEO_STREAM_REASON_DEVICE_SYSTEM_PRESSURE = 101,
+  /**
+   * 11: (macOS only) The shared window is minimized when you call `startScreenCaptureByWindowId`
+   * to share a window. The SDK cannot share a minimized window. You can cancel the minimization
+   * of this window at the application layer, for example by maximizing this window.
+   */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_MINIMIZED = 11,
+  /**
+   * 12: (macOS and Windows only) The error code indicates that a window shared by the window ID
+   * has been closed or a full-screen window shared by the window ID has exited full-screen mode.
+   * After exiting full-screen mode, remote users cannot see the shared window. To prevent remote
+   * users from seeing a black screen, Agora recommends that you immediately stop screen sharing.
+   *
+   * Common scenarios for reporting this error code:
+   * - When the local user closes the shared window, the SDK reports this error code.
+   * - The local user shows some slides in full-screen mode first, and then shares the windows of
+   * the slides. After the user exits full-screen mode, the SDK reports this error code.
+   * - The local user watches a web video or reads a web document in full-screen mode first, and
+   * then shares the window of the web video or document. After the user exits full-screen mode,
+   * the SDK reports this error code.
+   */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_CLOSED = 12,
+  /** 13: The local screen capture window is occluded. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_OCCLUDED = 13,
+  /** 20: The local screen capture window is not supported. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_NOT_SUPPORTED = 20,
+  /** 21: The screen capture fails. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_FAILURE = 21,
+  /** 22: No permision to capture screen. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_NO_PERMISSION = 22,
+  /**
+   * 24: (Windows Only) An unexpected error (possibly due to window block failure) occurs during the screen 
+   * sharing process, resulting in performance degradation. However, the screen sharing process itself is 
+   * functioning normally.
+   */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_AUTO_FALLBACK = 24,
+  /** 25: (Windows only) The local screen capture window is currently hidden and not visible on the desktop. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_HIDDEN = 25,
+  /** 26: (Windows only) The local screen capture window is recovered from its hidden state. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_HIDDEN = 26,
+  /** 27: (Windows and macOS only) The window is recovered from miniminzed */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_WINDOW_RECOVER_FROM_MINIMIZED = 27,
+    /** 
+   * 28: The screen capture paused.
+   * 
+   * Common scenarios for reporting this error code:
+   * - When the desktop switch to the secure desktop such as UAC dialog or the Winlogon desktop on
+   * Windows platform, the SDK reports this error code.
+   */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_PAUSED = 28,
+  /** 29: The screen capture is resumed. */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_RESUMED = 29,
+  /** 30: The shared display has been disconnected */
+  LOCAL_VIDEO_STREAM_REASON_SCREEN_CAPTURE_DISPLAY_DISCONNECTED = 30,
+
+};
+
+/**
+ * Remote audio states.
+ */
+enum REMOTE_AUDIO_STATE
+{
+  /**
+   * 0: The remote audio is in the default state. The SDK reports this state in the case of
+   * `REMOTE_AUDIO_REASON_LOCAL_MUTED(3)`, `REMOTE_AUDIO_REASON_REMOTE_MUTED(5)`, or
+   * `REMOTE_AUDIO_REASON_REMOTE_OFFLINE(7)`.
+   */
+  REMOTE_AUDIO_STATE_STOPPED = 0,  // Default state, audio is started or remote user disabled/muted audio stream
+  /**
+   * 1: The first remote audio packet is received.
+   */
+  REMOTE_AUDIO_STATE_STARTING = 1,  // The first audio frame packet has been received
+  /**
+   * 2: The remote audio stream is decoded and plays normally. The SDK reports this state in the case of
+   * `REMOTE_AUDIO_REASON_NETWORK_RECOVERY(2)`, `REMOTE_AUDIO_REASON_LOCAL_UNMUTED(4)`, or
+   * `REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6)`.
+   */
+  REMOTE_AUDIO_STATE_DECODING = 2,  // The first remote audio frame has been decoded or fronzen state ends
+  /**
+   * 3: The remote audio is frozen. The SDK reports this state in the case of
+   * `REMOTE_AUDIO_REASON_NETWORK_CONGESTION(1)`.
+   */
+  REMOTE_AUDIO_STATE_FROZEN = 3,    // Remote audio is frozen, probably due to network issue
+  /**
+   * 4: The remote audio fails to start. The SDK reports this state in the case of
+   * `REMOTE_AUDIO_REASON_INTERNAL(0)`.
+   */
+  REMOTE_AUDIO_STATE_FAILED = 4,    // Remote audio play failed
+};
+
+/**
+ * Reasons for the remote audio state change.
+ */
+enum REMOTE_AUDIO_STATE_REASON
+{
+  /**
+   * 0: The SDK reports this reason when the video state changes.
+   */
+  REMOTE_AUDIO_REASON_INTERNAL = 0,
+  /**
+   * 1: Network congestion.
+   */
+  REMOTE_AUDIO_REASON_NETWORK_CONGESTION = 1,
+  /**
+   * 2: Network recovery.
+   */
+  REMOTE_AUDIO_REASON_NETWORK_RECOVERY = 2,
+  /**
+   * 3: The local user stops receiving the remote audio stream or
+   * disables the audio module.
+   */
+  REMOTE_AUDIO_REASON_LOCAL_MUTED = 3,
+  /**
+   * 4: The local user resumes receiving the remote audio stream or
+   * enables the audio module.
+   */
+  REMOTE_AUDIO_REASON_LOCAL_UNMUTED = 4,
+  /**
+   * 5: The remote user stops sending the audio stream or disables the
+   * audio module.
+   */
+  REMOTE_AUDIO_REASON_REMOTE_MUTED = 5,
+  /**
+   * 6: The remote user resumes sending the audio stream or enables the
+   * audio module.
+   */
+  REMOTE_AUDIO_REASON_REMOTE_UNMUTED = 6,
+  /**
+   * 7: The remote user leaves the channel.
+   */
+  REMOTE_AUDIO_REASON_REMOTE_OFFLINE = 7,
+  /**
+   * 8: The local user does not receive any audio packet from remote user.
+   */
+  REMOTE_AUDIO_REASON_NO_PACKET_RECEIVE = 8,
+  /**
+   * 9: The local user receives remote audio packet but fails to play.
+   */
+  REMOTE_AUDIO_REASON_LOCAL_PLAY_FAILED = 9,
+};
+
+/**
+ * The state of the remote video.
+ */
+enum REMOTE_VIDEO_STATE {
+  /**
+   * 0: The remote video is in the default state. The SDK reports this state in the case of
+   * `REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED (3)`, `REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED (5)`,
+   * `REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE (7)`, or `REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK (8)`.
+   */
+  REMOTE_VIDEO_STATE_STOPPED = 0,
+  /**
+   * 1: The first remote video packet is received.
+   */
+  REMOTE_VIDEO_STATE_STARTING = 1,
+  /**
+   * 2: The remote video stream is decoded and plays normally. The SDK reports this state in the case of
+   * `REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY (2)`, `REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED (4)`,
+   * `REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED (6)`, or `REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY (9)`.
+   */
+  REMOTE_VIDEO_STATE_DECODING = 2,
+  /** 3: The remote video is frozen, probably due to
+   * #REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION (1).
+   */
+  REMOTE_VIDEO_STATE_FROZEN = 3,
+  /** 4: The remote video fails to start. The SDK reports this state in the case of
+   * `REMOTE_VIDEO_STATE_REASON_INTERNAL (0)`.
+   */
+  REMOTE_VIDEO_STATE_FAILED = 4,
+};
+/**
+ * The reason for the remote video state change.
+ */
+enum REMOTE_VIDEO_STATE_REASON {
+  /**
+  * 0: The SDK reports this reason when the video state changes.
+  */
+  REMOTE_VIDEO_STATE_REASON_INTERNAL = 0,
+  /**
+  * 1: Network congestion.
+  */
+  REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION = 1,
+  /**
+  * 2: Network recovery.
+  */
+  REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY = 2,
+  /**
+  * 3: The local user stops receiving the remote video stream or disables the video module.
+  */
+  REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED = 3,
+  /**
+  * 4: The local user resumes receiving the remote video stream or enables the video module.
+  */
+  REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED = 4,
+  /**
+  * 5: The remote user stops sending the video stream or disables the video module.
+  */
+  REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED = 5,
+  /**
+  * 6: The remote user resumes sending the video stream or enables the video module.
+  */
+  REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED = 6,
+  /**
+  * 7: The remote user leaves the channel.
+  */
+  REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE = 7,
+  /** 8: The remote audio-and-video stream falls back to the audio-only stream
+   * due to poor network conditions.
+   */
+  REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK = 8,
+  /** 9: The remote audio-only stream switches back to the audio-and-video
+   * stream after the network conditions improve.
+   */
+  REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY = 9,
+  /** (Internal use only) 10: The remote video stream type change to low stream type
+   */
+  REMOTE_VIDEO_STATE_REASON_VIDEO_STREAM_TYPE_CHANGE_TO_LOW = 10,
+  /** (Internal use only)  11: The remote video stream type change to high stream type
+   */
+  REMOTE_VIDEO_STATE_REASON_VIDEO_STREAM_TYPE_CHANGE_TO_HIGH = 11,
+    /** (iOS only) 12: The app of the remote user is in background.
+   */
+  REMOTE_VIDEO_STATE_REASON_SDK_IN_BACKGROUND = 12,
+
+  /** 13: The remote video stream is not supported by the decoder
+   */
+  REMOTE_VIDEO_STATE_REASON_CODEC_NOT_SUPPORT = 13,
+
+};
+
+/**
+ * The remote user state information.
+ */
+enum REMOTE_USER_STATE {
+  /**
+   * The remote user has muted the audio.
+   */
+  USER_STATE_MUTE_AUDIO = (1 << 0),
+  /**
+   * The remote user has muted the video.
+   */
+  USER_STATE_MUTE_VIDEO = (1 << 1),
+  /**
+   * The remote user has enabled the video, which includes video capturing and encoding.
+   */
+  USER_STATE_ENABLE_VIDEO = (1 << 4),
+  /**
+   * The remote user has enabled the local video capturing.
+   */
+  USER_STATE_ENABLE_LOCAL_VIDEO = (1 << 8),
+};
+
+/**
+ * The definition of the VideoTrackInfo struct, which contains information of
+ * the video track.
+ */
+struct VideoTrackInfo {
+  VideoTrackInfo()
+  : isLocal(false), ownerUid(0), trackId(0), channelId(OPTIONAL_NULLPTR)
+  , codecType(VIDEO_CODEC_H265)
+  , encodedFrameOnly(false), sourceType(VIDEO_SOURCE_CAMERA_PRIMARY)
+  , observationPosition(agora::media::base::POSITION_POST_CAPTURER) {}
+  /**
+   * Whether the video track is local or remote.
+   * - true: The video track is local.
+   * - false: The video track is remote.
+   */
+  bool isLocal;
+  /**
+   * ID of the user who publishes the video track.
+   */
+  uid_t ownerUid;
+  /**
+   * ID of the video track.
+   */
+  track_id_t trackId;
+  /**
+   * The channel ID of the video track.
+   */
+  const char* channelId;
+  /**
+   * The video codec type: #VIDEO_CODEC_TYPE.
+   */
+  VIDEO_CODEC_TYPE codecType;
+  /**
+   * Whether the video track contains encoded video frame only.
+   * - true: The video track contains encoded video frame only.
+   * - false: The video track does not contain encoded video frame only.
+   */
+  bool encodedFrameOnly;
+  /**
+   * The video source type: #VIDEO_SOURCE_TYPE
+   */
+  VIDEO_SOURCE_TYPE sourceType;
+  /**
+   * the frame position for the video observer: #VIDEO_MODULE_POSITION
+   */
+  uint32_t observationPosition;
+};
+
+/**
+ * The downscale level of the remote video stream . The higher the downscale level, the more the video downscales.
+ */
+enum REMOTE_VIDEO_DOWNSCALE_LEVEL {
+  /**
+   * No downscale.
+   */
+  REMOTE_VIDEO_DOWNSCALE_LEVEL_NONE,
+  /**
+   * Downscale level 1.
+   */
+  REMOTE_VIDEO_DOWNSCALE_LEVEL_1,
+  /**
+   * Downscale level 2.
+   */
+  REMOTE_VIDEO_DOWNSCALE_LEVEL_2,
+  /**
+   * Downscale level 3.
+   */
+  REMOTE_VIDEO_DOWNSCALE_LEVEL_3,
+  /**
+   * Downscale level 4.
+   */
+  REMOTE_VIDEO_DOWNSCALE_LEVEL_4,
+};
+
+/**
+ * The volume information of users.
+ */
+struct AudioVolumeInfo {
+  /**
+   * User ID of the speaker.
+   * - In the local user's callback, `uid` = 0.
+   * - In the remote users' callback, `uid` is the user ID of a remote user whose instantaneous
+   * volume is one of the three highest.
+   */
+  uid_t uid;
+  /**
+   * The volume of the user. The value ranges between 0 (the lowest volume) and 255 (the highest
+   * volume). If the user calls `startAudioMixing`, the value of volume is the volume after audio
+   * mixing.
+   */
+  unsigned int volume;  // [0,255]
+  /**
+   * Voice activity status of the local user.
+   * - 0: The local user is not speaking.
+   * - 1: The local user is speaking.
+   * @note
+   * - The `vad` parameter does not report the voice activity status of remote users. In a remote
+   * user's callback, the value of `vad` is always 1.
+   * - To use this parameter, you must set `reportVad` to true when calling `enableAudioVolumeIndication`.
+   */
+  unsigned int vad;
+  /**
+   * The voice pitch (Hz) of the local user. The value ranges between 0.0 and 4000.0.
+   * @note The `voicePitch` parameter does not report the voice pitch of remote users. In the
+   * remote users' callback, the value of `voicePitch` is always 0.0.
+   */
+  double voicePitch;
+
+  AudioVolumeInfo() : uid(0), volume(0), vad(0), voicePitch(0.0) {}
+};
+
+/**
+ * The audio device information.
+ */
+struct DeviceInfo {
+  /*
+   * Whether the audio device supports ultra-low-latency capture and playback:
+   * - `true`: The device supports ultra-low-latency capture and playback.
+   * - `false`: The device does not support ultra-low-latency capture and playback.
+   */
+  bool isLowLatencyAudioSupported;
+
+  DeviceInfo() : isLowLatencyAudioSupported(false) {}
+};
+
+/**
+ * The definition of the IPacketObserver struct.
+ */
+class IPacketObserver {
+ public:
+  virtual ~IPacketObserver() {}
+  /**
+   * The definition of the Packet struct.
+   */
+  struct Packet {
+    /**
+     * The buffer address of the sent or received data.
+     * @note Agora recommends setting `buffer` to a value larger than 2048 bytes. Otherwise, you
+     * may encounter undefined behaviors (such as crashes).
+     */
+    const unsigned char* buffer;
+    /**
+     * The buffer size of the sent or received data.
+     */
+    unsigned int size;
+
+    Packet() : buffer(OPTIONAL_NULLPTR), size(0) {}
+  };
+  /**
+   * Occurs when the SDK is ready to send the audio packet.
+   * @param packet The audio packet to be sent: Packet.
+   * @return Whether to send the audio packet:
+   * - true: Send the packet.
+   * - false: Do not send the packet, in which case the audio packet will be discarded.
+   */
+  virtual bool onSendAudioPacket(Packet& packet) = 0;
+  /**
+   * Occurs when the SDK is ready to send the video packet.
+   * @param packet The video packet to be sent: Packet.
+   * @return Whether to send the video packet:
+   * - true: Send the packet.
+   * - false: Do not send the packet, in which case the audio packet will be discarded.
+   */
+  virtual bool onSendVideoPacket(Packet& packet) = 0;
+  /**
+   * Occurs when the audio packet is received.
+   * @param packet The received audio packet: Packet.
+   * @return Whether to process the audio packet:
+   * - true: Process the packet.
+   * - false: Do not process the packet, in which case the audio packet will be discarded.
+   */
+  virtual bool onReceiveAudioPacket(Packet& packet) = 0;
+  /**
+   * Occurs when the video packet is received.
+   * @param packet The received video packet: Packet.
+   * @return Whether to process the audio packet:
+   * - true: Process the packet.
+   * - false: Do not process the packet, in which case the video packet will be discarded.
+   */
+  virtual bool onReceiveVideoPacket(Packet& packet) = 0;
+};
+
+/**
+ * Audio sample rate types.
+ */
+enum AUDIO_SAMPLE_RATE_TYPE {
+  /**
+   * 32000: 32 KHz.
+   */
+  AUDIO_SAMPLE_RATE_32000 = 32000,
+  /**
+   * 44100: 44.1 KHz.
+   */
+  AUDIO_SAMPLE_RATE_44100 = 44100,
+  /**
+   * 48000: 48 KHz.
+   */
+  AUDIO_SAMPLE_RATE_48000 = 48000,
+};
+/**
+ * The codec type of the output video.
+ */
+enum VIDEO_CODEC_TYPE_FOR_STREAM {
+  /**
+   * 1: H.264.
+   */
+  VIDEO_CODEC_H264_FOR_STREAM = 1,
+  /**
+   * 2: H.265.
+   */
+  VIDEO_CODEC_H265_FOR_STREAM = 2,
+};
+
+/**
+ * Video codec profile types.
+ */
+enum VIDEO_CODEC_PROFILE_TYPE {
+  /**
+   * 66: Baseline video codec profile. Generally used in video calls on mobile phones.
+   */
+  VIDEO_CODEC_PROFILE_BASELINE = 66,
+  /**
+   * 77: Main video codec profile. Generally used in mainstream electronics, such as MP4 players, portable video players, PSP, and iPads.
+   */
+  VIDEO_CODEC_PROFILE_MAIN = 77,
+  /**
+   * 100: High video codec profile. Generally used in high-resolution broadcasts or television.
+   */
+  VIDEO_CODEC_PROFILE_HIGH = 100,
+};
+
+
+/**
+ * Self-defined audio codec profile.
+ */
+enum AUDIO_CODEC_PROFILE_TYPE {
+  /**
+   * 0: LC-AAC.
+   */
+  AUDIO_CODEC_PROFILE_LC_AAC = 0,
+  /**
+   * 1: HE-AAC.
+   */
+  AUDIO_CODEC_PROFILE_HE_AAC = 1,
+  /**
+   *  2: HE-AAC v2.
+   */
+  AUDIO_CODEC_PROFILE_HE_AAC_V2 = 2,
+};
+
+/**
+ * Local audio statistics.
+ */
+struct LocalAudioStats
+{
+  /**
+   * The number of audio channels.
+   */
+  int numChannels;
+  /**
+   * The sampling rate (Hz) of sending the local user's audio stream.
+   */
+  int sentSampleRate;
+  /**
+   * The average bitrate (Kbps) of sending the local user's audio stream.
+   */
+  int sentBitrate;
+  /**
+   * The internal payload codec.
+   */
+  int internalCodec;
+  /**
+   * The packet loss rate (%) from the local client to the Agora server before applying the anti-packet loss strategies.
+   */
+  unsigned short txPacketLossRate;
+  /**
+   * The audio delay of the device, contains record and playout delay
+   */
+  int audioDeviceDelay;
+  /**
+   * The playout delay of the device
+   */
+  int audioPlayoutDelay;
+  /**
+   * The signal delay estimated from audio in-ear monitoring (ms).
+   */
+  int earMonitorDelay;
+  /**
+   * The signal delay estimated during the AEC process from nearin and farin (ms).
+   */
+  int aecEstimatedDelay;
+};
+
+
+/**
+ * States of the Media Push.
+ */
+enum RTMP_STREAM_PUBLISH_STATE {
+  /**
+   * 0: The Media Push has not started or has ended. This state is also triggered after you remove a RTMP or RTMPS stream from the CDN by calling `removePublishStreamUrl`.
+   */
+  RTMP_STREAM_PUBLISH_STATE_IDLE = 0,
+  /**
+   * 1: The SDK is connecting to Agora's streaming server and the CDN server. This state is triggered after you call the `addPublishStreamUrl` method.
+   */
+  RTMP_STREAM_PUBLISH_STATE_CONNECTING = 1,
+  /**
+   * 2: The RTMP or RTMPS streaming publishes. The SDK successfully publishes the RTMP or RTMPS streaming and returns this state.
+   */
+  RTMP_STREAM_PUBLISH_STATE_RUNNING = 2,
+  /**
+   * 3: The RTMP or RTMPS streaming is recovering. When exceptions occur to the CDN, or the streaming is interrupted, the SDK tries to resume RTMP or RTMPS streaming and returns this state.
+   * - If the SDK successfully resumes the streaming, #RTMP_STREAM_PUBLISH_STATE_RUNNING (2) returns.
+   * - If the streaming does not resume within 60 seconds or server errors occur, #RTMP_STREAM_PUBLISH_STATE_FAILURE (4) returns. You can also reconnect to the server by calling the `removePublishStreamUrl` and `addPublishStreamUrl` methods.
+   */
+  RTMP_STREAM_PUBLISH_STATE_RECOVERING = 3,
+  /**
+   * 4: The RTMP or RTMPS streaming fails. See the `errCode` parameter for the detailed error information. You can also call the `addPublishStreamUrl` method to publish the RTMP or RTMPS streaming again.
+   */
+  RTMP_STREAM_PUBLISH_STATE_FAILURE = 4,
+  /**
+   * 5: The SDK is disconnecting to Agora's streaming server and the CDN server. This state is triggered after you call the `removePublishStreamUrl` method.
+   */
+  RTMP_STREAM_PUBLISH_STATE_DISCONNECTING = 5,
+};
+
+/**
+ * Error codes of the RTMP or RTMPS streaming.
+ */
+enum RTMP_STREAM_PUBLISH_REASON {
+  /**
+   * 0: The RTMP or RTMPS streaming publishes successfully.
+   */
+  RTMP_STREAM_PUBLISH_REASON_OK = 0,
+  /**
+   * 1: Invalid argument used. If, for example, you do not call the `setLiveTranscoding` method to configure the LiveTranscoding parameters before calling the addPublishStreamUrl method,
+   * the SDK returns this error. Check whether you set the parameters in the `setLiveTranscoding` method properly.
+   */
+  RTMP_STREAM_PUBLISH_REASON_INVALID_ARGUMENT = 1,
+  /**
+   * 2: The RTMP or RTMPS streaming is encrypted and cannot be published.
+   */
+  RTMP_STREAM_PUBLISH_REASON_ENCRYPTED_STREAM_NOT_ALLOWED = 2,
+  /**
+   * 3: Timeout for the RTMP or RTMPS streaming. Call the `addPublishStreamUrl` method to publish the streaming again.
+   */
+  RTMP_STREAM_PUBLISH_REASON_CONNECTION_TIMEOUT = 3,
+  /**
+   * 4: An error occurs in Agora's streaming server. Call the `addPublishStreamUrl` method to publish the streaming again.
+   */
+  RTMP_STREAM_PUBLISH_REASON_INTERNAL_SERVER_ERROR = 4,
+  /**
+   * 5: An error occurs in the CDN server.
+   */
+  RTMP_STREAM_PUBLISH_REASON_RTMP_SERVER_ERROR = 5,
+  /**
+   * 6: The RTMP or RTMPS streaming publishes too frequently.
+   */
+  RTMP_STREAM_PUBLISH_REASON_TOO_OFTEN = 6,
+  /**
+   * 7: The host publishes more than 10 URLs. Delete the unnecessary URLs before adding new ones.
+   */
+  RTMP_STREAM_PUBLISH_REASON_REACH_LIMIT = 7,
+  /**
+   * 8: The host manipulates other hosts' URLs. Check your app logic.
+   */
+  RTMP_STREAM_PUBLISH_REASON_NOT_AUTHORIZED = 8,
+  /**
+   * 9: Agora's server fails to find the RTMP or RTMPS streaming.
+   */
+  RTMP_STREAM_PUBLISH_REASON_STREAM_NOT_FOUND = 9,
+  /**
+   * 10: The format of the RTMP or RTMPS streaming URL is not supported. Check whether the URL format is correct.
+   */
+  RTMP_STREAM_PUBLISH_REASON_FORMAT_NOT_SUPPORTED = 10,
+  /**
+   * 11: The user role is not host, so the user cannot use the CDN live streaming function. Check your application code logic.
+   */
+  RTMP_STREAM_PUBLISH_REASON_NOT_BROADCASTER = 11,  // Note: match to ERR_PUBLISH_STREAM_NOT_BROADCASTER in AgoraBase.h
+  /**
+   * 13: The `updateRtmpTranscoding` or `setLiveTranscoding` method is called to update the transcoding configuration in a scenario where there is streaming without transcoding. Check your application code logic.
+   */
+  RTMP_STREAM_PUBLISH_REASON_TRANSCODING_NO_MIX_STREAM = 13,  // Note: match to ERR_PUBLISH_STREAM_TRANSCODING_NO_MIX_STREAM in AgoraBase.h
+  /**
+   * 14: Errors occurred in the host's network.
+   */
+  RTMP_STREAM_PUBLISH_REASON_NET_DOWN = 14,  // Note: match to ERR_NET_DOWN in AgoraBase.h
+  /**
+   * 15: Your App ID does not have permission to use the CDN live streaming function.
+   */
+  RTMP_STREAM_PUBLISH_REASON_INVALID_APPID = 15,  // Note: match to ERR_PUBLISH_STREAM_APPID_INVALID in AgoraBase.h
+  /** invalid privilege. */
+  RTMP_STREAM_PUBLISH_REASON_INVALID_PRIVILEGE = 16,
+  /**
+   * 100: The streaming has been stopped normally. After you call `removePublishStreamUrl` to stop streaming, the SDK returns this value.
+   */
+  RTMP_STREAM_UNPUBLISH_REASON_OK = 100,
+};
+
+/** Events during the RTMP or RTMPS streaming. */
+enum RTMP_STREAMING_EVENT {
+  /**
+   * 1: An error occurs when you add a background image or a watermark image to the RTMP or RTMPS stream.
+   */
+  RTMP_STREAMING_EVENT_FAILED_LOAD_IMAGE = 1,
+  /**
+   * 2: The streaming URL is already being used for CDN live streaming. If you want to start new streaming, use a new streaming URL.
+   */
+  RTMP_STREAMING_EVENT_URL_ALREADY_IN_USE = 2,
+  /**
+   * 3: The feature is not supported.
+   */
+  RTMP_STREAMING_EVENT_ADVANCED_FEATURE_NOT_SUPPORT = 3,
+  /**
+   * 4: Client request too frequently.
+   */
+  RTMP_STREAMING_EVENT_REQUEST_TOO_OFTEN = 4,
+};
+
+/**
+ * Image properties.
+ */
+typedef struct RtcImage {
+  /**
+   *The HTTP/HTTPS URL address of the image in the live video. The maximum length of this parameter is 1024 bytes.
+   */
+  const char* url;
+  /**
+   * The x coordinate (pixel) of the image on the video frame (taking the upper left corner of the video frame as the origin).
+   */
+  int x;
+  /**
+   * The y coordinate (pixel) of the image on the video frame (taking the upper left corner of the video frame as the origin).
+   */
+  int y;
+  /**
+   * The width (pixel) of the image on the video frame.
+   */
+  int width;
+  /**
+   * The height (pixel) of the image on the video frame.
+   */
+  int height;
+  /**
+   * The layer index of the watermark or background image. When you use the watermark array to add
+   * a watermark or multiple watermarks, you must pass a value to `zOrder` in the range [1,255];
+   * otherwise, the SDK reports an error. In other cases, zOrder can optionally be passed in the
+   * range [0,255], with 0 being the default value. 0 means the bottom layer and 255 means the top
+   * layer.
+   */
+  int zOrder;
+  /** The transparency level of the image. The value ranges between 0.0 and 1.0:
+   *
+   * - 0.0: Completely transparent.
+   * - 1.0: (Default) Opaque.
+   */
+  double alpha;
+
+  RtcImage() : url(OPTIONAL_NULLPTR), x(0), y(0), width(0), height(0), zOrder(0), alpha(1.0) {}
+} RtcImage;
+/**
+ * The configuration for advanced features of the RTMP or RTMPS streaming with transcoding.
+ *
+ * If you want to enable the advanced features of streaming with transcoding, contact support@agora.io.
+ */
+struct LiveStreamAdvancedFeature {
+  LiveStreamAdvancedFeature() : featureName(OPTIONAL_NULLPTR), opened(false) {}
+  LiveStreamAdvancedFeature(const char* feat_name, bool open) : featureName(feat_name), opened(open) {}
+  /** The advanced feature for high-quality video with a lower bitrate. */
+  // static const char* LBHQ = "lbhq";
+  /** The advanced feature for the optimized video encoder. */
+  // static const char* VEO = "veo";
+
+  /**
+   * The feature names, including LBHQ (high-quality video with a lower bitrate) and VEO (optimized video encoder).
+   */
+  const char* featureName;
+
+  /**
+   * Whether to enable the advanced features of streaming with transcoding:
+   * - `true`: Enable the advanced feature.
+   * - `false`: (Default) Disable the advanced feature.
+   */
+  bool opened;
+} ;
+
+/**
+ * Connection state types.
+ */
+enum CONNECTION_STATE_TYPE
+{
+  /**
+   * 1: The SDK is disconnected from the Agora edge server. The state indicates the SDK is in one of the following phases:
+   * - The initial state before calling the `joinChannel` method.
+   * - The app calls the `leaveChannel` method.
+   */
+  CONNECTION_STATE_DISCONNECTED = 1,
+  /**
+   * 2: The SDK is connecting to the Agora edge server. This state indicates that the SDK is
+   * establishing a connection with the specified channel after the app calls `joinChannel`.
+   * - If the SDK successfully joins the channel, it triggers the `onConnectionStateChanged`
+   * callback and the connection state switches to `CONNECTION_STATE_CONNECTED`.
+   * - After the connection is established, the SDK also initializes the media and triggers
+   * `onJoinChannelSuccess` when everything is ready.
+   */
+  CONNECTION_STATE_CONNECTING = 2,
+  /**
+   * 3: The SDK is connected to the Agora edge server. This state also indicates that the user
+   * has joined a channel and can now publish or subscribe to a media stream in the channel.
+   * If the connection to the Agora edge server is lost because, for example, the network is down
+   * or switched, the SDK automatically tries to reconnect and triggers `onConnectionStateChanged`
+   * that indicates the connection state switches to `CONNECTION_STATE_RECONNECTING`.
+   */
+  CONNECTION_STATE_CONNECTED = 3,
+  /**
+   * 4: The SDK keeps reconnecting to the Agora edge server. The SDK keeps rejoining the channel
+   * after being disconnected from a joined channel because of network issues.
+   * - If the SDK cannot rejoin the channel within 10 seconds, it triggers `onConnectionLost`,
+   * stays in the `CONNECTION_STATE_RECONNECTING` state, and keeps rejoining the channel.
+   * - If the SDK fails to rejoin the channel 20 minutes after being disconnected from the Agora
+   * edge server, the SDK triggers the `onConnectionStateChanged` callback, switches to the
+   * `CONNECTION_STATE_FAILED` state, and stops rejoining the channel.
+   */
+  CONNECTION_STATE_RECONNECTING = 4,
+  /**
+   * 5: The SDK fails to connect to the Agora edge server or join the channel. This state indicates
+   * that the SDK stops trying to rejoin the channel. You must call `leaveChannel` to leave the
+   * channel.
+   * - You can call `joinChannel` to rejoin the channel.
+   * - If the SDK is banned from joining the channel by the Agora edge server through the RESTful
+   * API, the SDK triggers the `onConnectionStateChanged` callback.
+   */
+  CONNECTION_STATE_FAILED = 5,
+};
+
+/**
+ * Transcoding configurations of each host.
+ */
+struct TranscodingUser {
+  /**
+   * The user ID of the host.
+   */
+  uid_t uid;
+  /**
+   * The x coordinate (pixel) of the host's video on the output video frame (taking the upper left corner of the video frame as the origin). The value range is [0, width], where width is the `width` set in `LiveTranscoding`.
+   */
+  int x;
+  /**
+   * The y coordinate (pixel) of the host's video on the output video frame (taking the upper left corner of the video frame as the origin). The value range is [0, height], where height is the `height` set in `LiveTranscoding`.
+   */
+  int y;
+  /**
+   * The width (pixel) of the host's video.
+   */
+  int width;
+  /**
+   * The height (pixel) of the host's video.
+   */
+  int height;
+  /**
+   * The layer index number of the host's video. The value range is [0, 100].
+   * - 0: (Default) The host's video is the bottom layer.
+   * - 100: The host's video is the top layer.
+   *
+   * If the value is beyond this range, the SDK reports the error code `ERR_INVALID_ARGUMENT`.
+  */
+  int zOrder;
+  /**
+   * The transparency of the host's video. The value range is [0.0, 1.0].
+   * - 0.0: Completely transparent.
+   * - 1.0: (Default) Opaque.
+   */
+  double alpha;
+  /**
+   * The audio channel used by the host's audio in the output audio. The default value is 0, and the value range is [0, 5].
+   * - `0`: (Recommended) The defaut setting, which supports dual channels at most and depends on the upstream of the host.
+   * - `1`: The host's audio uses the FL audio channel. If the host's upstream uses multiple audio channels, the Agora server mixes them into mono first.
+   * - `2`: The host's audio uses the FC audio channel. If the host's upstream uses multiple audio channels, the Agora server mixes them into mono first.
+   * - `3`: The host's audio uses the FR audio channel. If the host's upstream uses multiple audio channels, the Agora server mixes them into mono first.
+   * - `4`: The host's audio uses the BL audio channel. If the host's upstream uses multiple audio channels, the Agora server mixes them into mono first.
+   * - `5`: The host's audio uses the BR audio channel. If the host's upstream uses multiple audio channels, the Agora server mixes them into mono first.
+   * - `0xFF` or a value greater than 5: The host's audio is muted, and the Agora server removes the host's audio.
+   *
+   * @note If the value is not `0`, a special player is required.
+   */
+  int audioChannel;
+
+  TranscodingUser()
+      : uid(0),
+        x(0),
+        y(0),
+        width(0),
+        height(0),
+        zOrder(0),
+        alpha(1.0),
+        audioChannel(0) {}
+};
+
+/**
+ * Transcoding configurations for Media Push.
+ */
+struct LiveTranscoding {
+  /** The width of the video in pixels. The default value is 360.
+   * - When pushing video streams to the CDN, the value range of `width` is [64,1920].
+   * If the value is less than 64, Agora server automatically adjusts it to 64; if the
+   * value is greater than 1920, Agora server automatically adjusts it to 1920.
+   * - When pushing audio streams to the CDN, set `width` and `height` as 0.
+   */
+  int width;
+  /** The height of the video in pixels. The default value is 640.
+   * - When pushing video streams to the CDN, the value range of `height` is [64,1080].
+   * If the value is less than 64, Agora server automatically adjusts it to 64; if the
+   * value is greater than 1080, Agora server automatically adjusts it to 1080.
+   * - When pushing audio streams to the CDN, set `width` and `height` as 0.
+   */
+  int height;
+  /** Bitrate of the CDN live output video stream. The default value is 400 Kbps.
+
+  Set this parameter according to the Video Bitrate Table. If you set a bitrate beyond the proper range, the SDK automatically adapts it to a value within the range.
+  */
+  int videoBitrate;
+  /** Frame rate of the output video stream set for the CDN live streaming. The default value is 15 fps, and the value range is (0,30].
+
+  @note The Agora server adjusts any value over 30 to 30.
+  */
+  int videoFramerate;
+
+  /** **DEPRECATED** Latency mode:
+
+   - true: Low latency with unassured quality.
+   - false: (Default) High latency with assured quality.
+   */
+  bool lowLatency;
+
+  /** Video GOP in frames. The default value is 30 fps.
+   */
+  int videoGop;
+  /** Self-defined video codec profile: #VIDEO_CODEC_PROFILE_TYPE.
+
+  @note If you set this parameter to other values, Agora adjusts it to the default value of 100.
+  */
+  VIDEO_CODEC_PROFILE_TYPE videoCodecProfile;
+  /** The background color in RGB hex value. Value only. Do not include a preceeding #. For example, 0xFFB6C1 (light pink). The default value is 0x000000 (black).
+   */
+  unsigned int backgroundColor;
+  /** Video codec profile types for Media Push. See VIDEO_CODEC_TYPE_FOR_STREAM. */
+  VIDEO_CODEC_TYPE_FOR_STREAM videoCodecType;
+  /** The number of users in the live interactive streaming.
+   *  The value range is [0, 17].
+   */
+  unsigned int userCount;
+  /** Manages the user layout configuration in the Media Push. Agora supports a maximum of 17 transcoding users in a Media Push channel. See `TranscodingUser`.
+   */
+  TranscodingUser* transcodingUsers;
+  /** Reserved property. Extra user-defined information to send SEI for the H.264/H.265 video stream to the CDN live client. Maximum length: 4096 Bytes.
+
+   For more information on SEI frame, see [SEI-related questions](https://docs.agora.io/en/faq/sei).
+   */
+  const char* transcodingExtraInfo;
+
+  /** **DEPRECATED** The metadata sent to the CDN live client.
+   */
+  const char* metadata;
+  /** The watermark on the live video. The image format needs to be PNG. See `RtcImage`.
+
+  You can add one watermark, or add multiple watermarks using an array. This parameter is used with `watermarkCount`.
+  */
+  RtcImage* watermark;
+  /**
+   * The number of watermarks on the live video. The total number of watermarks and background images can range from 0 to 10. This parameter is used with `watermark`.
+   */
+  unsigned int watermarkCount;
+
+  /** The number of background images on the live video. The image format needs to be PNG. See `RtcImage`.
+   *
+   * You can add a background image or use an array to add multiple background images. This parameter is used with `backgroundImageCount`.
+   */
+  RtcImage* backgroundImage;
+  /**
+   * The number of background images on the live video. The total number of watermarks and background images can range from 0 to 10. This parameter is used with `backgroundImage`.
+   */
+  unsigned int backgroundImageCount;
+
+  /** The audio sampling rate (Hz) of the output media stream. See #AUDIO_SAMPLE_RATE_TYPE.
+   */
+  AUDIO_SAMPLE_RATE_TYPE audioSampleRate;
+  /** Bitrate (Kbps) of the audio output stream for Media Push. The default value is 48, and the highest value is 128.
+   */
+  int audioBitrate;
+  /** The number of audio channels for Media Push. Agora recommends choosing 1 (mono), or 2 (stereo) audio channels. Special players are required if you choose 3, 4, or 5.
+   * - 1: (Default) Mono.
+   * - 2: Stereo.
+   * - 3: Three audio channels.
+   * - 4: Four audio channels.
+   * - 5: Five audio channels.
+   */
+  int audioChannels;
+  /** Audio codec profile type for Media Push. See #AUDIO_CODEC_PROFILE_TYPE.
+   */
+  AUDIO_CODEC_PROFILE_TYPE audioCodecProfile;
+  /** Advanced features of the RTMP or RTMPS streaming with transcoding. See LiveStreamAdvancedFeature.
+   */
+  LiveStreamAdvancedFeature* advancedFeatures;
+
+  /** The number of enabled advanced features. The default value is 0. */
+  unsigned int advancedFeatureCount;
+
+  LiveTranscoding()
+      : width(360),
+        height(640),
+        videoBitrate(400),
+        videoFramerate(15),
+        lowLatency(false),
+        videoGop(30),
+        videoCodecProfile(VIDEO_CODEC_PROFILE_HIGH),
+        backgroundColor(0x000000),
+        videoCodecType(VIDEO_CODEC_H264_FOR_STREAM),
+        userCount(0), 
+        transcodingUsers(OPTIONAL_NULLPTR),
+        transcodingExtraInfo(OPTIONAL_NULLPTR),
+        metadata(OPTIONAL_NULLPTR),
+        watermark(OPTIONAL_NULLPTR),
+        watermarkCount(0),
+        backgroundImage(OPTIONAL_NULLPTR),
+        backgroundImageCount(0),
+        audioSampleRate(AUDIO_SAMPLE_RATE_48000),
+        audioBitrate(48),
+        audioChannels(1),
+        audioCodecProfile(AUDIO_CODEC_PROFILE_LC_AAC),
+        advancedFeatures(OPTIONAL_NULLPTR),
+        advancedFeatureCount(0) {}
+};
+
+/**
+ * The video streams for the video mixing on the local client.
+ */
+struct TranscodingVideoStream {
+  /**
+   * The source type of video for the video mixing on the local client. See #VIDEO_SOURCE_TYPE.
+   */
+  VIDEO_SOURCE_TYPE sourceType;
+  /**
+   * The ID of the remote user.
+   * @note Use this parameter only when the source type of the video for the video mixing on the local client is `VIDEO_SOURCE_REMOTE`.
+   */
+  uid_t remoteUserUid;
+  /**
+   * The URL of the image.
+   * @note Use this parameter only when the source type of the video for the video mixing on the local client is `RTC_IMAGE`.
+   */
+  const char* imageUrl;
+  /**
+   * MediaPlayer id if sourceType is MEDIA_PLAYER_SOURCE.
+   */
+  int mediaPlayerId;
+  /**
+   * The horizontal displacement of the top-left corner of the video for the video mixing on the client relative to the top-left corner (origin) of the canvas for this video mixing.
+   */
+  int x;
+  /**
+   * The vertical displacement of the top-left corner of the video for the video mixing on the client relative to the top-left corner (origin) of the canvas for this video mixing.
+   */
+  int y;
+  /**
+   * The width (px) of the video for the video mixing on the local client.
+   */
+  int width;
+  /**
+   * The height (px) of the video for the video mixing on the local client.
+   */
+  int height;
+  /**
+   * The number of the layer to which the video for the video mixing on the local client belongs. The value range is [0,100].
+   * - 0: (Default) The layer is at the bottom.
+   * - 100: The layer is at the top.
+   */
+  int zOrder;
+  /**
+   * The transparency of the video for the video mixing on the local client. The value range is [0.0,1.0]. 0.0 means the transparency is completely transparent. 1.0 means the transparency is opaque.
+   */
+  double alpha;
+  /**
+   * Whether to mirror the video for the video mixing on the local client.
+   * - true: Mirroring.
+   * - false: (Default) Do not mirror.
+   * @note The paramter only works for videos with the source type `CAMERA`.
+   */
+  bool mirror;
+
+  TranscodingVideoStream()
+    : sourceType(VIDEO_SOURCE_CAMERA_PRIMARY),
+      remoteUserUid(0),
+      imageUrl(OPTIONAL_NULLPTR),
+      x(0),
+      y(0),
+      width(0),
+      height(0),
+      zOrder(0),
+      alpha(1.0),
+      mirror(false) {}
+};
+
+/**
+ * The configuration of the video mixing on the local client.
+ */
+struct LocalTranscoderConfiguration {
+  /**
+   * The number of the video streams for the video mixing on the local client.
+   */
+  unsigned int streamCount;
+  /**
+   * The video streams for the video mixing on the local client. See TranscodingVideoStream.
+   */
+  TranscodingVideoStream* videoInputStreams;
+  /**
+   * The encoding configuration of the mixed video stream after the video mixing on the local client. See VideoEncoderConfiguration.
+   */
+  VideoEncoderConfiguration videoOutputConfiguration;
+  /**
+   * Whether to use the timestamp when the primary camera captures the video frame as the timestamp of the mixed video frame.
+   * - true: (Default) Use the timestamp of the captured video frame as the timestamp of the mixed video frame.
+   * - false: Do not use the timestamp of the captured video frame as the timestamp of the mixed video frame. Instead, use the timestamp when the mixed video frame is constructed.
+   */
+  bool syncWithPrimaryCamera;
+
+  LocalTranscoderConfiguration() : streamCount(0), videoInputStreams(OPTIONAL_NULLPTR), videoOutputConfiguration(), syncWithPrimaryCamera(true) {}
+};
+
+enum VIDEO_TRANSCODER_ERROR {
+  /**
+   * The video track of the video source is not started.
+   */
+  VT_ERR_VIDEO_SOURCE_NOT_READY = 1,
+  /**
+   * The video source type is not supported.
+   */
+  VT_ERR_INVALID_VIDEO_SOURCE_TYPE = 2,
+  /**
+   * The image url is not correctly of image source.
+   */
+  VT_ERR_INVALID_IMAGE_PATH = 3,
+  /**
+   * The image format not the type png/jpeg/gif of image source.
+   */
+  VT_ERR_UNSUPPORT_IMAGE_FORMAT = 4,
+  /**
+   * The layout is invalid such as width is zero.
+   */
+  VT_ERR_INVALID_LAYOUT = 5,
+  /**
+   * Internal error.
+   */
+  VT_ERR_INTERNAL = 20
+};
+
+/**
+ * Configurations of the last-mile network test.
+ */
+struct LastmileProbeConfig {
+  /**
+   * Determines whether to test the uplink network. Some users, for example,
+   * the audience in a live broadcast channel, do not need such a test:
+   * - true: Test.
+   * - false: Do not test.
+   */
+  bool probeUplink;
+  /**
+   * Determines whether to test the downlink network:
+   * - true: Test.
+   * - false: Do not test.
+   */
+  bool probeDownlink;
+  /**
+   * The expected maximum sending bitrate (bps) of the local user. The value range is [100000, 5000000]. We recommend setting this parameter
+   * according to the bitrate value set by `setVideoEncoderConfiguration`.
+   */
+  unsigned int expectedUplinkBitrate;
+  /**
+   * The expected maximum receiving bitrate (bps) of the local user. The value range is [100000,5000000].
+   */
+  unsigned int expectedDownlinkBitrate;
+};
+
+/**
+ * The status of the last-mile network tests.
+ */
+enum LASTMILE_PROBE_RESULT_STATE {
+  /**
+   * 1: The last-mile network probe test is complete.
+   */
+  LASTMILE_PROBE_RESULT_COMPLETE = 1,
+  /**
+   * 2: The last-mile network probe test is incomplete because the bandwidth estimation is not available due to limited test resources.
+   */
+  LASTMILE_PROBE_RESULT_INCOMPLETE_NO_BWE = 2,
+  /**
+   * 3: The last-mile network probe test is not carried out, probably due to poor network conditions.
+   */
+  LASTMILE_PROBE_RESULT_UNAVAILABLE = 3
+};
+
+/**
+ * Results of the uplink or downlink last-mile network test.
+ */
+struct LastmileProbeOneWayResult {
+  /**
+   * The packet loss rate (%).
+   */
+  unsigned int packetLossRate;
+  /**
+   * The network jitter (ms).
+   */
+  unsigned int jitter;
+  /**
+   * The estimated available bandwidth (bps).
+   */
+  unsigned int availableBandwidth;
+
+  LastmileProbeOneWayResult() : packetLossRate(0),
+                                jitter(0),
+                                availableBandwidth(0) {}
+};
+
+/**
+ * Results of the uplink and downlink last-mile network tests.
+ */
+struct LastmileProbeResult {
+  /**
+   * The status of the last-mile network tests. See #LASTMILE_PROBE_RESULT_STATE.
+   */
+  LASTMILE_PROBE_RESULT_STATE state;
+  /**
+   * Results of the uplink last-mile network test. For details, see LastmileProbeOneWayResult.
+   */
+  LastmileProbeOneWayResult uplinkReport;
+  /**
+   * Results of the downlink last-mile network test. For details, see LastmileProbeOneWayResult.
+   */
+  LastmileProbeOneWayResult downlinkReport;
+  /**
+   * The round-trip time (ms).
+   */
+  unsigned int rtt;
+
+  LastmileProbeResult()
+    : state(LASTMILE_PROBE_RESULT_UNAVAILABLE),
+      rtt(0) {}
+};
+
+/**
+ * Reasons causing the change of the connection state.
+ */
+enum CONNECTION_CHANGED_REASON_TYPE
+{
+  /**
+   * 0: The SDK is connecting to the server.
+   */
+  CONNECTION_CHANGED_CONNECTING = 0,
+  /**
+   * 1: The SDK has joined the channel successfully.
+   */
+  CONNECTION_CHANGED_JOIN_SUCCESS = 1,
+  /**
+   * 2: The connection between the SDK and the server is interrupted.
+   */
+  CONNECTION_CHANGED_INTERRUPTED = 2,
+  /**
+   * 3: The connection between the SDK and the server is banned by the server. This error occurs when the user is kicked out of the channel by the server.
+   */
+  CONNECTION_CHANGED_BANNED_BY_SERVER = 3,
+  /**
+   * 4: The SDK fails to join the channel. When the SDK fails to join the channel for more than 20 minutes, this error occurs and the SDK stops reconnecting to the channel.
+   */
+  CONNECTION_CHANGED_JOIN_FAILED = 4,
+  /**
+   * 5: The SDK has left the channel.
+   */
+  CONNECTION_CHANGED_LEAVE_CHANNEL = 5,
+  /**
+   * 6: The connection fails because the App ID is not valid.
+   */
+  CONNECTION_CHANGED_INVALID_APP_ID = 6,
+  /**
+   * 7: The connection fails because the channel name is not valid. Please rejoin the channel with a valid channel name.
+   */
+  CONNECTION_CHANGED_INVALID_CHANNEL_NAME = 7,
+  /**
+   * 8: The connection fails because the token is not valid. Typical reasons include:
+   * - The App Certificate for the project is enabled in Agora Console, but you do not use a token when joining the channel. If you enable the App Certificate, you must use a token to join the channel.
+   * - The `uid` specified when calling `joinChannel` to join the channel is inconsistent with the `uid` passed in when generating the token.
+   */
+  CONNECTION_CHANGED_INVALID_TOKEN = 8,
+  /**
+   * 9: The connection fails because the token has expired.
+   */
+  CONNECTION_CHANGED_TOKEN_EXPIRED = 9,
+  /**
+   * 10: The connection is rejected by the server. Typical reasons include:
+   * - The user is already in the channel and still calls a method, for example, `joinChannel`, to join the channel. Stop calling this method to clear this error.
+   * - The user tries to join the channel when conducting a pre-call test. The user needs to call the channel after the call test ends.
+   */
+  CONNECTION_CHANGED_REJECTED_BY_SERVER = 10,
+  /**
+   * 11: The connection changes to reconnecting because the SDK has set a proxy server.
+   */
+  CONNECTION_CHANGED_SETTING_PROXY_SERVER = 11,
+  /**
+   * 12: The connection state changed because the token is renewed.
+   */
+  CONNECTION_CHANGED_RENEW_TOKEN = 12,
+  /**
+   * 13: The IP address of the client has changed, possibly because the network type, IP address, or port has been changed.
+   */
+  CONNECTION_CHANGED_CLIENT_IP_ADDRESS_CHANGED = 13,
+  /**
+   * 14: Timeout for the keep-alive of the connection between the SDK and the Agora edge server. The connection state changes to CONNECTION_STATE_RECONNECTING.
+   */
+  CONNECTION_CHANGED_KEEP_ALIVE_TIMEOUT = 14,
+  /**
+   * 15: The SDK has rejoined the channel successfully.
+   */
+  CONNECTION_CHANGED_REJOIN_SUCCESS = 15,
+  /**
+   * 16: The connection between the SDK and the server is lost.
+   */
+  CONNECTION_CHANGED_LOST = 16,
+  /**
+   * 17: The change of connection state is caused by echo test.
+   */
+  CONNECTION_CHANGED_ECHO_TEST = 17,
+  /**
+   * 18: The local IP Address is changed by user.
+   */
+  CONNECTION_CHANGED_CLIENT_IP_ADDRESS_CHANGED_BY_USER = 18,
+  /**
+   * 19: The connection is failed due to join the same channel on another device with the same uid.
+   */
+  CONNECTION_CHANGED_SAME_UID_LOGIN = 19,
+  /**
+   * 20: The connection is failed due to too many broadcasters in the channel.
+   */
+  CONNECTION_CHANGED_TOO_MANY_BROADCASTERS = 20,
+
+  /**
+   * 21: The connection is failed due to license validation failure.
+   */
+  CONNECTION_CHANGED_LICENSE_VALIDATION_FAILURE = 21,
+  /*
+   * 22: The connection is failed due to certification verify failure.
+   */
+  CONNECTION_CHANGED_CERTIFICATION_VERYFY_FAILURE = 22,
+  /**
+   * 23: The connection is failed due to the lack of granting permission to the stream channel.
+   */
+  CONNECTION_CHANGED_STREAM_CHANNEL_NOT_AVAILABLE = 23,
+  /**
+   * 24: The connection is failed due to join channel with an inconsistent appid.
+   */
+  CONNECTION_CHANGED_INCONSISTENT_APPID = 24,
+};
+
+/**
+ * The reason of changing role's failure.
+ */
+enum CLIENT_ROLE_CHANGE_FAILED_REASON {
+  /**
+   * 1: Too many broadcasters in the channel.
+   */
+  CLIENT_ROLE_CHANGE_FAILED_TOO_MANY_BROADCASTERS = 1,
+  /**
+   * 2: The operation of changing role is not authorized.
+   */
+  CLIENT_ROLE_CHANGE_FAILED_NOT_AUTHORIZED = 2,
+  /**
+   * 3: The operation of changing role is timeout.
+   */
+  CLIENT_ROLE_CHANGE_FAILED_REQUEST_TIME_OUT = 3,
+  /**
+   * 4: The operation of changing role is interrupted since we lost connection with agora service.
+   */
+  CLIENT_ROLE_CHANGE_FAILED_CONNECTION_FAILED = 4,
+};
+
+/**
+ * The reason of notifying the user of a message.
+ */
+enum WLACC_MESSAGE_REASON {
+  /**
+   * WIFI signal is weak.
+   */
+  WLACC_MESSAGE_REASON_WEAK_SIGNAL = 0,
+  /**
+   * Channel congestion.
+   */
+  WLACC_MESSAGE_REASON_CHANNEL_CONGESTION = 1,
+};
+
+/**
+ * Suggest an action for the user.
+ */
+enum WLACC_SUGGEST_ACTION {
+  /**
+   * Please get close to AP.
+   */
+  WLACC_SUGGEST_ACTION_CLOSE_TO_WIFI = 0,
+  /**
+   * The user is advised to connect to the prompted SSID.
+   */
+  WLACC_SUGGEST_ACTION_CONNECT_SSID = 1,
+  /**
+   * The user is advised to check whether the AP supports 5G band and enable 5G band (the aciton link is attached), or purchases an AP that supports 5G. AP does not support 5G band.
+   */
+  WLACC_SUGGEST_ACTION_CHECK_5G = 2,
+  /**
+   * The user is advised to change the SSID of the 2.4G or 5G band (the aciton link is attached). The SSID of the 2.4G band AP is the same as that of the 5G band.
+   */
+  WLACC_SUGGEST_ACTION_MODIFY_SSID = 3,
+};
+
+/**
+ * Indicator optimization degree.
+ */
+struct WlAccStats {
+  /**
+   * End-to-end delay optimization percentage.
+   */
+  unsigned short e2eDelayPercent;
+  /**
+   * Frozen Ratio optimization percentage.
+   */
+  unsigned short frozenRatioPercent;
+  /**
+   * Loss Rate optimization percentage.
+   */
+  unsigned short lossRatePercent;
+};
+
+/**
+ * The network type.
+ */
+enum NETWORK_TYPE {
+  /**
+   * -1: The network type is unknown.
+   */
+  NETWORK_TYPE_UNKNOWN = -1,
+  /**
+   * 0: The SDK disconnects from the network.
+   */
+  NETWORK_TYPE_DISCONNECTED = 0,
+  /**
+   * 1: The network type is LAN.
+   */
+  NETWORK_TYPE_LAN = 1,
+  /**
+   * 2: The network type is Wi-Fi (including hotspots).
+   */
+  NETWORK_TYPE_WIFI = 2,
+  /**
+   * 3: The network type is mobile 2G.
+   */
+  NETWORK_TYPE_MOBILE_2G = 3,
+  /**
+   * 4: The network type is mobile 3G.
+   */
+  NETWORK_TYPE_MOBILE_3G = 4,
+  /**
+   * 5: The network type is mobile 4G.
+   */
+  NETWORK_TYPE_MOBILE_4G = 5,
+  /**
+   * 6: The network type is mobile 5G.
+   */
+  NETWORK_TYPE_MOBILE_5G = 6,
+};
+
+/**
+ * The mode of setting up video views.
+ */
+enum VIDEO_VIEW_SETUP_MODE {
+  /**
+   * 0: replace one view
+   */
+  VIDEO_VIEW_SETUP_REPLACE = 0,
+  /**
+   * 1: add one view
+   */
+  VIDEO_VIEW_SETUP_ADD = 1,
+  /**
+   * 2: remove one view
+   */
+  VIDEO_VIEW_SETUP_REMOVE = 2,
+};
+
+/**
+ * Attributes of video canvas object.
+ */
+struct VideoCanvas {
+  /**
+   * The user id of local video.
+   */
+  uid_t uid;
+
+  /**
+  * The uid of video stream composing the video stream from transcoder which will be drawn on this video canvas. 
+  */
+  uid_t subviewUid;
+  /**
+   * Video display window.
+   */
+  view_t view;
+  /**
+   * A RGBA value indicates background color of the render view. Defaults to 0x00000000.
+   */
+  uint32_t backgroundColor;
+  /**
+   * The video render mode. See \ref agora::media::base::RENDER_MODE_TYPE "RENDER_MODE_TYPE".
+   * The default value is RENDER_MODE_HIDDEN.
+   */
+  media::base::RENDER_MODE_TYPE renderMode;
+  /**
+   * The video mirror mode. See \ref VIDEO_MIRROR_MODE_TYPE "VIDEO_MIRROR_MODE_TYPE".
+   * The default value is VIDEO_MIRROR_MODE_AUTO.
+   * @note
+   * - For the mirror mode of the local video view: 
+   * If you use a front camera, the SDK enables the mirror mode by default;
+   * if you use a rear camera, the SDK disables the mirror mode by default.
+   * - For the remote user: The mirror mode is disabled by default.
+   */
+  VIDEO_MIRROR_MODE_TYPE mirrorMode;
+  /**
+   * The mode of setting up video view. See \ref VIDEO_VIEW_SETUP_MODE "VIDEO_VIEW_SETUP_MODE"
+   * The default value is VIDEO_VIEW_SETUP_REPLACE.
+   */
+  VIDEO_VIEW_SETUP_MODE setupMode;
+  /**
+   * The video source type. See \ref VIDEO_SOURCE_TYPE "VIDEO_SOURCE_TYPE".
+   * The default value is VIDEO_SOURCE_CAMERA_PRIMARY.
+   */
+  VIDEO_SOURCE_TYPE sourceType;
+  /**
+   * The media player id of AgoraMediaPlayer. It should set this parameter when the 
+   * sourceType is VIDEO_SOURCE_MEDIA_PLAYER to show the video that AgoraMediaPlayer is playing.
+   * You can get this value by calling the method \ref getMediaPlayerId().
+   */
+  int mediaPlayerId;
+  /**
+   * If you want to display a certain part of a video frame, you can set 
+   * this value to crop the video frame to show. 
+   * The default value is empty(that is, if it has zero width or height), which means no cropping.
+   */
+  Rectangle cropArea;
+  /**
+   * Whether to apply alpha mask to the video frame if exsit:
+   * true: Apply alpha mask to video frame.
+   * false: (Default) Do not apply alpha mask to video frame.
+   */
+  bool enableAlphaMask;
+  /**
+   * The video frame position in pipeline. See \ref VIDEO_MODULE_POSITION "VIDEO_MODULE_POSITION".
+   * The default value is POSITION_POST_CAPTURER.
+   */
+  media::base::VIDEO_MODULE_POSITION position;
+
+  VideoCanvas()
+    : uid(0), subviewUid(0), view(NULL), backgroundColor(0x00000000), renderMode(media::base::RENDER_MODE_HIDDEN), mirrorMode(VIDEO_MIRROR_MODE_AUTO),
+      setupMode(VIDEO_VIEW_SETUP_REPLACE), sourceType(VIDEO_SOURCE_CAMERA_PRIMARY), mediaPlayerId(-ERR_NOT_READY),
+      cropArea(0, 0, 0, 0), enableAlphaMask(false), position(media::base::POSITION_POST_CAPTURER) {}
+
+  VideoCanvas(view_t v, media::base::RENDER_MODE_TYPE m, VIDEO_MIRROR_MODE_TYPE mt)
+    : uid(0), subviewUid(0), view(v), backgroundColor(0x00000000), renderMode(m), mirrorMode(mt), setupMode(VIDEO_VIEW_SETUP_REPLACE),
+      sourceType(VIDEO_SOURCE_CAMERA_PRIMARY), mediaPlayerId(-ERR_NOT_READY),
+      cropArea(0, 0, 0, 0), enableAlphaMask(false), position(media::base::POSITION_POST_CAPTURER) {}
+
+  VideoCanvas(view_t v, media::base::RENDER_MODE_TYPE m, VIDEO_MIRROR_MODE_TYPE mt, uid_t u)
+    : uid(u), subviewUid(0), view(v), backgroundColor(0x00000000), renderMode(m), mirrorMode(mt), setupMode(VIDEO_VIEW_SETUP_REPLACE),
+      sourceType(VIDEO_SOURCE_CAMERA_PRIMARY), mediaPlayerId(-ERR_NOT_READY),
+      cropArea(0, 0, 0, 0), enableAlphaMask(false), position(media::base::POSITION_POST_CAPTURER) {}
+
+  VideoCanvas(view_t v, media::base::RENDER_MODE_TYPE m, VIDEO_MIRROR_MODE_TYPE mt, uid_t u, uid_t subu)
+    : uid(u), subviewUid(subu), view(v), backgroundColor(0x00000000), renderMode(m), mirrorMode(mt), setupMode(VIDEO_VIEW_SETUP_REPLACE),
+      sourceType(VIDEO_SOURCE_CAMERA_PRIMARY), mediaPlayerId(-ERR_NOT_READY),
+      cropArea(0, 0, 0, 0), enableAlphaMask(false), position(media::base::POSITION_POST_CAPTURER) {}
+};
+
+enum PIP_STATE {
+  PIP_STATE_STARTED = 0,
+  PIP_STATE_STOPPED = 1,
+  PIP_STATE_FAILED = 2,
+};
+
+struct PipOptions {
+  void* contentSource;
+  int contentWidth;
+  int contentHeight;
+#if defined(__APPLE__) && TARGET_OS_IOS
+  bool autoEnterPip = false;
+  VideoCanvas canvas = VideoCanvas();
+#endif
+  PipOptions(): contentSource(NULL), contentWidth(0), contentHeight(0) {}
+};
+
+/** Image enhancement options.
+ */
+struct BeautyOptions {
+  /** The contrast level.
+    */
+  enum LIGHTENING_CONTRAST_LEVEL {
+      /** Low contrast level. */
+      LIGHTENING_CONTRAST_LOW = 0,
+      /** (Default) Normal contrast level. */
+      LIGHTENING_CONTRAST_NORMAL = 1,
+      /** High contrast level. */
+      LIGHTENING_CONTRAST_HIGH = 2,
+  };
+
+  /** The contrast level, used with the `lighteningLevel` parameter. The larger the value, the greater the contrast between light and dark. See #LIGHTENING_CONTRAST_LEVEL.
+    */
+  LIGHTENING_CONTRAST_LEVEL lighteningContrastLevel;
+
+  /** The brightness level. The value ranges from 0.0 (original) to 1.0. The default value is 0.0. The greater the value, the greater the degree of whitening. */
+  float lighteningLevel;
+
+  /** The value ranges from 0.0 (original) to 1.0. The default value is 0.0. The greater the value, the greater the degree of skin grinding.
+    */
+  float smoothnessLevel;
+
+  /** The redness level. The value ranges from 0.0 (original) to 1.0. The default value is 0.0. The larger the value, the greater the rosy degree.
+    */
+  float rednessLevel;
+
+  /** The sharpness level. The value ranges from 0.0 (original) to 1.0. The default value is 0.0. The larger the value, the greater the sharpening degree.
+  */
+  float sharpnessLevel;
+
+  BeautyOptions(LIGHTENING_CONTRAST_LEVEL contrastLevel, float lightening, float smoothness, float redness, float sharpness) : lighteningContrastLevel(contrastLevel), lighteningLevel(lightening), smoothnessLevel(smoothness), rednessLevel(redness), sharpnessLevel(sharpness) {}
+
+  BeautyOptions() : lighteningContrastLevel(LIGHTENING_CONTRAST_NORMAL), lighteningLevel(0), smoothnessLevel(0), rednessLevel(0), sharpnessLevel(0) {}
+};
+
+struct LowlightEnhanceOptions {
+  /**
+   * The low-light enhancement mode.
+   */
+  enum LOW_LIGHT_ENHANCE_MODE {
+    /** 0: (Default) Automatic mode. The SDK automatically enables or disables the low-light enhancement feature according to the ambient light to compensate for the lighting level or prevent overexposure, as necessary. */
+    LOW_LIGHT_ENHANCE_AUTO = 0,
+    /** Manual mode. Users need to enable or disable the low-light enhancement feature manually. */
+    LOW_LIGHT_ENHANCE_MANUAL = 1,
+  };
+  /**
+   * The low-light enhancement level.
+   */
+  enum LOW_LIGHT_ENHANCE_LEVEL {
+    /**
+     * 0: (Default) Promotes video quality during low-light enhancement. It processes the brightness, details, and noise of the video image. The performance consumption is moderate, the processing speed is moderate, and the overall video quality is optimal.
+     */
+    LOW_LIGHT_ENHANCE_LEVEL_HIGH_QUALITY = 0,
+    /**
+     * Promotes performance during low-light enhancement. It processes the brightness and details of the video image. The processing speed is faster.
+     */
+    LOW_LIGHT_ENHANCE_LEVEL_FAST = 1,
+  };
+
+  /** The low-light enhancement mode. See #LOW_LIGHT_ENHANCE_MODE.
+   */
+  LOW_LIGHT_ENHANCE_MODE mode;
+
+  /** The low-light enhancement level. See #LOW_LIGHT_ENHANCE_LEVEL.
+   */
+  LOW_LIGHT_ENHANCE_LEVEL level;
+
+  LowlightEnhanceOptions(LOW_LIGHT_ENHANCE_MODE lowlightMode, LOW_LIGHT_ENHANCE_LEVEL lowlightLevel) : mode(lowlightMode), level(lowlightLevel) {}
+
+  LowlightEnhanceOptions() : mode(LOW_LIGHT_ENHANCE_AUTO), level(LOW_LIGHT_ENHANCE_LEVEL_HIGH_QUALITY) {}
+};
+/**
+ * The video noise reduction options.
+ *
+ * @since v4.0.0
+ */
+struct VideoDenoiserOptions {
+  /** The video noise reduction mode.
+   */
+  enum VIDEO_DENOISER_MODE {
+    /** 0: (Default) Automatic mode. The SDK automatically enables or disables the video noise reduction feature according to the ambient light. */
+    VIDEO_DENOISER_AUTO = 0,
+    /** Manual mode. Users need to enable or disable the video noise reduction feature manually. */
+    VIDEO_DENOISER_MANUAL = 1,
+  };
+  /**
+   * The video noise reduction level.
+   */
+  enum VIDEO_DENOISER_LEVEL {
+    /**
+     * 0: (Default) Promotes video quality during video noise reduction. `HIGH_QUALITY` balances performance consumption and video noise reduction quality.
+     * The performance consumption is moderate, the video noise reduction speed is moderate, and the overall video quality is optimal.
+     */
+    VIDEO_DENOISER_LEVEL_HIGH_QUALITY = 0,
+    /**
+     * Promotes reducing performance consumption during video noise reduction. `FAST` prioritizes reducing performance consumption over video noise reduction quality.
+     * The performance consumption is lower, and the video noise reduction speed is faster. To avoid a noticeable shadowing effect (shadows trailing behind moving objects) in the processed video, Agora recommends that you use `FAST` when the camera is fixed.
+     */
+    VIDEO_DENOISER_LEVEL_FAST = 1,
+    /**
+     * Enhanced video noise reduction. `STRENGTH` prioritizes video noise reduction quality over reducing performance consumption.
+     * The performance consumption is higher, the video noise reduction speed is slower, and the video noise reduction quality is better.
+     * If `HIGH_QUALITY` is not enough for your video noise reduction needs, you can use `STRENGTH`.
+     */
+    VIDEO_DENOISER_LEVEL_STRENGTH = 2,
+  };
+  /** The video noise reduction mode. See #VIDEO_DENOISER_MODE.
+   */
+  VIDEO_DENOISER_MODE mode;
+
+  /** The video noise reduction level. See #VIDEO_DENOISER_LEVEL.
+   */
+  VIDEO_DENOISER_LEVEL level;
+
+  VideoDenoiserOptions(VIDEO_DENOISER_MODE denoiserMode, VIDEO_DENOISER_LEVEL denoiserLevel) : mode(denoiserMode), level(denoiserLevel) {}
+
+  VideoDenoiserOptions() : mode(VIDEO_DENOISER_AUTO), level(VIDEO_DENOISER_LEVEL_HIGH_QUALITY) {}
+};
+
+/** The color enhancement options.
+ *
+ * @since v4.0.0
+ */
+struct ColorEnhanceOptions {
+  /** The level of color enhancement. The value range is [0.0,1.0]. `0.0` is the default value, which means no color enhancement is applied to the video. The higher the value, the higher the level of color enhancement.
+   */
+  float strengthLevel;
+
+  /** The level of skin tone protection. The value range is [0.0,1.0]. `0.0` means no skin tone protection. The higher the value, the higher the level of skin tone protection.
+   * The default value is `1.0`. When the level of color enhancement is higher, the portrait skin tone can be significantly distorted, so you need to set the level of skin tone protection; when the level of skin tone protection is higher, the color enhancement effect can be slightly reduced.
+   * Therefore, to get the best color enhancement effect, Agora recommends that you adjust `strengthLevel` and `skinProtectLevel` to get the most appropriate values.
+   */
+  float skinProtectLevel;
+
+  ColorEnhanceOptions(float stength, float skinProtect) : strengthLevel(stength), skinProtectLevel(skinProtect) {}
+
+  ColorEnhanceOptions() : strengthLevel(0), skinProtectLevel(1) {}
+};
+
+/**
+ * The custom background image.
+ */
+struct VirtualBackgroundSource {
+  /** The type of the custom background source.
+   */
+  enum BACKGROUND_SOURCE_TYPE {
+    /**
+     * 0: Enable segementation with the captured video frame without replacing the background.
+     */
+    BACKGROUND_NONE = 0,
+    /**
+     * 1: (Default) The background source is a solid color.
+     */
+    BACKGROUND_COLOR = 1,
+    /**
+     * The background source is a file in PNG or JPG format.
+     */
+    BACKGROUND_IMG = 2,
+    /** 
+     * The background source is the blurred original video frame.
+     * */
+    BACKGROUND_BLUR = 3,
+    /** 
+     * The background source is a file in MP4, AVI, MKV, FLV format. 
+     * */
+    BACKGROUND_VIDEO = 4,
+  };
+
+  /** The degree of blurring applied to the background source.
+   */
+  enum BACKGROUND_BLUR_DEGREE {
+    /** 1: The degree of blurring applied to the custom background image is low. The user can almost see the background clearly. */
+    BLUR_DEGREE_LOW = 1,
+    /** 2: The degree of blurring applied to the custom background image is medium. It is difficult for the user to recognize details in the background. */
+    BLUR_DEGREE_MEDIUM = 2,
+    /** 3: (Default) The degree of blurring applied to the custom background image is high. The user can barely see any distinguishing features in the background. */
+    BLUR_DEGREE_HIGH = 3,
+  };
+
+  /** The type of the custom background image. See #BACKGROUND_SOURCE_TYPE.
+   */
+  BACKGROUND_SOURCE_TYPE background_source_type;
+
+  /**
+   * The color of the custom background image. The format is a hexadecimal integer defined by RGB, without the # sign,
+   * such as 0xFFB6C1 for light pink. The default value is 0xFFFFFF, which signifies white. The value range
+   * is [0x000000,0xFFFFFF]. If the value is invalid, the SDK replaces the original background image with a white
+   * background image.
+   *
+   * @note This parameter takes effect only when the type of the custom background image is `BACKGROUND_COLOR`.
+   */
+  unsigned int color;
+
+  /**
+   * The local absolute path of the custom background image. PNG and JPG formats are supported. If the path is invalid,
+   * the SDK replaces the original background image with a white background image.
+   *
+   * @note This parameter takes effect only when the type of the custom background image is `BACKGROUND_IMG`.
+   */
+  const char* source;
+
+  /** The degree of blurring applied to the custom background image. See BACKGROUND_BLUR_DEGREE.
+   * @note This parameter takes effect only when the type of the custom background image is `BACKGROUND_BLUR`.
+   */
+  BACKGROUND_BLUR_DEGREE blur_degree;
+
+  VirtualBackgroundSource() : background_source_type(BACKGROUND_COLOR), color(0xffffff), source(OPTIONAL_NULLPTR),  blur_degree(BLUR_DEGREE_HIGH) {}
+};
+
+struct SegmentationProperty {
+
+    enum SEG_MODEL_TYPE {
+
+    SEG_MODEL_AI = 1,
+    SEG_MODEL_GREEN = 2
+  };
+
+  SEG_MODEL_TYPE modelType;
+
+  float greenCapacity;
+
+
+  SegmentationProperty() : modelType(SEG_MODEL_AI), greenCapacity(0.5){}
+};
+
+/** The type of custom audio track
+*/
+enum AUDIO_TRACK_TYPE {
+  /** 
+   * -1: Invalid audio track
+   */
+  AUDIO_TRACK_INVALID = -1,
+  /** 
+   * 0: Mixable audio track
+   * You can push more than one mixable Audio tracks into one RTC connection(channel id + uid), 
+   * and SDK will mix these tracks into one audio track automatically.
+   * However, compare to direct audio track, mixable track might cause extra 30ms+ delay.
+   */
+  AUDIO_TRACK_MIXABLE = 0,
+  /**
+   * 1: Direct audio track
+   * You can only push one direct (non-mixable) audio track into one RTC connection(channel id + uid). 
+   * Compare to mixable stream, you can have lower lantency using direct audio track.
+   */
+  AUDIO_TRACK_DIRECT = 1,
+  /**
+   * 2: Extenal AEC reference audio track
+   * When all playback audio is handled outside SDK, but AEC is expected working.
+   * Used this track to push audio frame for AEC reference.
+   */
+  AUDIO_TRACK_EXTERNAL_AEC_REFERENCE = 3,
+};
+
+/** The configuration of custom audio track
+*/
+struct AudioTrackConfig {
+  /**
+   * Enable local playback, enabled by default
+   * true: (Default) Enable local playback
+   * false: Do not enable local playback
+   */
+  bool enableLocalPlayback;
+  /**
+   * Enable APM processing
+   * false: AUDIO_TRACK_DIRECT is default false
+   * true: AUDIO_TRACK_DIRECT would process by APM(AEC/ANS/AGC)
+   */
+  bool enableAudioProcessing;
+  /**
+   * Only for direct audio track
+   * Send to encoder directly without any pipeline processing.
+   * false: default value, use default process and callback etc. 
+   * true: ultra simple mode, neither APM or pipeline using.
+   */
+  bool enableDirectPublish;
+  AudioTrackConfig()
+    : enableLocalPlayback(true),enableAudioProcessing(false),enableDirectPublish(false)  {}
+};
+
+/**
+ * Preset local voice reverberation options.
+ * bitmap allocation:
+ * |  bit31  |    bit30 - bit24   |        bit23 - bit16        | bit15 - bit8 |  bit7 - bit0   |
+ * |---------|--------------------|-----------------------------|--------------|----------------|
+ * |reserved | 0x1: voice beauty  | 0x1: chat beautification    | effect types | effect settings|
+ * |         |                    | 0x2: singing beautification |              |                |
+ * |         |                    | 0x3: timbre transform       |              |                |
+ * |         |                    | 0x4: ultra high_quality     |              |                |
+ * |         |--------------------|-----------------------------|              |                |
+ * |         | 0x2: audio effect  | 0x1: space construction     |              |                |
+ * |         |                    | 0x2: voice changer effect   |              |                |
+ * |         |                    | 0x3: style transform        |              |                |
+ * |         |                    | 0x4: electronic sound       |              |                |
+ * |         |                    | 0x5: magic tone             |              |                |
+ * |         |--------------------|-----------------------------|              |                |
+ * |         | 0x3: voice changer | 0x1: voice transform        |              |                |
+ */
+/** The options for SDK preset voice beautifier effects.
+ */
+enum VOICE_BEAUTIFIER_PRESET {
+  /** Turn off voice beautifier effects and use the original voice.
+   */
+  VOICE_BEAUTIFIER_OFF = 0x00000000,
+  /** A more magnetic voice.
+   *
+   * @note Agora recommends using this enumerator to process a male-sounding voice; otherwise, you
+   * may experience vocal distortion.
+   */
+  CHAT_BEAUTIFIER_MAGNETIC = 0x01010100,
+  /** A fresher voice.
+   *
+   * @note Agora recommends using this enumerator to process a female-sounding voice; otherwise, you
+   * may experience vocal distortion.
+   */
+  CHAT_BEAUTIFIER_FRESH = 0x01010200,
+  /** A more vital voice.
+   *
+   * @note Agora recommends using this enumerator to process a female-sounding voice; otherwise, you
+   * may experience vocal distortion.
+   */
+  CHAT_BEAUTIFIER_VITALITY = 0x01010300,
+  /**
+   * Singing beautifier effect.
+   * - If you call `setVoiceBeautifierPreset`(SINGING_BEAUTIFIER), you can beautify a male-sounding voice and add a reverberation effect
+   * that sounds like singing in a small room. Agora recommends not using `setVoiceBeautifierPreset`(SINGING_BEAUTIFIER) to process
+   * a female-sounding voice; otherwise, you may experience vocal distortion.
+   * - If you call `setVoiceBeautifierParameters`(SINGING_BEAUTIFIER, param1, param2), you can beautify a male- or
+   * female-sounding voice and add a reverberation effect.
+   */
+  SINGING_BEAUTIFIER = 0x01020100,
+  /** A more vigorous voice.
+   */
+  TIMBRE_TRANSFORMATION_VIGOROUS = 0x01030100,
+  /** A deeper voice.
+   */
+  TIMBRE_TRANSFORMATION_DEEP = 0x01030200,
+  /** A mellower voice.
+   */
+  TIMBRE_TRANSFORMATION_MELLOW = 0x01030300,
+  /** A falsetto voice.
+   */
+  TIMBRE_TRANSFORMATION_FALSETTO = 0x01030400,
+  /** A fuller voice.
+   */
+  TIMBRE_TRANSFORMATION_FULL = 0x01030500,
+  /** A clearer voice.
+   */
+  TIMBRE_TRANSFORMATION_CLEAR = 0x01030600,
+  /** A more resounding voice.
+   */
+  TIMBRE_TRANSFORMATION_RESOUNDING = 0x01030700,
+  /** A more ringing voice.
+   */
+  TIMBRE_TRANSFORMATION_RINGING = 0x01030800,
+  /**
+   * A ultra-high quality voice, which makes the audio clearer and restores more details.
+   * - To achieve better audio effect quality, Agora recommends that you call `setAudioProfile`
+   * and set the `profile` to `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)`
+   * and `scenario` to `AUDIO_SCENARIO_HIGH_DEFINITION(6)` before calling `setVoiceBeautifierPreset`.
+   * - If you have an audio capturing device that can already restore audio details to a high
+   * degree, Agora recommends that you do not enable ultra-high quality; otherwise, the SDK may
+   * over-restore audio details, and you may not hear the anticipated voice effect.
+   */
+  ULTRA_HIGH_QUALITY_VOICE = 0x01040100
+};
+
+/** Preset voice effects.
+ *
+ * For better voice effects, Agora recommends setting the `profile` parameter of `setAudioProfile` to `AUDIO_PROFILE_MUSIC_HIGH_QUALITY` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO` before using the following presets:
+ *
+ * - `ROOM_ACOUSTICS_KTV`
+ * - `ROOM_ACOUSTICS_VOCAL_CONCERT`
+ * - `ROOM_ACOUSTICS_STUDIO`
+ * - `ROOM_ACOUSTICS_PHONOGRAPH`
+ * - `ROOM_ACOUSTICS_SPACIAL`
+ * - `ROOM_ACOUSTICS_ETHEREAL`
+ * - `ROOM_ACOUSTICS_CHORUS`
+ * - `VOICE_CHANGER_EFFECT_UNCLE`
+ * - `VOICE_CHANGER_EFFECT_OLDMAN`
+ * - `VOICE_CHANGER_EFFECT_BOY`
+ * - `VOICE_CHANGER_EFFECT_SISTER`
+ * - `VOICE_CHANGER_EFFECT_GIRL`
+ * - `VOICE_CHANGER_EFFECT_PIGKING`
+ * - `VOICE_CHANGER_EFFECT_HULK`
+ * - `PITCH_CORRECTION`
+ */
+enum AUDIO_EFFECT_PRESET {
+  /** Turn off voice effects, that is, use the original voice.
+   */
+  AUDIO_EFFECT_OFF = 0x00000000,
+  /** The voice effect typical of a KTV venue.
+   */
+  ROOM_ACOUSTICS_KTV = 0x02010100,
+  /** The voice effect typical of a concert hall.
+   */
+  ROOM_ACOUSTICS_VOCAL_CONCERT = 0x02010200,
+  /** The voice effect typical of a recording studio.
+   */
+  ROOM_ACOUSTICS_STUDIO = 0x02010300,
+  /** The voice effect typical of a vintage phonograph.
+   */
+  ROOM_ACOUSTICS_PHONOGRAPH = 0x02010400,
+  /** The virtual stereo effect, which renders monophonic audio as stereo audio.
+   *
+   * @note Before using this preset, set the `profile` parameter of `setAudioProfile`
+   * to `AUDIO_PROFILE_MUSIC_STANDARD_STEREO(3)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)`;
+   * otherwise, the preset setting is invalid.
+   */
+  ROOM_ACOUSTICS_VIRTUAL_STEREO = 0x02010500,
+  /** A more spatial voice effect.
+   */
+  ROOM_ACOUSTICS_SPACIAL = 0x02010600,
+  /** A more ethereal voice effect.
+   */
+  ROOM_ACOUSTICS_ETHEREAL = 0x02010700,
+  /** A 3D voice effect that makes the voice appear to be moving around the user. The default cycle
+   * period of the 3D voice effect is 10 seconds. To change the cycle period, call `setAudioEffectParameters`
+   * after this method.
+   *
+   * @note
+   * - Before using this preset, set the `profile` parameter of `setAudioProfile` to
+   * `AUDIO_PROFILE_MUSIC_STANDARD_STEREO` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO`; otherwise,
+   * the preset setting is invalid.
+   * - If the 3D voice effect is enabled, users need to use stereo audio playback devices to hear
+   * the anticipated voice effect.
+   */
+  ROOM_ACOUSTICS_3D_VOICE = 0x02010800,
+  /** virtual suround sound.
+   *
+   * @note
+   * - Agora recommends using this enumerator to process virtual suround sound; otherwise, you may
+   * not hear the anticipated voice effect.
+   * - To achieve better audio effect quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `profile` parameter to
+   * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before
+   * setting this enumerator.
+   */
+  ROOM_ACOUSTICS_VIRTUAL_SURROUND_SOUND = 0x02010900,
+  /** The voice effect for chorus.
+   * 
+   * @note: To achieve better audio effect quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `profile` parameter to
+   * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before
+   * setting this enumerator.
+  */
+  ROOM_ACOUSTICS_CHORUS = 0x02010D00,
+  /** A middle-aged man's voice.
+   *
+   * @note
+   * Agora recommends using this enumerator to process a male-sounding voice; otherwise, you may
+   * not hear the anticipated voice effect.
+   */
+  VOICE_CHANGER_EFFECT_UNCLE = 0x02020100,
+  /** A senior man's voice.
+   *
+   * @note Agora recommends using this enumerator to process a male-sounding voice; otherwise, you may
+   * not hear the anticipated voice effect.
+   */
+  VOICE_CHANGER_EFFECT_OLDMAN = 0x02020200,
+  /** A boy's voice.
+   *
+   * @note Agora recommends using this enumerator to process a male-sounding voice; otherwise, you may
+   * not hear the anticipated voice effect.
+   */
+  VOICE_CHANGER_EFFECT_BOY = 0x02020300,
+  /** A young woman's voice.
+   *
+   * @note
+   * - Agora recommends using this enumerator to process a female-sounding voice; otherwise, you may
+   * not hear the anticipated voice effect.
+   */
+  VOICE_CHANGER_EFFECT_SISTER = 0x02020400,
+  /** A girl's voice.
+   *
+   * @note Agora recommends using this enumerator to process a female-sounding voice; otherwise, you may
+   * not hear the anticipated voice effect.
+   */
+  VOICE_CHANGER_EFFECT_GIRL = 0x02020500,
+  /** The voice of Pig King, a character in Journey to the West who has a voice like a growling
+   * bear.
+   */
+  VOICE_CHANGER_EFFECT_PIGKING = 0x02020600,
+  /** The Hulk's voice.
+   */
+  VOICE_CHANGER_EFFECT_HULK = 0x02020700,
+  /** An audio effect typical of R&B music.
+   *
+   * @note Before using this preset, set the `profile` parameter of `setAudioProfile` to
+   - `AUDIO_PROFILE_MUSIC_HIGH_QUALITY` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO`; otherwise,
+   * the preset setting is invalid.
+   */
+  STYLE_TRANSFORMATION_RNB = 0x02030100,
+  /** The voice effect typical of popular music.
+   *
+   * @note Before using this preset, set the `profile` parameter of `setAudioProfile` to
+   - `AUDIO_PROFILE_MUSIC_HIGH_QUALITY` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO`; otherwise,
+   * the preset setting is invalid.
+   */
+  STYLE_TRANSFORMATION_POPULAR = 0x02030200,
+  /** A pitch correction effect that corrects the user's pitch based on the pitch of the natural C
+  * major scale. After setting this voice effect, you can call `setAudioEffectParameters` to adjust
+  * the basic mode of tuning and the pitch of the main tone.
+   */
+  PITCH_CORRECTION = 0x02040100,
+
+  /** Todo:  Electronic sound, Magic tone haven't been implemented.
+   *
+   */
+};
+
+/** The options for SDK preset voice conversion.
+ */
+enum VOICE_CONVERSION_PRESET {
+  /** Turn off voice conversion and use the original voice.
+   */
+  VOICE_CONVERSION_OFF = 0x00000000,
+  /** A gender-neutral voice. To avoid audio distortion, ensure that you use this enumerator to process a female-sounding voice.
+   */
+  VOICE_CHANGER_NEUTRAL = 0x03010100,
+  /** A sweet voice. To avoid audio distortion, ensure that you use this enumerator to process a female-sounding voice.
+   */
+  VOICE_CHANGER_SWEET = 0x03010200,
+  /** A steady voice. To avoid audio distortion, ensure that you use this enumerator to process a male-sounding voice.
+   */
+  VOICE_CHANGER_SOLID = 0x03010300,
+  /** A deep voice. To avoid audio distortion, ensure that you use this enumerator to process a male-sounding voice.
+   */
+  VOICE_CHANGER_BASS = 0x03010400,
+  /** A voice like a cartoon character.
+   */
+  VOICE_CHANGER_CARTOON = 0x03010500,
+  /** A voice like a child.
+   */
+  VOICE_CHANGER_CHILDLIKE = 0x03010600,
+  /** A voice like a phone operator.
+   */
+  VOICE_CHANGER_PHONE_OPERATOR = 0x03010700,
+  /** A monster voice.
+   */
+  VOICE_CHANGER_MONSTER = 0x03010800,
+  /** A voice like Transformers.
+   */
+  VOICE_CHANGER_TRANSFORMERS = 0x03010900,
+  /** A voice like Groot.
+   */
+  VOICE_CHANGER_GROOT = 0x03010A00,
+  /** A voice like Darth Vader.
+   */
+  VOICE_CHANGER_DARTH_VADER = 0x03010B00,
+  /** A rough female voice.
+   */
+  VOICE_CHANGER_IRON_LADY = 0x03010C00,
+  /** A voice like Crayon Shin-chan.
+   */
+  VOICE_CHANGER_SHIN_CHAN = 0x03010D00,
+  /** A voice like a castrato.
+   */
+  VOICE_CHANGER_GIRLISH_MAN = 0x03010E00,
+  /** A voice like chipmunk.
+   */
+  VOICE_CHANGER_CHIPMUNK = 0x03010F00,
+
+};
+
+/** The options for SDK preset headphone equalizer.
+ */
+enum HEADPHONE_EQUALIZER_PRESET {
+  /** Turn off headphone EQ and use the original voice.
+   */
+  HEADPHONE_EQUALIZER_OFF = 0x00000000,
+  /** For over-ear headphones.
+   */
+  HEADPHONE_EQUALIZER_OVEREAR = 0x04000001,
+  /** For in-ear headphones.
+   */
+  HEADPHONE_EQUALIZER_INEAR = 0x04000002
+};
+
+/**
+ * Screen sharing configurations.
+ */
+struct ScreenCaptureParameters {
+  /**
+   * On Windows and macOS, it represents the video encoding resolution of the shared screen stream.
+   * See `VideoDimensions`. The default value is 1920 x 1080, that is, 2,073,600 pixels. Agora uses
+   * the value of this parameter to calculate the charges.
+   *
+   * If the aspect ratio is different between the encoding dimensions and screen dimensions, Agora
+   * applies the following algorithms for encoding. Suppose dimensions are 1920 x 1080:
+   * - If the value of the screen dimensions is lower than that of dimensions, for example,
+   * 1000 x 1000 pixels, the SDK uses 1000 x 1000 pixels for encoding.
+   * - If the value of the screen dimensions is higher than that of dimensions, for example,
+   * 2000 x 1500, the SDK uses the maximum value under dimensions with the aspect ratio of
+   * the screen dimension (4:3) for encoding, that is, 1440 x 1080.
+   */
+  VideoDimensions dimensions;
+  /**
+   * On Windows and macOS, it represents the video encoding frame rate (fps) of the shared screen stream.
+   * The frame rate (fps) of the shared region. The default value is 5. We do not recommend setting
+   * this to a value greater than 15.
+   */
+  int frameRate;
+  /**
+   * On Windows and macOS, it represents the video encoding bitrate of the shared screen stream.
+   * The bitrate (Kbps) of the shared region. The default value is 0 (the SDK works out a bitrate
+   * according to the dimensions of the current screen).
+   */
+  int bitrate;
+  /** Whether to capture the mouse in screen sharing:
+   * - `true`: (Default) Capture the mouse.
+   * - `false`: Do not capture the mouse.
+   */
+  bool captureMouseCursor;
+  /**
+   * Whether to bring the window to the front when calling the `startScreenCaptureByWindowId` method to share it:
+   * - `true`: Bring the window to the front.
+   * - `false`: (Default) Do not bring the window to the front.
+  */
+  bool windowFocus;
+  /**
+   * A list of IDs of windows to be blocked. When calling `startScreenCaptureByDisplayId` to start screen sharing,
+   * you can use this parameter to block a specified window. When calling `updateScreenCaptureParameters` to update
+   * screen sharing configurations, you can use this parameter to dynamically block the specified windows during
+   * screen sharing.
+   */
+  view_t *excludeWindowList;
+  /**
+   * The number of windows to be blocked.
+   */
+  int excludeWindowCount;
+
+  /** The width (px) of the border. Defaults to 0, and the value range is [0,50].
+    *
+    */
+  int highLightWidth;
+  /** The color of the border in RGBA format. The default value is 0xFF8CBF26.
+    *
+    */
+  unsigned int highLightColor;
+  /** Whether to place a border around the shared window or screen:
+    * - true: Place a border.
+    * - false: (Default) Do not place a border.
+    *
+    * @note When you share a part of a window or screen, the SDK places a border around the entire window or screen if you set `enableHighLight` as true.
+    *
+    */
+  bool enableHighLight;
+
+  ScreenCaptureParameters()
+    : dimensions(1920, 1080), frameRate(5), bitrate(STANDARD_BITRATE), captureMouseCursor(true), windowFocus(false), excludeWindowList(OPTIONAL_NULLPTR), excludeWindowCount(0), highLightWidth(0), highLightColor(0), enableHighLight(false)  {}
+  ScreenCaptureParameters(const VideoDimensions& d, int f, int b)
+    : dimensions(d), frameRate(f), bitrate(b), captureMouseCursor(true), windowFocus(false), excludeWindowList(OPTIONAL_NULLPTR), excludeWindowCount(0), highLightWidth(0), highLightColor(0), enableHighLight(false) {}
+  ScreenCaptureParameters(int width, int height, int f, int b)
+    : dimensions(width, height), frameRate(f), bitrate(b), captureMouseCursor(true), windowFocus(false), excludeWindowList(OPTIONAL_NULLPTR), excludeWindowCount(0), highLightWidth(0), highLightColor(0), enableHighLight(false){}
+  ScreenCaptureParameters(int width, int height, int f, int b, bool cur, bool fcs)
+    : dimensions(width, height), frameRate(f), bitrate(b), captureMouseCursor(cur), windowFocus(fcs), excludeWindowList(OPTIONAL_NULLPTR), excludeWindowCount(0), highLightWidth(0), highLightColor(0), enableHighLight(false) {}
+  ScreenCaptureParameters(int width, int height, int f, int b, view_t *ex, int cnt)
+    : dimensions(width, height), frameRate(f), bitrate(b), captureMouseCursor(true), windowFocus(false), excludeWindowList(ex), excludeWindowCount(cnt), highLightWidth(0), highLightColor(0), enableHighLight(false) {}
+  ScreenCaptureParameters(int width, int height, int f, int b, bool cur, bool fcs, view_t *ex, int cnt)
+    : dimensions(width, height), frameRate(f), bitrate(b), captureMouseCursor(cur), windowFocus(fcs), excludeWindowList(ex), excludeWindowCount(cnt), highLightWidth(0), highLightColor(0), enableHighLight(false) {}
+};
+
+/**
+ * Audio recording quality.
+ */
+enum AUDIO_RECORDING_QUALITY_TYPE {
+  /**
+   * 0: Low quality. The sample rate is 32 kHz, and the file size is around 1.2 MB after 10 minutes of recording.
+   */
+  AUDIO_RECORDING_QUALITY_LOW = 0,
+  /**
+   * 1: Medium quality. The sample rate is 32 kHz, and the file size is around 2 MB after 10 minutes of recording.
+   */
+  AUDIO_RECORDING_QUALITY_MEDIUM = 1,
+  /**
+   * 2: High quality. The sample rate is 32 kHz, and the file size is around 3.75 MB after 10 minutes of recording.
+   */
+  AUDIO_RECORDING_QUALITY_HIGH = 2,
+  /**
+   * 3: Ultra high audio recording quality.
+   */
+  AUDIO_RECORDING_QUALITY_ULTRA_HIGH = 3,
+};
+
+/**
+ * Recording content. Set in `startAudioRecording`.
+ */
+enum AUDIO_FILE_RECORDING_TYPE {
+  /**
+   * 1: Only records the audio of the local user.
+   */
+  AUDIO_FILE_RECORDING_MIC = 1,
+  /**
+   * 2: Only records the audio of all remote users.
+   */
+  AUDIO_FILE_RECORDING_PLAYBACK = 2,
+  /**
+   * 3: Records the mixed audio of the local and all remote users.
+   */
+  AUDIO_FILE_RECORDING_MIXED = 3,
+};
+
+/**
+ * Audio encoded frame observer position.
+ */
+enum AUDIO_ENCODED_FRAME_OBSERVER_POSITION {
+  /**
+  * 1: Only records the audio of the local user.
+  */
+  AUDIO_ENCODED_FRAME_OBSERVER_POSITION_RECORD = 1,
+  /**
+  * 2: Only records the audio of all remote users.
+  */
+  AUDIO_ENCODED_FRAME_OBSERVER_POSITION_PLAYBACK = 2,
+  /**
+  * 3: Records the mixed audio of the local and all remote users.
+  */
+  AUDIO_ENCODED_FRAME_OBSERVER_POSITION_MIXED = 3,
+};
+
+/**
+ * Recording configuration.
+ */
+struct AudioRecordingConfiguration {
+  /**
+   * The absolute path (including the filename extensions) of the recording file. For example: `C:\music\audio.mp4`.
+   * @note Ensure that the directory for the log files exists and is writable.
+   */
+  const char* filePath;
+  /**
+   * Whether to encode the audio data:
+   * - `true`: Encode audio data in AAC.
+   * - `false`: (Default) Do not encode audio data, but save the recorded audio data directly.
+   */
+  bool encode;
+  /**
+   * Recording sample rate (Hz).
+   * - 16000
+   * - (Default) 32000
+   * - 44100
+   * - 48000
+   * @note If you set this parameter to 44100 or 48000, Agora recommends recording WAV files, or AAC files with quality
+   * to be `AUDIO_RECORDING_QUALITY_MEDIUM` or `AUDIO_RECORDING_QUALITY_HIGH` for better recording quality.
+   */
+  int sampleRate;
+  /**
+   * The recording content. See `AUDIO_FILE_RECORDING_TYPE`.
+   */
+  AUDIO_FILE_RECORDING_TYPE fileRecordingType;
+  /**
+   * Recording quality. See `AUDIO_RECORDING_QUALITY_TYPE`.
+   * @note This parameter applies to AAC files only.
+   */
+  AUDIO_RECORDING_QUALITY_TYPE quality;
+
+  /**
+   * Recording channel. The following values are supported:
+   * - (Default) 1
+   * - 2
+   */
+  int recordingChannel;
+
+  AudioRecordingConfiguration()
+    : filePath(OPTIONAL_NULLPTR),
+      encode(false),
+      sampleRate(32000),
+      fileRecordingType(AUDIO_FILE_RECORDING_MIXED),
+      quality(AUDIO_RECORDING_QUALITY_LOW),
+      recordingChannel(1) {}
+
+  AudioRecordingConfiguration(const char* file_path, int sample_rate, AUDIO_RECORDING_QUALITY_TYPE quality_type, int channel)
+    : filePath(file_path),
+      encode(false),
+      sampleRate(sample_rate),
+      fileRecordingType(AUDIO_FILE_RECORDING_MIXED),
+      quality(quality_type),
+      recordingChannel(channel) {}
+
+  AudioRecordingConfiguration(const char* file_path, bool enc, int sample_rate, AUDIO_FILE_RECORDING_TYPE type, AUDIO_RECORDING_QUALITY_TYPE quality_type, int channel)
+    : filePath(file_path),
+      encode(enc),
+      sampleRate(sample_rate),
+      fileRecordingType(type),
+      quality(quality_type),
+      recordingChannel(channel) {}
+
+  AudioRecordingConfiguration(const AudioRecordingConfiguration &rhs)
+    : filePath(rhs.filePath),
+      encode(rhs.encode),
+      sampleRate(rhs.sampleRate),
+      fileRecordingType(rhs.fileRecordingType),
+      quality(rhs.quality),
+      recordingChannel(rhs.recordingChannel) {}
+};
+
+/**
+ * Observer settings for the encoded audio.
+ */
+struct AudioEncodedFrameObserverConfig {
+    /**
+     * Audio profile. For details, see `AUDIO_ENCODED_FRAME_OBSERVER_POSITION`.
+     */
+    AUDIO_ENCODED_FRAME_OBSERVER_POSITION postionType;
+    /**
+     * Audio encoding type. For details, see `AUDIO_ENCODING_TYPE`.
+     */
+    AUDIO_ENCODING_TYPE encodingType;
+
+    AudioEncodedFrameObserverConfig()
+    : postionType(AUDIO_ENCODED_FRAME_OBSERVER_POSITION_PLAYBACK),
+      encodingType(AUDIO_ENCODING_TYPE_OPUS_48000_MEDIUM){}
+
+};
+/**
+ * The encoded audio observer.
+ */
+class IAudioEncodedFrameObserver {
+public:
+/**
+* Gets the encoded audio data of the local user.
+*
+* After calling `registerAudioEncodedFrameObserver` and setting the encoded audio as `AUDIO_ENCODED_FRAME_OBSERVER_POSITION_RECORD`,
+* you can get the encoded audio data of the local user from this callback.
+*
+* @param frameBuffer The pointer to the audio frame buffer.
+* @param length The data length (byte) of the audio frame.
+* @param audioEncodedFrameInfo Audio information after encoding. For details, see `EncodedAudioFrameInfo`.
+*/
+virtual void onRecordAudioEncodedFrame(const uint8_t* frameBuffer,  int length, const EncodedAudioFrameInfo& audioEncodedFrameInfo) = 0;
+
+/**
+* Gets the encoded audio data of all remote users.
+*
+* After calling `registerAudioEncodedFrameObserver` and setting the encoded audio as `AUDIO_ENCODED_FRAME_OBSERVER_POSITION_PLAYBACK`,
+* you can get encoded audio data of all remote users through this callback.
+*
+* @param frameBuffer The pointer to the audio frame buffer.
+* @param length The data length (byte) of the audio frame.
+* @param audioEncodedFrameInfo Audio information after encoding. For details, see `EncodedAudioFrameInfo`.
+*/
+virtual void onPlaybackAudioEncodedFrame(const uint8_t* frameBuffer,  int length, const EncodedAudioFrameInfo& audioEncodedFrameInfo) = 0;
+
+/**
+* Gets the mixed and encoded audio data of the local and all remote users.
+*
+* After calling `registerAudioEncodedFrameObserver` and setting the audio profile as `AUDIO_ENCODED_FRAME_OBSERVER_POSITION_MIXED`,
+* you can get the mixed and encoded audio data of the local and all remote users through this callback.
+*
+* @param frameBuffer The pointer to the audio frame buffer.
+* @param length The data length (byte) of the audio frame.
+* @param audioEncodedFrameInfo Audio information after encoding. For details, see `EncodedAudioFrameInfo`.
+*/
+virtual void onMixedAudioEncodedFrame(const uint8_t* frameBuffer,  int length, const EncodedAudioFrameInfo& audioEncodedFrameInfo) = 0;
+
+virtual ~IAudioEncodedFrameObserver () {}
+};
+
+/** The region for connection, which is the region where the server the SDK connects to is located.
+ */
+enum AREA_CODE {
+    /**
+     * Mainland China.
+     */
+    AREA_CODE_CN = 0x00000001,
+    /**
+     * North America.
+     */
+    AREA_CODE_NA = 0x00000002,
+    /**
+     * Europe.
+     */
+    AREA_CODE_EU = 0x00000004,
+    /**
+     * Asia, excluding Mainland China.
+     */
+    AREA_CODE_AS = 0x00000008,
+    /**
+     * Japan.
+     */
+    AREA_CODE_JP = 0x00000010,
+    /**
+     * India.
+     */
+    AREA_CODE_IN = 0x00000020,
+    /**
+     * (Default) Global.
+     */
+    AREA_CODE_GLOB = (0xFFFFFFFF)
+};
+
+/**
+  Extra region code
+  @technical preview
+*/
+enum AREA_CODE_EX {
+    /**
+     * Oceania
+    */
+    AREA_CODE_OC = 0x00000040,
+    /**
+     * South-American
+    */
+    AREA_CODE_SA = 0x00000080,
+    /**
+     * Africa
+    */
+    AREA_CODE_AF = 0x00000100,
+    /**
+     * South Korea
+     */
+    AREA_CODE_KR = 0x00000200,
+    /**
+     * Hong Kong and Macou
+     */
+    AREA_CODE_HKMC = 0x00000400,
+    /**
+     * United States
+     */
+    AREA_CODE_US = 0x00000800,
+    /**
+     * Russia
+     */
+    AREA_CODE_RU = 0x00001000,
+    /**
+     * The global area (except China)
+     */
+    AREA_CODE_OVS = 0xFFFFFFFE
+};
+
+/**
+ * The error code of the channel media replay.
+ */
+enum CHANNEL_MEDIA_RELAY_ERROR {
+  /** 0: No error.
+   */
+  RELAY_OK = 0,
+  /** 1: An error occurs in the server response.
+   */
+  RELAY_ERROR_SERVER_ERROR_RESPONSE = 1,
+  /** 2: No server response. You can call the `leaveChannel` method to leave the channel.
+   *
+   * This error can also occur if your project has not enabled co-host token authentication. You can contact technical
+   * support to enable the service for cohosting across channels before starting a channel media relay.
+   */
+  RELAY_ERROR_SERVER_NO_RESPONSE = 2,
+  /** 3: The SDK fails to access the service, probably due to limited resources of the server.
+   */
+  RELAY_ERROR_NO_RESOURCE_AVAILABLE = 3,
+  /** 4: Fails to send the relay request.
+   */
+  RELAY_ERROR_FAILED_JOIN_SRC = 4,
+  /** 5: Fails to accept the relay request.
+   */
+  RELAY_ERROR_FAILED_JOIN_DEST = 5,
+  /** 6: The server fails to receive the media stream.
+   */
+  RELAY_ERROR_FAILED_PACKET_RECEIVED_FROM_SRC = 6,
+  /** 7: The server fails to send the media stream.
+   */
+  RELAY_ERROR_FAILED_PACKET_SENT_TO_DEST = 7,
+  /** 8: The SDK disconnects from the server due to poor network connections. You can call the `leaveChannel` method to
+   * leave the channel.
+   */
+  RELAY_ERROR_SERVER_CONNECTION_LOST = 8,
+  /** 9: An internal error occurs in the server.
+   */
+  RELAY_ERROR_INTERNAL_ERROR = 9,
+  /** 10: The token of the source channel has expired.
+   */
+  RELAY_ERROR_SRC_TOKEN_EXPIRED = 10,
+  /** 11: The token of the destination channel has expired.
+   */
+  RELAY_ERROR_DEST_TOKEN_EXPIRED = 11,
+};
+
+/**
+ * The state code of the channel media relay.
+ */
+enum CHANNEL_MEDIA_RELAY_STATE {
+  /** 0: The initial state. After you successfully stop the channel media relay by calling `stopChannelMediaRelay`,
+   * the `onChannelMediaRelayStateChanged` callback returns this state.
+   */
+  RELAY_STATE_IDLE = 0,
+  /** 1: The SDK tries to relay the media stream to the destination channel.
+   */
+  RELAY_STATE_CONNECTING = 1,
+  /** 2: The SDK successfully relays the media stream to the destination channel.
+   */
+  RELAY_STATE_RUNNING = 2,
+  /** 3: An error occurs. See `code` in `onChannelMediaRelayStateChanged` for the error code.
+   */
+  RELAY_STATE_FAILURE = 3,
+};
+
+/** The definition of ChannelMediaInfo.
+ */
+struct ChannelMediaInfo {
+  /** The user ID.
+     */
+  uid_t uid;
+  /** The channel name. The default value is NULL, which means that the SDK
+    * applies the current channel name.
+    */
+  const char* channelName;
+  /** The token that enables the user to join the channel. The default value
+    * is NULL, which means that the SDK applies the current token.
+    */
+  const char* token;
+
+  ChannelMediaInfo() : uid(0), channelName(NULL), token(NULL) {}
+  ChannelMediaInfo(const char* c, const char* t, uid_t u) : uid(u), channelName(c), token(t) {}
+};
+
+/** The definition of ChannelMediaRelayConfiguration.
+ */
+struct ChannelMediaRelayConfiguration {
+  /** The information of the source channel `ChannelMediaInfo`. It contains the following members:
+   * - `channelName`: The name of the source channel. The default value is `NULL`, which means the SDK applies the name
+   * of the current channel.
+   * - `uid`: The unique ID to identify the relay stream in the source channel. The default value is 0, which means the
+   * SDK generates a random UID. You must set it as 0.
+   * - `token`: The token for joining the source channel. It is generated with the `channelName` and `uid` you set in
+   * `srcInfo`.
+   *   - If you have not enabled the App Certificate, set this parameter as the default value `NULL`, which means the
+   * SDK applies the App ID.
+   *   - If you have enabled the App Certificate, you must use the token generated with the `channelName` and `uid`, and
+   * the `uid` must be set as 0.
+   */
+  ChannelMediaInfo* srcInfo;
+  /** The information of the destination channel `ChannelMediaInfo`. It contains the following members:
+   * - `channelName`: The name of the destination channel.
+   * - `uid`: The unique ID to identify the relay stream in the destination channel. The value
+   * ranges from 0 to (2^32-1). To avoid UID conflicts, this `UID` must be different from any
+   * other `UID` in the destination channel. The default value is 0, which means the SDK generates
+   * a random `UID`. Do not set this parameter as the `UID` of the host in the destination channel,
+   * and ensure that this `UID` is different from any other `UID` in the channel.
+   * - `token`: The token for joining the destination channel. It is generated with the `channelName`
+   * and `uid` you set in `destInfos`.
+   *   - If you have not enabled the App Certificate, set this parameter as the default value NULL,
+   * which means the SDK applies the App ID.
+   * If you have enabled the App Certificate, you must use the token generated with the `channelName`
+   * and `uid`.
+   */
+  ChannelMediaInfo* destInfos;
+  /** The number of destination channels. The default value is 0, and the value range is from 0 to
+   * 6. Ensure that the value of this parameter corresponds to the number of `ChannelMediaInfo`
+   * structs you define in `destInfo`.
+   */
+  int destCount;
+
+  ChannelMediaRelayConfiguration() : srcInfo(OPTIONAL_NULLPTR), destInfos(OPTIONAL_NULLPTR), destCount(0) {}
+};
+
+/**
+ * The uplink network information.
+ */
+struct UplinkNetworkInfo {
+  /**
+   * The target video encoder bitrate (bps).
+   */
+  int video_encoder_target_bitrate_bps;
+
+  UplinkNetworkInfo() : video_encoder_target_bitrate_bps(0) {}
+
+  bool operator==(const UplinkNetworkInfo& rhs) const {
+    return (video_encoder_target_bitrate_bps == rhs.video_encoder_target_bitrate_bps);
+  }
+};
+
+struct DownlinkNetworkInfo {
+  struct PeerDownlinkInfo {
+    /**
+     * The ID of the user who owns the remote video stream.
+     */
+    const char* userId;
+    /**
+     * The remote video stream type: #VIDEO_STREAM_TYPE.
+     */
+    VIDEO_STREAM_TYPE stream_type;
+    /**
+     * The remote video downscale type: #REMOTE_VIDEO_DOWNSCALE_LEVEL.
+     */
+    REMOTE_VIDEO_DOWNSCALE_LEVEL current_downscale_level;
+    /**
+     * The expected bitrate in bps.
+     */
+    int expected_bitrate_bps;
+
+    PeerDownlinkInfo()
+        : userId(OPTIONAL_NULLPTR),
+          stream_type(VIDEO_STREAM_HIGH),
+          current_downscale_level(REMOTE_VIDEO_DOWNSCALE_LEVEL_NONE),
+          expected_bitrate_bps(-1) {}
+
+    PeerDownlinkInfo(const PeerDownlinkInfo& rhs)
+         : stream_type(rhs.stream_type),
+          current_downscale_level(rhs.current_downscale_level),
+          expected_bitrate_bps(rhs.expected_bitrate_bps) {
+      if (rhs.userId != OPTIONAL_NULLPTR) {
+        const int len = std::strlen(rhs.userId);
+        char* buf = new char[len + 1];
+        std::memcpy(buf, rhs.userId, len);
+        buf[len] = '\0';
+        userId = buf;
+      }
+    }
+
+    PeerDownlinkInfo& operator=(const PeerDownlinkInfo& rhs) {
+      if (this == &rhs) return *this;
+      userId = OPTIONAL_NULLPTR;
+      stream_type = rhs.stream_type;
+      current_downscale_level = rhs.current_downscale_level;
+      expected_bitrate_bps = rhs.expected_bitrate_bps;
+      if (rhs.userId != OPTIONAL_NULLPTR) {
+        const int len = std::strlen(rhs.userId);
+        char* buf = new char[len + 1];
+        std::memcpy(buf, rhs.userId, len);
+        buf[len] = '\0';
+        userId = buf;
+      }
+      return *this;
+    }
+
+    ~PeerDownlinkInfo() { delete[] userId; }
+  };
+
+  /**
+   * The lastmile buffer delay queue time in ms.
+   */
+  int lastmile_buffer_delay_time_ms;
+  /**
+   * The current downlink bandwidth estimation(bps) after downscale.
+   */
+  int bandwidth_estimation_bps;
+  /**
+   * The total video downscale level count.
+   */
+  int total_downscale_level_count;
+  /**
+   * The peer video downlink info array.
+   */
+  PeerDownlinkInfo* peer_downlink_info;
+  /**
+   * The total video received count.
+   */
+  int total_received_video_count;
+
+  DownlinkNetworkInfo()
+    : lastmile_buffer_delay_time_ms(-1),
+      bandwidth_estimation_bps(-1),
+      total_downscale_level_count(-1),
+      peer_downlink_info(OPTIONAL_NULLPTR),
+      total_received_video_count(-1) {}
+
+  DownlinkNetworkInfo(const DownlinkNetworkInfo& info)
+    : lastmile_buffer_delay_time_ms(info.lastmile_buffer_delay_time_ms),
+      bandwidth_estimation_bps(info.bandwidth_estimation_bps),
+      total_downscale_level_count(info.total_downscale_level_count),
+      peer_downlink_info(OPTIONAL_NULLPTR),
+      total_received_video_count(info.total_received_video_count) {
+    if (total_received_video_count <= 0) return;
+    peer_downlink_info = new PeerDownlinkInfo[total_received_video_count];
+    for (int i = 0; i < total_received_video_count; ++i)
+      peer_downlink_info[i] = info.peer_downlink_info[i];
+  }
+
+  DownlinkNetworkInfo& operator=(const DownlinkNetworkInfo& rhs) {
+    if (this == &rhs) return *this;
+    lastmile_buffer_delay_time_ms = rhs.lastmile_buffer_delay_time_ms;
+    bandwidth_estimation_bps = rhs.bandwidth_estimation_bps;
+    total_downscale_level_count = rhs.total_downscale_level_count;
+    peer_downlink_info = OPTIONAL_NULLPTR;
+    total_received_video_count = rhs.total_received_video_count;
+    if (total_received_video_count > 0) {
+      peer_downlink_info = new PeerDownlinkInfo[total_received_video_count];
+      for (int i = 0; i < total_received_video_count; ++i)
+        peer_downlink_info[i] = rhs.peer_downlink_info[i];
+    }
+    return *this;
+  }
+
+  ~DownlinkNetworkInfo() { delete[] peer_downlink_info; }
+};
+
+/**
+ * The built-in encryption mode.
+ *
+ * Agora recommends using AES_128_GCM2 or AES_256_GCM2 encrypted mode. These two modes support the
+ * use of salt for higher security.
+ */
+enum ENCRYPTION_MODE {
+  /** 1: 128-bit AES encryption, XTS mode.
+   */
+  AES_128_XTS = 1,
+  /** 2: 128-bit AES encryption, ECB mode.
+   */
+  AES_128_ECB = 2,
+  /** 3: 256-bit AES encryption, XTS mode.
+   */
+  AES_256_XTS = 3,
+  /** 4: 128-bit SM4 encryption, ECB mode.
+   */
+  SM4_128_ECB = 4,
+  /** 5: 128-bit AES encryption, GCM mode.
+   */
+  AES_128_GCM = 5,
+  /** 6: 256-bit AES encryption, GCM mode.
+   */
+  AES_256_GCM = 6,
+  /** 7: (Default) 128-bit AES encryption, GCM mode. This encryption mode requires the setting of
+   * salt (`encryptionKdfSalt`).
+   */
+  AES_128_GCM2 = 7,
+  /** 8: 256-bit AES encryption, GCM mode. This encryption mode requires the setting of salt (`encryptionKdfSalt`).
+   */
+  AES_256_GCM2 = 8,
+  /** Enumerator boundary.
+   */
+  MODE_END,
+};
+
+/** Built-in encryption configurations. */
+struct EncryptionConfig {
+  /**
+   * The built-in encryption mode. See #ENCRYPTION_MODE. Agora recommends using `AES_128_GCM2`
+   * or `AES_256_GCM2` encrypted mode. These two modes support the use of salt for higher security.
+   */
+  ENCRYPTION_MODE encryptionMode;
+  /**
+   * Encryption key in string type with unlimited length. Agora recommends using a 32-byte key.
+   *
+   * @note If you do not set an encryption key or set it as NULL, you cannot use the built-in encryption, and the SDK returns #ERR_INVALID_ARGUMENT (-2).
+   */
+  const char* encryptionKey;
+  /**
+   * Salt, 32 bytes in length. Agora recommends that you use OpenSSL to generate salt on the server side.
+   *
+   * @note This parameter takes effect only in `AES_128_GCM2` or `AES_256_GCM2` encrypted mode.
+   * In this case, ensure that this parameter is not 0.
+   */
+  uint8_t encryptionKdfSalt[32];
+    
+  bool datastreamEncryptionEnabled;
+
+  EncryptionConfig()
+    : encryptionMode(AES_128_GCM2),
+      encryptionKey(OPTIONAL_NULLPTR),
+      datastreamEncryptionEnabled(false)
+  {
+    memset(encryptionKdfSalt, 0, sizeof(encryptionKdfSalt));
+  }
+
+  /// @cond
+  const char* getEncryptionString() const {
+    switch(encryptionMode) {
+      case AES_128_XTS:
+        return "aes-128-xts";
+      case AES_128_ECB:
+        return "aes-128-ecb";
+      case AES_256_XTS:
+        return "aes-256-xts";
+      case SM4_128_ECB:
+        return "sm4-128-ecb";
+      case AES_128_GCM:
+        return "aes-128-gcm";
+      case AES_256_GCM:
+        return "aes-256-gcm";
+      case AES_128_GCM2:
+        return "aes-128-gcm-2";
+      case AES_256_GCM2:
+        return "aes-256-gcm-2";
+      default:
+        return "aes-128-gcm-2";
+    }
+    return "aes-128-gcm-2";
+  }
+  /// @endcond
+};
+
+/** Encryption error type.
+ */
+enum ENCRYPTION_ERROR_TYPE {
+    /**
+     * 0: Internal reason.
+     */
+    ENCRYPTION_ERROR_INTERNAL_FAILURE = 0,
+    /**
+     * 1: MediaStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key.
+     */
+    ENCRYPTION_ERROR_DECRYPTION_FAILURE = 1,
+    /**
+     * 2: MediaStream encryption errors.
+     */
+    ENCRYPTION_ERROR_ENCRYPTION_FAILURE = 2,
+    /**
+     * 3: DataStream decryption errors. Ensure that the receiver and the sender use the same encryption mode and key.
+     */
+    ENCRYPTION_ERROR_DATASTREAM_DECRYPTION_FAILURE = 3,
+    /**
+     * 4: DataStream encryption errors.
+     */
+    ENCRYPTION_ERROR_DATASTREAM_ENCRYPTION_FAILURE = 4,
+};
+
+enum UPLOAD_ERROR_REASON
+{
+  UPLOAD_SUCCESS = 0,
+  UPLOAD_NET_ERROR = 1,
+  UPLOAD_SERVER_ERROR = 2,
+};
+
+/** The type of the device permission.
+ */
+enum PERMISSION_TYPE {
+  /**
+   * 0: Permission for the audio capture device.
+   */
+  RECORD_AUDIO = 0,
+  /**
+   * 1: Permission for the camera.
+   */
+  CAMERA = 1,
+
+  SCREEN_CAPTURE = 2,
+};
+
+/**
+ * The subscribing state.
+ */
+enum STREAM_SUBSCRIBE_STATE {
+  /**
+   * 0: The initial subscribing state after joining the channel.
+   */
+  SUB_STATE_IDLE = 0,
+  /**
+   * 1: Fails to subscribe to the remote stream. Possible reasons:
+   * - The remote user:
+   *   - Calls `muteLocalAudioStream(true)` or `muteLocalVideoStream(true)` to stop sending local
+   * media stream.
+   *   - Calls `disableAudio` or `disableVideo `to disable the local audio or video module.
+   *   - Calls `enableLocalAudio(false)` or `enableLocalVideo(false)` to disable the local audio or video capture.
+   *   - The role of the remote user is audience.
+   * - The local user calls the following methods to stop receiving remote streams:
+   *   - Calls `muteRemoteAudioStream(true)`, `muteAllRemoteAudioStreams(true)` or `setDefaultMuteAllRemoteAudioStreams(true)` to stop receiving the remote audio streams.
+   *   - Calls `muteRemoteVideoStream(true)`, `muteAllRemoteVideoStreams(true)` or `setDefaultMuteAllRemoteVideoStreams(true)` to stop receiving the remote video streams.
+   */
+  SUB_STATE_NO_SUBSCRIBED = 1,
+  /**
+   * 2: Subscribing.
+   */
+  SUB_STATE_SUBSCRIBING = 2,
+  /**
+   * 3: Subscribes to and receives the remote stream successfully.
+   */
+  SUB_STATE_SUBSCRIBED = 3
+};
+
+/**
+ * The publishing state.
+ */
+enum STREAM_PUBLISH_STATE {
+  /**
+   * 0: The initial publishing state after joining the channel.
+   */
+  PUB_STATE_IDLE = 0,
+  /**
+   * 1: Fails to publish the local stream. Possible reasons:
+   * - The local user calls `muteLocalAudioStream(true)` or `muteLocalVideoStream(true)` to stop sending the local media stream.
+   * - The local user calls `disableAudio` or `disableVideo` to disable the local audio or video module.
+   * - The local user calls `enableLocalAudio(false)` or `enableLocalVideo(false)` to disable the local audio or video capture.
+   * - The role of the local user is audience.
+   */
+  PUB_STATE_NO_PUBLISHED = 1,
+  /**
+   * 2: Publishing.
+   */
+  PUB_STATE_PUBLISHING = 2,
+  /**
+   * 3: Publishes successfully.
+   */
+  PUB_STATE_PUBLISHED = 3
+};
+
+/**
+ * The EchoTestConfiguration struct.
+ */
+struct EchoTestConfiguration {
+  view_t view;
+  bool enableAudio;
+  bool enableVideo;
+  const char* token;
+  const char* channelId;
+  int intervalInSeconds;
+
+  EchoTestConfiguration(view_t v, bool ea, bool ev, const char* t, const char* c, const int is)
+   : view(v), enableAudio(ea), enableVideo(ev), token(t), channelId(c), intervalInSeconds(is) {}
+
+  EchoTestConfiguration()
+   : view(OPTIONAL_NULLPTR), enableAudio(true), enableVideo(true), token(OPTIONAL_NULLPTR), channelId(OPTIONAL_NULLPTR), intervalInSeconds(2) {}
+};
+
+/**
+ * The information of the user.
+ */
+struct UserInfo {
+  /**
+   * The user ID.
+   */
+  uid_t uid;
+  /**
+   * The user account. The maximum data length is `MAX_USER_ACCOUNT_LENGTH_TYPE`.
+   */
+  char userAccount[MAX_USER_ACCOUNT_LENGTH];
+
+  UserInfo() : uid(0) {
+    userAccount[0] = '\0';
+  }
+};
+
+/**
+ * The audio filter of in-ear monitoring.
+ */
+enum EAR_MONITORING_FILTER_TYPE {
+  /**
+   * 1: Do not add an audio filter to the in-ear monitor.
+   */
+  EAR_MONITORING_FILTER_NONE = (1<<0),
+  /**
+   * 2: Enable audio filters to the in-ear monitor. If you implement functions such as voice
+   * beautifier and audio effect, users can hear the voice after adding these effects.
+   */
+  EAR_MONITORING_FILTER_BUILT_IN_AUDIO_FILTERS = (1<<1),
+  /**
+   * 4: Enable noise suppression to the in-ear monitor.
+   */
+  EAR_MONITORING_FILTER_NOISE_SUPPRESSION = (1<<2),
+  /**
+   * 32768: Enable audio filters by reuse post-processing filter to the in-ear monitor.
+   * This bit is intended to be used in exclusive mode, which means, if this bit is set, all other bits will be disregarded.
+   */
+  EAR_MONITORING_FILTER_REUSE_POST_PROCESSING_FILTER = (1<<15),
+};
+
+/**
+ * Thread priority type.
+ */
+enum THREAD_PRIORITY_TYPE {
+  /**
+   * 0: Lowest priority.
+   */
+  LOWEST = 0,
+  /**
+   * 1: Low priority.
+   */
+  LOW = 1,
+  /**
+   * 2: Normal priority.
+   */
+  NORMAL = 2,
+  /**
+   * 3: High priority.
+   */
+  HIGH = 3,
+  /**
+   * 4. Highest priority.
+   */
+  HIGHEST = 4,
+  /**
+   * 5. Critical priority.
+   */
+  CRITICAL = 5,
+};
+
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+
+/**
+ * The video configuration for the shared screen stream.
+ */
+struct ScreenVideoParameters {
+  /**
+   * The dimensions of the video encoding resolution. The default value is `1280` x `720`.
+   * For recommended values, see [Recommended video
+   * profiles](https://docs.agora.io/en/Interactive%20Broadcast/game_streaming_video_profile?platform=Android#recommended-video-profiles).
+   * If the aspect ratio is different between width and height and the screen, the SDK adjusts the
+   * video encoding resolution according to the following rules (using an example where `width` ×
+   * `height` is 1280 × 720):
+   * - When the width and height of the screen are both lower than `width` and `height`, the SDK
+   * uses the resolution of the screen for video encoding. For example, if the screen is 640 ×
+   * 360, The SDK uses 640 × 360 for video encoding.
+   * - When either the width or height of the screen is higher than `width` or `height`, the SDK
+   * uses the maximum values that do not exceed those of `width` and `height` while maintaining
+   * the aspect ratio of the screen for video encoding. For example, if the screen is 2000 × 1500,
+   * the SDK uses 960 × 720 for video encoding.
+   *
+   * @note
+   * - The billing of the screen sharing stream is based on the values of width and height.
+   * When you do not pass in these values, Agora bills you at 1280 × 720;
+   * when you pass in these values, Agora bills you at those values.
+   * For details, see [Pricing for Real-time
+   * Communication](https://docs.agora.io/en/Interactive%20Broadcast/billing_rtc).
+   * - This value does not indicate the orientation mode of the output ratio.
+   * For how to set the video orientation, see `ORIENTATION_MODE`.
+   * - Whether the SDK can support a resolution at 720P depends on the performance of the device.
+   * If you set 720P but the device cannot support it, the video frame rate can be lower.
+   */
+  VideoDimensions dimensions;
+  /**
+   * The video encoding frame rate (fps). The default value is `15`.
+   * For recommended values, see [Recommended video
+   * profiles](https://docs.agora.io/en/Interactive%20Broadcast/game_streaming_video_profile?platform=Android#recommended-video-profiles).
+   */
+  int frameRate = 15;
+   /**
+   * The video encoding bitrate (Kbps). For recommended values, see [Recommended video
+   * profiles](https://docs.agora.io/en/Interactive%20Broadcast/game_streaming_video_profile?platform=Android#recommended-video-profiles).
+   */
+  int bitrate;
+  /*
+   * The content hint of the screen sharing:
+   */
+  VIDEO_CONTENT_HINT contentHint = VIDEO_CONTENT_HINT::CONTENT_HINT_MOTION;
+
+  ScreenVideoParameters() : dimensions(1280, 720) {}
+};
+
+/**
+ * The audio configuration for the shared screen stream.
+ */
+struct ScreenAudioParameters {
+  /**
+   * The audio sample rate (Hz). The default value is `16000`.
+   */
+  int sampleRate = 16000;
+  /**
+   * The number of audio channels. The default value is `2`, indicating dual channels.
+   */
+  int channels = 2;
+  /**
+   * The volume of the captured system audio. The value range is [0,100]. The default value is
+   * `100`.
+   */
+  int captureSignalVolume = 100;
+};
+
+/**
+ * The configuration of the screen sharing
+ */
+struct ScreenCaptureParameters2 {
+  /**
+   * Determines whether to capture system audio during screen sharing:
+   * - `true`: Capture.
+   * - `false`: (Default)  Do not capture.
+   *
+   * **Note**
+   * Due to system limitations, capturing system audio is only available for Android API level 29
+   * and later (that is, Android 10 and later).
+   */
+  bool captureAudio = false;
+  /**
+   * The audio configuration for the shared screen stream.
+   */
+  ScreenAudioParameters audioParams;
+  /**
+   * Determines whether to capture the screen during screen sharing:
+   * - `true`: (Default) Capture.
+   * - `false`: Do not capture.
+   *
+   * **Note**
+   * Due to system limitations, screen capture is only available for Android API level 21 and later
+   * (that is, Android 5 and later).
+   */
+  bool captureVideo = true;
+  /**
+   * The video configuration for the shared screen stream.
+   */
+  ScreenVideoParameters videoParams;
+};
+#endif
+
+/**
+ * The tracing event of media rendering.
+ */
+enum MEDIA_TRACE_EVENT {
+  /**
+   * 0: The media frame has been rendered.
+   */
+  MEDIA_TRACE_EVENT_VIDEO_RENDERED = 0,
+  /**
+   * 1: The media frame has been decoded.
+   */
+  MEDIA_TRACE_EVENT_VIDEO_DECODED,
+};
+
+/**
+ * The video rendering tracing result
+ */
+struct VideoRenderingTracingInfo {
+  /**
+   * Elapsed time from the start tracing time to the time when the tracing event occurred.
+   */
+  int elapsedTime;
+  /**
+   * Elapsed time from the start tracing time to the time when join channel.
+   * 
+   * **Note**
+   * If the start tracing time is behind the time when join channel, this value will be negative.
+   */
+  int start2JoinChannel;
+  /**
+   * Elapsed time from joining channel to finishing joining channel.
+   */
+  int join2JoinSuccess;
+  /**
+   * Elapsed time from finishing joining channel to remote user joined.
+   * 
+   * **Note**
+   * If the start tracing time is after the time finishing join channel, this value will be
+   * the elapsed time from the start tracing time to remote user joined. The minimum value is 0.
+   */
+  int joinSuccess2RemoteJoined;
+  /**
+   * Elapsed time from remote user joined to set the view.
+   * 
+   * **Note**
+   * If the start tracing time is after the time when remote user joined, this value will be
+   * the elapsed time from the start tracing time to set the view. The minimum value is 0.
+   */
+  int remoteJoined2SetView;
+  /**
+   * Elapsed time from remote user joined to the time subscribing remote video stream.
+   * 
+   * **Note**
+   * If the start tracing time is after the time when remote user joined, this value will be
+   * the elapsed time from the start tracing time to the time subscribing remote video stream.
+   * The minimum value is 0.
+   */
+  int remoteJoined2UnmuteVideo;
+  /**
+   * Elapsed time from remote user joined to the remote video packet received.
+   * 
+   * **Note**
+   * If the start tracing time is after the time when remote user joined, this value will be
+   * the elapsed time from the start tracing time to the time subscribing remote video stream.
+   * The minimum value is 0.
+   */
+  int remoteJoined2PacketReceived;
+};
+
+enum CONFIG_FETCH_TYPE {
+  /**
+   * 1: Fetch config when initializing RtcEngine, without channel info.
+   */
+  CONFIG_FETCH_TYPE_INITIALIZE = 1,
+  /**
+   * 2: Fetch config when joining channel with channel info, such as channel name and uid.
+   */
+  CONFIG_FETCH_TYPE_JOIN_CHANNEL = 2,
+};
+
+
+/** The local  proxy mode type. */
+enum LOCAL_PROXY_MODE {
+  /** 0: Connect local proxy with high priority, if not connected to local proxy, fallback to sdrtn.
+   */
+  ConnectivityFirst = 0,
+  /** 1: Only connect local proxy
+   */
+  LocalOnly = 1,
+};
+
+struct LogUploadServerInfo {
+  /** Log upload server domain
+   */
+  const char* serverDomain;
+  /** Log upload server path
+   */
+  const char* serverPath;
+  /** Log upload server port
+   */
+  int serverPort;
+  /** Whether to use HTTPS request:
+    - true: Use HTTPS request
+    - fasle: Use HTTP request
+   */
+  bool serverHttps;
+
+  LogUploadServerInfo() : serverDomain(NULL), serverPath(NULL), serverPort(0), serverHttps(true) {}
+
+  LogUploadServerInfo(const char* domain, const char* path, int port, bool https) : serverDomain(domain), serverPath(path), serverPort(port), serverHttps(https) {}
+};
+
+struct AdvancedConfigInfo {
+  /** Log upload server
+   */
+  LogUploadServerInfo logUploadServer;
+};
+
+struct LocalAccessPointConfiguration {
+  /** Local access point IP address list.
+   */
+  const char** ipList;
+  /** The number of local access point IP address.
+   */
+  int ipListSize;
+  /** Local access point domain list.
+   */
+  const char** domainList;
+  /** The number of local access point domain.
+   */
+  int domainListSize;
+  /** Certificate domain name installed on specific local access point. pass "" means using sni domain on specific local access point
+   *  SNI(Server Name Indication) is an extension to the TLS protocol.
+   */
+  const char* verifyDomainName;
+  /** Local proxy connection mode, connectivity first or local only.
+   */
+  LOCAL_PROXY_MODE mode;
+  /** Local proxy connection, advanced Config info.
+   */
+  AdvancedConfigInfo advancedConfig;
+  /**
+    * Whether to disable vos-aut:
+    - true: (Default)disable vos-aut.
+    - false: not disable vos-aut
+  */
+  bool disableAut;
+  LocalAccessPointConfiguration() : ipList(NULL), ipListSize(0), domainList(NULL), domainListSize(0), verifyDomainName(NULL), mode(ConnectivityFirst), disableAut(true) {}
+};
+
+/**
+ * The information about recorded media streams.
+ */
+struct RecorderStreamInfo {
+    const char* channelId;
+    /**
+     * The user ID.
+     */
+    uid_t uid;
+    /**
+     * The channel ID of the audio/video stream needs to be recorded.
+     */
+    RecorderStreamInfo() : channelId(NULL), uid(0) {}
+    RecorderStreamInfo(const char* channelId, uid_t uid) : channelId(channelId), uid(uid) {}
+};
+}  // namespace rtc
+
+namespace base {
+
+class IEngineBase {
+ public:
+  virtual int queryInterface(rtc::INTERFACE_ID_TYPE iid, void** inter) = 0;
+  virtual ~IEngineBase() {}
+};
+
+class AParameter : public agora::util::AutoPtr<IAgoraParameter> {
+ public:
+  AParameter(IEngineBase& engine) { initialize(&engine); }
+  AParameter(IEngineBase* engine) { initialize(engine); }
+  AParameter(IAgoraParameter* p) : agora::util::AutoPtr<IAgoraParameter>(p) {}
+
+ private:
+  bool initialize(IEngineBase* engine) {
+    IAgoraParameter* p = OPTIONAL_NULLPTR;
+    if (engine && !engine->queryInterface(rtc::AGORA_IID_PARAMETER_ENGINE, (void**)&p)) reset(p);
+    return p != OPTIONAL_NULLPTR;
+  }
+};
+
+class LicenseCallback {
+  public:
+    virtual ~LicenseCallback() {}
+    virtual void onCertificateRequired() = 0;
+    virtual void onLicenseRequest() = 0;
+    virtual void onLicenseValidated() = 0;
+    virtual void onLicenseError(int result) = 0;
+};
+
+}  // namespace base
+
+/**
+ * Spatial audio parameters
+ */
+struct SpatialAudioParams {
+  /**
+   * Speaker azimuth in a spherical coordinate system centered on the listener.
+   */
+  Optional<double> speaker_azimuth;
+  /**
+   * Speaker elevation in a spherical coordinate system centered on the listener.
+   */
+  Optional<double> speaker_elevation;
+  /**
+   * Distance between speaker and listener.
+   */
+  Optional<double> speaker_distance;
+  /**
+   * Speaker orientation [0-180], 0 degree is the same with listener orientation.
+   */
+  Optional<int> speaker_orientation;
+  /**
+   * Enable blur or not for the speaker.
+   */
+  Optional<bool> enable_blur;
+  /**
+   * Enable air absorb or not for the speaker.
+   */
+  Optional<bool> enable_air_absorb;
+  /**
+   * Speaker attenuation factor.
+   */
+  Optional<double> speaker_attenuation;
+  /**
+   * Enable doppler factor.
+   */
+  Optional<bool> enable_doppler;
+};
+/**
+ * Layout info of video stream which compose a transcoder video stream.
+*/
+struct VideoLayout
+{
+  /**
+   * Channel Id from which this video stream come from.
+  */
+  const char* channelId;
+  /**
+   * User id of video stream.
+  */
+  rtc::uid_t uid;
+  /**
+   * User account of video stream.
+  */
+  user_id_t strUid;
+  /**
+   * x coordinate of video stream on a transcoded video stream canvas.
+  */
+  uint32_t x;
+  /**
+   * y coordinate of video stream on a transcoded video stream canvas.
+  */
+  uint32_t y;
+  /**
+   * width of video stream on a transcoded video stream canvas.
+  */
+  uint32_t width;
+  /**
+   * height of video stream on a transcoded video stream canvas.
+  */
+  uint32_t height;
+  /**
+   * video state  of video stream on a transcoded video stream canvas.
+   * 0 for normal video , 1 for placeholder image showed , 2 for black image.
+  */ 
+  uint32_t videoState; 
+
+  VideoLayout() : channelId(OPTIONAL_NULLPTR), uid(0), strUid(OPTIONAL_NULLPTR), x(0), y(0), width(0), height(0), videoState(0) {}
+};
+}  // namespace agora
+
+/**
+ * Gets the version of the SDK.
+ * @param [out] build The build number of Agora SDK.
+ * @return The string of the version of the SDK.
+ */
+AGORA_API const char* AGORA_CALL getAgoraSdkVersion(int* build);
+
+/**
+ * Gets error description of an error code.
+ * @param [in] err The error code.
+ * @return The description of the error code.
+ */
+AGORA_API const char* AGORA_CALL getAgoraSdkErrorDescription(int err);
+
+AGORA_API int AGORA_CALL setAgoraSdkExternalSymbolLoader(void* (*func)(const char* symname));
+
+/**
+ * Generate credential
+ * @param [in, out] credential The content of the credential.
+ * @return The description of the error code.
+ * @note For license only, everytime will generate a different credential.
+ * So, just need to call once for a device, and then save the credential
+ */
+AGORA_API int AGORA_CALL createAgoraCredential(agora::util::AString &credential);
+
+/**
+ * Verify given certificate and return the result
+ * When you receive onCertificateRequired event, you must validate the certificate by calling
+ * this function. This is sync call, and if validation is success, it will return ERR_OK. And
+ * if failed to pass validation, you won't be able to joinChannel and ERR_CERT_FAIL will be
+ * returned.
+ * @param [in] credential_buf pointer to the credential's content.
+ * @param [in] credential_len the length of the credential's content.
+ * @param [in] certificate_buf pointer to the certificate's content.
+ * @param [in] certificate_len the length of the certificate's content.
+ * @return The description of the error code.
+ * @note For license only.
+ */
+AGORA_API int AGORA_CALL getAgoraCertificateVerifyResult(const char *credential_buf, int credential_len,
+    const char *certificate_buf, int certificate_len);
+
+/**
+ * @brief Implement the agora::base::LicenseCallback,
+ * create a LicenseCallback object to receive callbacks of license.
+ *
+ * @param [in] callback The object of agora::LiceseCallback,
+ *                      set the callback to null before delete it.
+ */
+AGORA_API void setAgoraLicenseCallback(agora::base::LicenseCallback *callback);
+
+/**
+ * @brief Get the LicenseCallback pointer if already setup,
+ *  otherwise, return null.
+ *
+ * @return a pointer of agora::base::LicenseCallback
+ */
+
+AGORA_API agora::base::LicenseCallback* getAgoraLicenseCallback();
+
+/*
+ * Get monotonic time in ms which can be used by capture time,
+ * typical scenario is as follows:
+ *
+ *  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *  |  // custom audio/video base capture time, e.g. the first audio/video capture time.             |
+ *  |  int64_t custom_capture_time_base;                                                             |
+ *  |                                                                                                |
+ *  |  int64_t agora_monotonic_time = getAgoraCurrentMonotonicTimeInMs();                            |
+ *  |                                                                                                |
+ *  |  // offset is fixed once calculated in the begining.                                           |
+ *  |  const int64_t offset = agora_monotonic_time - custom_capture_time_base;                       |
+ *  |                                                                                                |
+ *  |  // realtime_custom_audio/video_capture_time is the origin capture time that customer provided.|
+ *  |  // actual_audio/video_capture_time is the actual capture time transfered to sdk.              |
+ *  |  int64_t actual_audio_capture_time = realtime_custom_audio_capture_time + offset;              |
+ *  |  int64_t actual_video_capture_time = realtime_custom_video_capture_time + offset;              |
+ *  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * @return
+ * - >= 0: Success.
+ * - < 0: Failure.
+ */
+AGORA_API int64_t AGORA_CALL getAgoraCurrentMonotonicTimeInMs();
diff --git a/headers/rtc_4.3.2.11/include/AgoraMediaBase.h b/headers/rtc_4.3.2.11/include/AgoraMediaBase.h
new file mode 100644
index 0000000..defdab3
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/AgoraMediaBase.h
@@ -0,0 +1,1746 @@
+//  Agora Engine SDK
+//
+//  Created by Sting Feng in 2017-11.
+//  Copyright (c) 2017 Agora.io. All rights reserved.
+
+#pragma once  // NOLINT(build/header_guard)
+
+#include <cstring>
+#include <stdint.h>
+#include <limits>
+#include <stddef.h>
+
+#ifndef OPTIONAL_ENUM_SIZE_T
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_ENUM_SIZE_T enum : size_t
+#else
+#define OPTIONAL_ENUM_SIZE_T enum
+#endif
+#endif
+
+#if !defined(__APPLE__)
+#define __deprecated
+#endif
+
+namespace agora {
+namespace rtc {
+
+typedef unsigned int uid_t;
+typedef unsigned int track_id_t;
+typedef unsigned int conn_id_t;
+typedef unsigned int video_track_id_t;
+
+static const unsigned int INVALID_TRACK_ID = 0xffffffff;
+static const unsigned int DEFAULT_CONNECTION_ID = 0;
+static const unsigned int DUMMY_CONNECTION_ID = (std::numeric_limits<unsigned int>::max)();
+
+struct EncodedVideoFrameInfo;
+
+/**
+* Video source types definition.
+**/
+enum VIDEO_SOURCE_TYPE {
+  /** Video captured by the camera.
+   */
+  VIDEO_SOURCE_CAMERA_PRIMARY = 0,
+  VIDEO_SOURCE_CAMERA = VIDEO_SOURCE_CAMERA_PRIMARY,
+  /** Video captured by the secondary camera.
+   */
+  VIDEO_SOURCE_CAMERA_SECONDARY = 1,
+  /** Video for screen sharing.
+   */
+  VIDEO_SOURCE_SCREEN_PRIMARY = 2,
+  VIDEO_SOURCE_SCREEN = VIDEO_SOURCE_SCREEN_PRIMARY,
+  /** Video for secondary screen sharing.
+   */
+  VIDEO_SOURCE_SCREEN_SECONDARY = 3,
+  /** Not define.
+   */
+  VIDEO_SOURCE_CUSTOM = 4,
+  /** Video for media player sharing.
+   */
+  VIDEO_SOURCE_MEDIA_PLAYER = 5,
+  /** Video for png image.
+   */
+  VIDEO_SOURCE_RTC_IMAGE_PNG = 6,
+  /** Video for png image.
+   */
+  VIDEO_SOURCE_RTC_IMAGE_JPEG = 7,
+  /** Video for png image.
+   */
+  VIDEO_SOURCE_RTC_IMAGE_GIF = 8,
+  /** Remote video received from network.
+   */
+  VIDEO_SOURCE_REMOTE = 9,
+  /** Video for transcoded.
+   */
+  VIDEO_SOURCE_TRANSCODED = 10,
+
+  /** Video captured by the third camera.
+   */
+  VIDEO_SOURCE_CAMERA_THIRD = 11,
+  /** Video captured by the fourth camera.
+   */
+  VIDEO_SOURCE_CAMERA_FOURTH = 12,
+  /** Video for third screen sharing.
+   */
+  VIDEO_SOURCE_SCREEN_THIRD = 13,
+  /** Video for fourth screen sharing.
+   */
+  VIDEO_SOURCE_SCREEN_FOURTH = 14,
+  /** Video for voice drive.
+  */
+ VIDEO_SOURCE_SPEECH_DRIVEN = 15,
+
+  VIDEO_SOURCE_UNKNOWN = 100
+};
+
+/**
+ * Audio routes.
+ */
+enum AudioRoute
+{
+  /**
+   * -1: The default audio route.
+   */
+  ROUTE_DEFAULT = -1,
+  /**
+   * The Headset.
+   */
+  ROUTE_HEADSET = 0,
+  /**
+   * The Earpiece.
+   */
+  ROUTE_EARPIECE = 1,
+  /**
+   * The Headset with no microphone.
+   */
+  ROUTE_HEADSETNOMIC = 2,
+  /**
+   * The Speakerphone.
+   */
+  ROUTE_SPEAKERPHONE = 3,
+  /**
+   * The Loudspeaker.
+   */
+  ROUTE_LOUDSPEAKER = 4,
+  /**
+   * The Bluetooth Device via HFP.
+   */
+  ROUTE_BLUETOOTH_DEVICE_HFP = 5,
+  /**
+   * The USB.
+   */
+  ROUTE_USB = 6,
+  /**
+   * The HDMI.
+   */
+  ROUTE_HDMI = 7,
+  /**
+   * The DisplayPort.
+   */
+  ROUTE_DISPLAYPORT = 8,
+  /**
+   * The AirPlay.
+   */
+  ROUTE_AIRPLAY = 9,
+  /**
+   * The Bluetooth Device via A2DP.
+   */
+  ROUTE_BLUETOOTH_DEVICE_A2DP = 10,
+};
+
+/**
+ * Bytes per sample
+ */
+enum BYTES_PER_SAMPLE {
+  /**
+   * two bytes per sample
+   */
+  TWO_BYTES_PER_SAMPLE = 2,
+};
+
+struct AudioParameters {
+  int sample_rate;
+  size_t channels;
+  size_t frames_per_buffer;
+
+  AudioParameters()
+      : sample_rate(0),
+        channels(0),
+        frames_per_buffer(0) {}
+};
+
+/**
+ * The use mode of the audio data.
+ */
+enum RAW_AUDIO_FRAME_OP_MODE_TYPE {
+  /** 0: Read-only mode: Users only read the data from `AudioFrame` without modifying anything. 
+   * For example, when users acquire the data with the Agora SDK, then start the media push.
+   */
+  RAW_AUDIO_FRAME_OP_MODE_READ_ONLY = 0,
+
+  /** 2: Read and write mode: Users read the data from `AudioFrame`, modify it, and then play it. 
+   * For example, when users have their own audio-effect processing module and perform some voice pre-processing, such as a voice change.
+   */
+  RAW_AUDIO_FRAME_OP_MODE_READ_WRITE = 2,
+};
+
+}  // namespace rtc
+
+namespace media {
+  /**
+ * The type of media device.
+ */
+enum MEDIA_SOURCE_TYPE {
+  /**
+   * 0: The audio playback device.
+   */
+  AUDIO_PLAYOUT_SOURCE = 0,
+  /**
+   * 1: Microphone.
+   */
+  AUDIO_RECORDING_SOURCE = 1,
+  /**
+   * 2: Video captured by primary camera.
+   */
+  PRIMARY_CAMERA_SOURCE = 2,
+  /**
+   * 3: Video captured by secondary camera.
+   */
+  SECONDARY_CAMERA_SOURCE = 3,
+  /**
+   * 4: Video captured by primary screen capturer.
+   */
+  PRIMARY_SCREEN_SOURCE = 4,
+  /**
+   * 5: Video captured by secondary screen capturer.
+   */
+  SECONDARY_SCREEN_SOURCE = 5,
+  /**
+   * 6: Video captured by custom video source.
+   */
+  CUSTOM_VIDEO_SOURCE = 6,
+  /**
+   * 7: Video for media player sharing.
+   */
+  MEDIA_PLAYER_SOURCE = 7,
+  /**
+   * 8: Video for png image.
+   */
+  RTC_IMAGE_PNG_SOURCE = 8,
+  /**
+   * 9: Video for jpeg image.
+   */
+  RTC_IMAGE_JPEG_SOURCE = 9,
+  /**
+   * 10: Video for gif image.
+   */
+  RTC_IMAGE_GIF_SOURCE = 10,
+  /**
+   * 11: Remote video received from network.
+   */
+  REMOTE_VIDEO_SOURCE = 11,
+  /**
+   * 12: Video for transcoded.
+   */
+  TRANSCODED_VIDEO_SOURCE = 12,
+  /**
+   * 13: Video for voice drive.
+   */
+  SPEECH_DRIVEN_VIDEO_SOURCE = 13,
+  /**
+   * 100: Internal Usage only.
+   */
+  UNKNOWN_MEDIA_SOURCE = 100
+};
+
+namespace base {
+
+typedef void* view_t;
+
+typedef const char* user_id_t;
+
+static const uint8_t kMaxCodecNameLength = 50;
+
+/**
+ * The definition of the PacketOptions struct, which contains infomation of the packet
+ * in the RTP (Real-time Transport Protocal) header.
+ */
+struct PacketOptions {
+  /**
+   * The timestamp of the packet.
+   */
+  uint32_t timestamp;
+  // Audio level indication.
+  uint8_t audioLevelIndication;
+  PacketOptions()
+      : timestamp(0),
+        audioLevelIndication(127) {}
+};
+
+/**
+ * The detailed information of the incoming audio encoded frame.
+ */
+
+struct AudioEncodedFrameInfo {
+  /**
+   * The send time of the packet.
+   */
+  uint64_t sendTs;
+  /**
+   * The codec of the packet.
+   */
+  uint8_t codec;
+  AudioEncodedFrameInfo()
+      : sendTs(0),
+        codec(0) {}
+};
+
+/**
+ * The detailed information of the incoming audio frame in the PCM format.
+ */
+struct AudioPcmFrame {
+  /**
+   * The buffer size of the PCM audio frame.
+   */
+  OPTIONAL_ENUM_SIZE_T {
+    // Stereo, 32 kHz, 60 ms (2 * 32 * 60)
+    /**
+     * The max number of the samples of the data.
+     *
+     * When the number of audio channel is two, the sample rate is 32 kHZ,
+     * the buffer length of the data is 60 ms, the number of the samples of the data is 3840 (2 x 32 x 60).
+     */
+    kMaxDataSizeSamples = 3840,
+    /** The max number of the bytes of the data. */
+    kMaxDataSizeBytes = kMaxDataSizeSamples * sizeof(int16_t),
+  };
+
+  /** The timestamp (ms) of the audio frame.
+   */
+  int64_t capture_timestamp;
+  /** The number of samples per channel.
+   */
+  size_t samples_per_channel_;
+  /** The sample rate (Hz) of the audio data.
+   */
+  int sample_rate_hz_;
+  /** The channel number.
+   */
+  size_t num_channels_;
+  /**  The audio track number. if mpk enableMultiAudioTrack, audio frame will have audio track number, eg 0 or 1.
+   */
+  int audio_track_number_;
+  /** The number of bytes per sample.
+   */
+  rtc::BYTES_PER_SAMPLE bytes_per_sample;
+  /** The audio frame data. */
+  int16_t data_[kMaxDataSizeSamples];
+
+  AudioPcmFrame& operator=(const AudioPcmFrame& src) {
+    if(this == &src) {
+      return *this;
+    }
+
+    this->capture_timestamp = src.capture_timestamp;
+    this->samples_per_channel_ = src.samples_per_channel_;
+    this->sample_rate_hz_ = src.sample_rate_hz_;
+    this->bytes_per_sample = src.bytes_per_sample;
+    this->num_channels_ = src.num_channels_;
+    this->audio_track_number_ = src.audio_track_number_;
+
+    size_t length = src.samples_per_channel_ * src.num_channels_;
+    if (length > kMaxDataSizeSamples) {
+      length = kMaxDataSizeSamples;
+    }
+
+    memcpy(this->data_, src.data_, length * sizeof(int16_t));
+
+    return *this;
+  }
+
+  AudioPcmFrame()
+      : capture_timestamp(0),
+        samples_per_channel_(0),
+        sample_rate_hz_(0),
+        num_channels_(0),
+        audio_track_number_(0),
+        bytes_per_sample(rtc::TWO_BYTES_PER_SAMPLE) {
+    memset(data_, 0, sizeof(data_));
+  }
+
+  AudioPcmFrame(const AudioPcmFrame& src)
+      : capture_timestamp(src.capture_timestamp),
+        samples_per_channel_(src.samples_per_channel_),
+        sample_rate_hz_(src.sample_rate_hz_),
+        num_channels_(src.num_channels_),
+        audio_track_number_(src.audio_track_number_),
+        bytes_per_sample(src.bytes_per_sample) {
+    size_t length = src.samples_per_channel_ * src.num_channels_;
+    if (length > kMaxDataSizeSamples) {
+      length = kMaxDataSizeSamples;
+    }
+
+    memcpy(this->data_, src.data_, length * sizeof(int16_t));
+  }
+};
+
+/** Audio dual-mono output mode
+ */
+enum AUDIO_DUAL_MONO_MODE {
+  /**< ChanLOut=ChanLin, ChanRout=ChanRin */
+  AUDIO_DUAL_MONO_STEREO = 0,
+  /**< ChanLOut=ChanRout=ChanLin */
+  AUDIO_DUAL_MONO_L = 1,
+  /**< ChanLOut=ChanRout=ChanRin */
+  AUDIO_DUAL_MONO_R = 2,
+  /**< ChanLout=ChanRout=(ChanLin+ChanRin)/2 */
+  AUDIO_DUAL_MONO_MIX = 3
+};
+
+/**
+ * Video pixel formats.
+ */
+enum VIDEO_PIXEL_FORMAT {
+  /**
+   * 0: Default format.
+   */
+  VIDEO_PIXEL_DEFAULT = 0,
+  /**
+   * 1: I420.
+   */
+  VIDEO_PIXEL_I420 = 1,
+  /**
+   * 2: BGRA.
+   */
+  VIDEO_PIXEL_BGRA = 2,
+  /**
+   * 3: NV21.
+   */
+  VIDEO_PIXEL_NV21 = 3,
+  /**
+   * 4: RGBA.
+   */
+  VIDEO_PIXEL_RGBA = 4,
+  /**
+   * 8: NV12.
+   */
+  VIDEO_PIXEL_NV12 = 8,
+  /**
+   * 10: GL_TEXTURE_2D
+   */
+  VIDEO_TEXTURE_2D = 10,
+  /**
+   * 11: GL_TEXTURE_OES
+   */
+  VIDEO_TEXTURE_OES = 11,
+  /*
+  12: pixel format for iOS CVPixelBuffer NV12
+  */
+  VIDEO_CVPIXEL_NV12 = 12,
+  /*
+  13: pixel format for iOS CVPixelBuffer I420
+  */
+  VIDEO_CVPIXEL_I420 = 13,
+  /*
+  14: pixel format for iOS CVPixelBuffer BGRA
+  */
+  VIDEO_CVPIXEL_BGRA = 14,
+  /**
+   * 16: I422.
+   */
+  VIDEO_PIXEL_I422 = 16,
+  /**
+   * 17: ID3D11Texture2D, only support DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_NV12 texture format
+   */
+  VIDEO_TEXTURE_ID3D11TEXTURE2D = 17,
+  /**
+   * 18: I010. 10bit I420 data.
+   * @technical preview
+   */
+  VIDEO_PIXEL_I010 = 18,
+};
+
+/**
+ * The video display mode.
+ */
+enum RENDER_MODE_TYPE {
+  /**
+   * 1: Uniformly scale the video until it fills the visible boundaries
+   * (cropped). One dimension of the video may have clipped contents.
+   */
+  RENDER_MODE_HIDDEN = 1,
+  /**
+   * 2: Uniformly scale the video until one of its dimension fits the boundary
+   * (zoomed to fit). Areas that are not filled due to the disparity in the
+   * aspect ratio will be filled with black.
+   */
+  RENDER_MODE_FIT = 2,
+  /**
+   * @deprecated
+   * 3: This mode is deprecated.
+   */
+  RENDER_MODE_ADAPTIVE __deprecated = 3,
+};
+
+/**
+ * The camera video source type
+ */
+enum CAMERA_VIDEO_SOURCE_TYPE {
+  /**
+   * 0: the video frame comes from the front camera
+   */
+  CAMERA_SOURCE_FRONT = 0,
+  /**
+   * 1: the video frame comes from the back camera
+   */
+  CAMERA_SOURCE_BACK = 1,
+  /**
+   * 1: the video frame source is unsepcified
+   */
+  VIDEO_SOURCE_UNSPECIFIED = 2,
+};
+
+/**
+ * The IVideoFrameMetaInfo class.
+ * This interface provides access to metadata information.
+ */
+class IVideoFrameMetaInfo {
+  public:
+    enum META_INFO_KEY {
+      KEY_FACE_CAPTURE = 0,
+    };
+    virtual ~IVideoFrameMetaInfo() {};
+    virtual const char* getMetaInfoStr(META_INFO_KEY key) const = 0;
+};
+
+/**
+ * The definition of the ExternalVideoFrame struct.
+ */
+struct ExternalVideoFrame {
+  ExternalVideoFrame()
+      : type(VIDEO_BUFFER_RAW_DATA),
+        format(VIDEO_PIXEL_DEFAULT),
+        buffer(NULL),
+        stride(0),
+        height(0),
+        cropLeft(0),
+        cropTop(0),
+        cropRight(0),
+        cropBottom(0),
+        rotation(0),
+        timestamp(0),
+        eglContext(NULL),
+        eglType(EGL_CONTEXT10),
+        textureId(0),
+        metadata_buffer(NULL),
+        metadata_size(0),
+        alphaBuffer(NULL),
+        fillAlphaBuffer(false),
+        d3d11_texture_2d(NULL),
+        texture_slice_index(0){}
+
+   /**
+   * The EGL context type.
+   */
+  enum EGL_CONTEXT_TYPE {
+    /**
+     * 0: When using the OpenGL interface (javax.microedition.khronos.egl.*) defined by Khronos
+     */
+    EGL_CONTEXT10 = 0,
+    /**
+     * 0: When using the OpenGL interface (android.opengl.*) defined by Android
+     */
+    EGL_CONTEXT14 = 1,
+  };
+
+  /**
+   * Video buffer types.
+   */
+  enum VIDEO_BUFFER_TYPE {
+    /**
+     * 1: Raw data.
+     */
+    VIDEO_BUFFER_RAW_DATA = 1,
+    /**
+     * 2: The same as VIDEO_BUFFER_RAW_DATA.
+     */
+    VIDEO_BUFFER_ARRAY = 2,
+    /**
+     * 3: The video buffer in the format of texture.
+     */
+    VIDEO_BUFFER_TEXTURE = 3,
+  };
+
+  /**
+   * The buffer type: #VIDEO_BUFFER_TYPE.
+   */
+  VIDEO_BUFFER_TYPE type;
+  /**
+   * The pixel format: #VIDEO_PIXEL_FORMAT
+   */
+  VIDEO_PIXEL_FORMAT format;
+  /**
+   * The video buffer.
+   */
+  void* buffer;
+  /**
+   * The line spacing of the incoming video frame (px). For
+   * texture, it is the width of the texture.
+   */
+  int stride;
+  /**
+   * The height of the incoming video frame.
+   */
+  int height;
+  /**
+   * [Raw data related parameter] The number of pixels trimmed from the left. The default value is
+   * 0.
+   */
+  int cropLeft;
+  /**
+   * [Raw data related parameter] The number of pixels trimmed from the top. The default value is
+   * 0.
+   */
+  int cropTop;
+  /**
+   * [Raw data related parameter] The number of pixels trimmed from the right. The default value is
+   * 0.
+   */
+  int cropRight;
+  /**
+   * [Raw data related parameter] The number of pixels trimmed from the bottom. The default value
+   * is 0.
+   */
+  int cropBottom;
+  /**
+   * [Raw data related parameter] The clockwise rotation information of the video frame. You can set the
+   * rotation angle as 0, 90, 180, or 270. The default value is 0.
+   */
+  int rotation;
+  /**
+   * The timestamp (ms) of the incoming video frame. An incorrect timestamp results in a frame loss or
+   * unsynchronized audio and video.
+   * 
+   * Please refer to getAgoraCurrentMonotonicTimeInMs or getCurrentMonotonicTimeInMs
+   * to determine how to fill this filed.
+   */
+  long long timestamp;
+  /**
+   * [Texture-related parameter]
+   * When using the OpenGL interface (javax.microedition.khronos.egl.*) defined by Khronos, set EGLContext to this field.
+   * When using the OpenGL interface (android.opengl.*) defined by Android, set EGLContext to this field.
+   */
+  void *eglContext;
+  /**
+   * [Texture related parameter] Texture ID used by the video frame.
+   */
+  EGL_CONTEXT_TYPE eglType;
+  /**
+   * [Texture related parameter] Incoming 4 &times; 4 transformational matrix. The typical value is a unit matrix.
+   */
+  int textureId;
+  /**
+   * [Texture related parameter] Incoming 4 &times; 4 transformational matrix. The typical value is a unit matrix.
+   */
+  float matrix[16];
+  /**
+   * [Texture related parameter] The MetaData buffer.
+   *  The default value is NULL
+   */
+  uint8_t* metadata_buffer;
+  /**
+   * [Texture related parameter] The MetaData size.
+   *  The default value is 0
+   */
+  int metadata_size;
+  /**
+   *  Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame.
+   *  The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground.
+   *  The default value is NULL.
+   *  @technical preview
+   */
+  uint8_t* alphaBuffer;
+  /**
+   *  Extract alphaBuffer from bgra or rgba data. Set it true if you do not explicitly specify the alphabuffer.
+   *  The default value is false
+   *  @technical preview
+   */
+  bool fillAlphaBuffer;
+
+  /**
+   * [For Windows only] The pointer of ID3D11Texture2D used by the video frame.
+   */
+  void *d3d11_texture_2d;
+
+  /**
+   * [For Windows only] The index of ID3D11Texture2D array used by the video frame.
+   */
+  int texture_slice_index;
+};
+
+/**
+ * The definition of the VideoFrame struct.
+ */
+struct VideoFrame {
+  VideoFrame():
+  type(VIDEO_PIXEL_DEFAULT),
+  width(0),
+  height(0),
+  yStride(0),
+  uStride(0),
+  vStride(0),
+  yBuffer(NULL),
+  uBuffer(NULL),
+  vBuffer(NULL),
+  rotation(0),
+  renderTimeMs(0),
+  avsync_type(0),
+  metadata_buffer(NULL),
+  metadata_size(0),
+  sharedContext(0),
+  textureId(0),
+  d3d11Texture2d(NULL),
+  alphaBuffer(NULL),
+  pixelBuffer(NULL),
+  metaInfo(NULL){
+    memset(matrix, 0, sizeof(matrix));
+  }
+  /**
+   * The video pixel format: #VIDEO_PIXEL_FORMAT.
+   */
+  VIDEO_PIXEL_FORMAT type;
+  /**
+   * The width of the video frame.
+   */
+  int width;
+  /**
+   * The height of the video frame.
+   */
+  int height;
+  /**
+   * The line span of Y buffer in the YUV data.
+   */
+  int yStride;
+  /**
+   * The line span of U buffer in the YUV data.
+   */
+  int uStride;
+  /**
+   * The line span of V buffer in the YUV data.
+   */
+  int vStride;
+  /**
+   * The pointer to the Y buffer in the YUV data.
+   */
+  uint8_t* yBuffer;
+  /**
+   * The pointer to the U buffer in the YUV data.
+   */
+  uint8_t* uBuffer;
+  /**
+   * The pointer to the V buffer in the YUV data.
+   */
+  uint8_t* vBuffer;
+  /**
+   * The clockwise rotation information of this frame. You can set it as 0, 90, 180 or 270.
+   */
+  int rotation;
+  /**
+   * The timestamp to render the video stream. Use this parameter for audio-video synchronization when
+   * rendering the video.
+   *
+   * @note This parameter is for rendering the video, not capturing the video.
+   */
+  int64_t renderTimeMs;
+  /**
+   * The type of audio-video synchronization.
+   */
+  int avsync_type;
+  /**
+   * [Texture related parameter] The MetaData buffer.
+   *  The default value is NULL
+   */
+  uint8_t* metadata_buffer;
+  /**
+   * [Texture related parameter] The MetaData size.
+   *  The default value is 0
+   */
+  int metadata_size;
+  /**
+   * [Texture related parameter], egl context.
+   */
+  void* sharedContext;
+  /**
+   * [Texture related parameter], Texture ID used by the video frame.
+   */
+  int textureId;
+  /**
+   * [Texture related parameter] The pointer of ID3D11Texture2D used by the video frame,for Windows only.
+   */
+  void* d3d11Texture2d;
+  /**
+   * [Texture related parameter], Incoming 4 &times; 4 transformational matrix.
+   */
+  float matrix[16];
+  /**
+   *  Indicates the alpha channel of current frame, which is consistent with the dimension of the video frame.
+   *  The value range of each pixel is [0,255], where 0 represents the background; 255 represents the foreground.
+   *  The default value is NULL.
+   *  @technical preview
+   */
+  uint8_t* alphaBuffer;
+  /**
+   *The type of CVPixelBufferRef, for iOS and macOS only.
+   */
+  void* pixelBuffer;
+  /**
+   *  The pointer to IVideoFrameMetaInfo, which is the interface to get metainfo contents from VideoFrame. 
+   */
+  IVideoFrameMetaInfo* metaInfo;
+};
+
+/**
+ * The IVideoFrameObserver class.
+ */
+class IVideoFrameObserver {
+ public:
+  /**
+   * Occurs each time the player receives a video frame.
+   *
+   * After registering the video frame observer,
+   * the callback occurs each time the player receives a video frame to report the detailed information of the video frame.
+   * @param frame The detailed information of the video frame. See {@link VideoFrame}.
+   */
+  virtual void onFrame(const VideoFrame* frame) = 0;
+  virtual ~IVideoFrameObserver() {}
+  virtual bool isExternal() { return true; }
+  virtual VIDEO_PIXEL_FORMAT getVideoFormatPreference() { return VIDEO_PIXEL_DEFAULT; }
+};
+
+enum MEDIA_PLAYER_SOURCE_TYPE {
+  /**
+   * The real type of media player when use MEDIA_PLAYER_SOURCE_DEFAULT is decided by the
+   * type of SDK package. It is full feature media player in full-featured SDK, or simple
+   * media player in others.
+   */
+  MEDIA_PLAYER_SOURCE_DEFAULT,
+  /**
+   * Full featured media player is designed to support more codecs and media format, which
+   * requires more package size than simple player. If you need this player enabled, you
+   * might need to download a full-featured SDK.
+   */
+  MEDIA_PLAYER_SOURCE_FULL_FEATURED,
+  /**
+   * Simple media player with limit codec supported, which requires minimal package size
+   * requirement and is enabled by default
+   */
+  MEDIA_PLAYER_SOURCE_SIMPLE,
+};
+
+enum VIDEO_MODULE_POSITION {
+  POSITION_POST_CAPTURER = 1 << 0,
+  POSITION_PRE_RENDERER = 1 << 1,
+  POSITION_PRE_ENCODER = 1 << 2,
+  POSITION_POST_CAPTURER_ORIGIN = 1 << 3,
+};
+
+}  // namespace base
+
+/** Definition of contentinspect
+ */
+#define MAX_CONTENT_INSPECT_MODULE_COUNT 32
+enum CONTENT_INSPECT_RESULT {
+  CONTENT_INSPECT_NEUTRAL = 1,
+  CONTENT_INSPECT_SEXY = 2,
+  CONTENT_INSPECT_PORN = 3,
+};
+
+enum CONTENT_INSPECT_TYPE {
+  /**
+   * (Default) content inspect type invalid
+   */
+  CONTENT_INSPECT_INVALID = 0,
+  /**
+   * @deprecated
+   * Content inspect type moderation
+   */
+  CONTENT_INSPECT_MODERATION __deprecated = 1,
+  /**
+   * Content inspect type supervise
+   */
+  CONTENT_INSPECT_SUPERVISION = 2,
+  /**
+   * Content inspect type image moderation
+   */
+  CONTENT_INSPECT_IMAGE_MODERATION = 3
+};
+
+struct ContentInspectModule {
+  /**
+   * The content inspect module type.
+   */
+  CONTENT_INSPECT_TYPE type;
+  /**The content inspect frequency, default is 0 second.
+   * the frequency <= 0 is invalid.
+   */
+  unsigned int interval;
+  /** 
+   * The position of the video observation. See VIDEO_MODULE_POSITION.
+   */
+  base::VIDEO_MODULE_POSITION position;
+  ContentInspectModule() {
+    type = CONTENT_INSPECT_INVALID;
+    interval = 0;
+    position = base::POSITION_PRE_ENCODER;
+  }
+};
+/** Definition of ContentInspectConfig.
+ */
+struct ContentInspectConfig {
+  const char* extraInfo;
+  /**
+   * The specific server configuration for image moderation. Please contact technical support.
+   */
+  const char* serverConfig;
+  /**The content inspect modules, max length of modules is 32.
+   * the content(snapshot of send video stream, image) can be used to max of 32 types functions.
+   */
+  ContentInspectModule modules[MAX_CONTENT_INSPECT_MODULE_COUNT];
+  /**The content inspect module count.
+   */
+  int moduleCount;
+  ContentInspectConfig& operator=(const ContentInspectConfig& rth) {
+    extraInfo = rth.extraInfo;
+    serverConfig = rth.serverConfig;
+    moduleCount = rth.moduleCount;
+    memcpy(&modules, &rth.modules, MAX_CONTENT_INSPECT_MODULE_COUNT * sizeof(ContentInspectModule));
+    return *this;
+  }
+  ContentInspectConfig() : extraInfo(NULL), serverConfig(NULL), moduleCount(0) {}
+};
+/** Definition of SnapshotConfig.
+ */
+struct SnapshotConfig {
+  /** 
+   * The local path (including filename extensions) of the snapshot. For example:
+   * - Windows: `C:\Users\<user_name>\AppData\Local\Agora\<process_name>\example.jpg`
+   * - iOS: `/App Sandbox/Library/Caches/example.jpg`
+   * - macOS: `~/Library/Logs/example.jpg`
+   * - Android: `/storage/emulated/0/Android/data/<package name>/files/example.jpg`
+   */
+  const char* filePath;
+
+  /** 
+   * The position of the video observation. See VIDEO_MODULE_POSITION.
+   * 
+   * Allowed values vary depending on the `uid` parameter passed in `takeSnapshot` or `takeSnapshotEx`:
+   * - uid = 0: Position 2, 4 and 8 are allowed.
+   * - uid != 0: Only position 2 is allowed.
+   * 
+   */
+  media::base::VIDEO_MODULE_POSITION position;
+  SnapshotConfig() :filePath(NULL), position(media::base::POSITION_PRE_ENCODER) {}
+};
+
+/**
+ * The audio frame observer.
+ */
+class IAudioPcmFrameSink {
+ public:
+  /**
+   * Occurs when each time the player receives an audio frame.
+   *
+   * After registering the audio frame observer,
+   * the callback occurs when each time the player receives an audio frame,
+   * reporting the detailed information of the audio frame.
+   * @param frame The detailed information of the audio frame. See {@link AudioPcmFrame}.
+   */
+  virtual void onFrame(agora::media::base::AudioPcmFrame* frame) = 0;
+  virtual ~IAudioPcmFrameSink() {}
+};
+
+/**
+ * The IAudioFrameObserverBase class.
+ */
+class IAudioFrameObserverBase {
+ public:
+  /**
+   * Audio frame types.
+   */
+  enum AUDIO_FRAME_TYPE {
+    /**
+     * 0: 16-bit PCM.
+     */
+    FRAME_TYPE_PCM16 = 0,
+  };
+  enum { MAX_HANDLE_TIME_CNT = 10 };
+  /**
+   * The definition of the AudioFrame struct.
+   */
+  struct AudioFrame {
+    /**
+     * The audio frame type: #AUDIO_FRAME_TYPE.
+     */
+    AUDIO_FRAME_TYPE type;
+    /**
+     * The number of samples per channel in this frame.
+     */
+    int samplesPerChannel;
+    /**
+     * The number of bytes per sample: #BYTES_PER_SAMPLE
+     */
+    agora::rtc::BYTES_PER_SAMPLE bytesPerSample;
+    /**
+     * The number of audio channels (data is interleaved, if stereo).
+     * - 1: Mono.
+     * - 2: Stereo.
+     */
+    int channels;
+    /**
+     * The sample rate
+     */
+    int samplesPerSec;
+    /**
+     * The data buffer of the audio frame. When the audio frame uses a stereo channel, the data 
+     * buffer is interleaved.
+     *
+     * Buffer data size: buffer = samplesPerChannel × channels × bytesPerSample.
+     */
+    void* buffer;
+    /**
+     * The timestamp to render the audio data.
+     *
+     * You can use this timestamp to restore the order of the captured audio frame, and synchronize 
+     * audio and video frames in video scenarios, including scenarios where external video sources 
+     * are used.
+     */
+    int64_t renderTimeMs;
+    /**
+     * A reserved parameter.
+     * 
+     * You can use this presentationMs parameter to indicate the presenation milisecond timestamp,
+     * this will then filled into audio4 extension part, the remote side could use this pts in av
+     * sync process with video frame.
+     */
+    int avsync_type;
+    /**
+     * The pts timestamp of this audio frame.
+     *
+     * This timestamp is used to indicate the origin pts time of the frame, and sync with video frame by
+     * the pts time stamp
+     */
+    int64_t presentationMs;
+     /**
+     * The number of the audio track.
+     */
+    int audioTrackNumber;
+    /**
+     * RTP timestamp of the first sample in the audio frame
+     */
+    uint32_t rtpTimestamp;
+
+    AudioFrame() : type(FRAME_TYPE_PCM16),
+                   samplesPerChannel(0),
+                   bytesPerSample(rtc::TWO_BYTES_PER_SAMPLE),
+                   channels(0),
+                   samplesPerSec(0),
+                   buffer(NULL),
+                   renderTimeMs(0),
+                   avsync_type(0),
+                   presentationMs(0),
+                   audioTrackNumber(0),
+                   rtpTimestamp(0) {}
+  };
+
+  enum AUDIO_FRAME_POSITION {
+    AUDIO_FRAME_POSITION_NONE = 0x0000,
+    /** The position for observing the playback audio of all remote users after mixing
+     */
+    AUDIO_FRAME_POSITION_PLAYBACK = 0x0001,
+    /** The position for observing the recorded audio of the local user
+     */
+    AUDIO_FRAME_POSITION_RECORD = 0x0002,
+    /** The position for observing the mixed audio of the local user and all remote users
+     */
+    AUDIO_FRAME_POSITION_MIXED = 0x0004,
+    /** The position for observing the audio of a single remote user before mixing
+     */
+    AUDIO_FRAME_POSITION_BEFORE_MIXING = 0x0008,
+    /** The position for observing the ear monitoring audio of the local user
+     */
+    AUDIO_FRAME_POSITION_EAR_MONITORING = 0x0010,
+  };
+
+  struct AudioParams {
+    /** The audio sample rate (Hz), which can be set as one of the following values:
+
+     - `8000`
+     - `16000` (Default)
+     - `32000`
+     - `44100 `
+     - `48000`
+     */
+    int sample_rate;
+
+    /* The number of audio channels, which can be set as either of the following values:
+
+     - `1`: Mono (Default)
+     - `2`: Stereo
+     */
+    int channels;
+
+    /* The use mode of the audio data. See AgoraAudioRawFrameOperationMode.
+     */
+    rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode;
+
+    /** The number of samples. For example, set it as 1024 for RTMP or RTMPS
+     streaming.
+     */
+    int samples_per_call;
+
+    AudioParams() : sample_rate(0), channels(0), mode(rtc::RAW_AUDIO_FRAME_OP_MODE_READ_ONLY), samples_per_call(0) {}
+    AudioParams(int samplerate, int channel, rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE type, int samplesPerCall) : sample_rate(samplerate), channels(channel), mode(type), samples_per_call(samplesPerCall) {}
+  };
+
+ public:
+  virtual ~IAudioFrameObserverBase() {}
+
+  /**
+   * Occurs when the recorded audio frame is received.
+   * @param channelId The channel name
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The recorded audio frame is valid and is encoded and sent.
+   * - false: The recorded audio frame is invalid and is not encoded or sent.
+   */
+  virtual bool onRecordAudioFrame(const char* channelId, AudioFrame& audioFrame) = 0;
+  /**
+   * Occurs when the playback audio frame is received.
+   * @param channelId The channel name
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The playback audio frame is valid and is encoded and sent.
+   * - false: The playback audio frame is invalid and is not encoded or sent.
+   */
+  virtual bool onPlaybackAudioFrame(const char* channelId, AudioFrame& audioFrame) = 0;
+  /**
+   * Occurs when the mixed audio data is received.
+   * @param channelId The channel name
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The mixed audio data is valid and is encoded and sent.
+   * - false: The mixed audio data is invalid and is not encoded or sent.
+   */
+  virtual bool onMixedAudioFrame(const char* channelId, AudioFrame& audioFrame) = 0;
+  /**
+   * Occurs when the ear monitoring audio frame is received.
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The ear monitoring audio data is valid and is encoded and sent.
+   * - false: The ear monitoring audio data is invalid and is not encoded or sent.
+   */
+  virtual bool onEarMonitoringAudioFrame(AudioFrame& audioFrame) = 0;
+  /**
+   * Occurs when the before-mixing playback audio frame is received.
+   * @param channelId The channel name
+   * @param userId ID of the remote user.
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The before-mixing playback audio frame is valid and is encoded and sent.
+   * - false: The before-mixing playback audio frame is invalid and is not encoded or sent.
+   */
+  virtual bool onPlaybackAudioFrameBeforeMixing(const char* channelId, base::user_id_t userId, AudioFrame& audioFrame) {
+    (void) channelId;
+    (void) userId;
+    (void) audioFrame;
+    return true;
+  }
+
+  /**
+   * Sets the frame position for the audio observer.
+   * @return A bit mask that controls the frame position of the audio observer.
+   * @note - Use '|' (the OR operator) to observe multiple frame positions.
+   * <p>
+   * After you successfully register the audio observer, the SDK triggers this callback each time it receives a audio frame. You can determine which position to observe by setting the return value.
+   * The SDK provides 4 positions for observer. Each position corresponds to a callback function:
+   * - `AUDIO_FRAME_POSITION_PLAYBACK (1 << 0)`: The position for playback audio frame is received, which corresponds to the \ref onPlaybackFrame "onPlaybackFrame" callback.
+   * - `AUDIO_FRAME_POSITION_RECORD (1 << 1)`: The position for record audio frame is received, which corresponds to the \ref onRecordFrame "onRecordFrame" callback.
+   * - `AUDIO_FRAME_POSITION_MIXED (1 << 2)`: The position for mixed audio frame is received, which corresponds to the \ref onMixedFrame "onMixedFrame" callback.
+   * - `AUDIO_FRAME_POSITION_BEFORE_MIXING (1 << 3)`: The position for playback audio frame before mixing is received, which corresponds to the \ref onPlaybackFrameBeforeMixing "onPlaybackFrameBeforeMixing" callback.
+   *  @return The bit mask that controls the audio observation positions.
+   * See AUDIO_FRAME_POSITION.
+   */
+
+  virtual int getObservedAudioFramePosition() = 0;
+
+  /** Sets the audio playback format
+   **Note**:
+
+   - The SDK calculates the sample interval according to the `AudioParams`
+   you set in the return value of this callback and triggers the
+   `onPlaybackAudioFrame` callback at the calculated sample interval.
+   Sample interval (seconds) = `samplesPerCall`/(`sampleRate` × `channel`).
+   Ensure that the value of sample interval is equal to or greater than 0.01.
+
+   @return Sets the audio format. See AgoraAudioParams.
+   */
+  virtual AudioParams getPlaybackAudioParams() = 0;
+
+  /** Sets the audio recording format
+   **Note**:
+   - The SDK calculates the sample interval according to the `AudioParams`
+   you set in the return value of this callback and triggers the
+   `onRecordAudioFrame` callback at the calculated sample interval.
+   Sample interval (seconds) = `samplesPerCall`/(`sampleRate` × `channel`).
+   Ensure that the value of sample interval is equal to or greater than 0.01.
+
+   @return Sets the audio format. See AgoraAudioParams.
+   */
+  virtual AudioParams getRecordAudioParams() = 0;
+
+  /** Sets the audio mixing format
+   **Note**:
+   - The SDK calculates the sample interval according to the `AudioParams`
+   you set in the return value of this callback and triggers the
+   `onMixedAudioFrame` callback at the calculated sample interval.
+   Sample interval (seconds) = `samplesPerCall`/(`sampleRate` × `channel`).
+   Ensure that the value of sample interval is equal to or greater than 0.01.
+
+   @return Sets the audio format. See AgoraAudioParams.
+   */
+  virtual AudioParams getMixedAudioParams() = 0;
+
+  /** Sets the ear monitoring audio format
+   **Note**:
+   - The SDK calculates the sample interval according to the `AudioParams`
+   you set in the return value of this callback and triggers the
+   `onEarMonitoringAudioFrame` callback at the calculated sample interval.
+   Sample interval (seconds) = `samplesPerCall`/(`sampleRate` × `channel`).
+   Ensure that the value of sample interval is equal to or greater than 0.01.
+
+   @return Sets the audio format. See AgoraAudioParams.
+   */
+  virtual AudioParams getEarMonitoringAudioParams() = 0;
+};
+
+/**
+ * The IAudioFrameObserver class.
+ */
+class IAudioFrameObserver : public IAudioFrameObserverBase {
+ public:
+  using IAudioFrameObserverBase::onPlaybackAudioFrameBeforeMixing;
+  /**
+   * Occurs when the before-mixing playback audio frame is received.
+   * @param channelId The channel name
+   * @param uid ID of the remote user.
+   * @param audioFrame The reference to the audio frame: AudioFrame.
+   * @return
+   * - true: The before-mixing playback audio frame is valid and is encoded and sent.
+   * - false: The before-mixing playback audio frame is invalid and is not encoded or sent.
+   */
+  virtual bool onPlaybackAudioFrameBeforeMixing(const char* channelId, rtc::uid_t uid, AudioFrame& audioFrame) = 0;
+};
+
+struct AudioSpectrumData {
+  /**
+   * The audio spectrum data of audio.
+   */
+  const float *audioSpectrumData;
+  /**
+   * The data length of audio spectrum data.
+   */
+  int dataLength;
+
+  AudioSpectrumData() : audioSpectrumData(NULL), dataLength(0) {}
+  AudioSpectrumData(const float *data, int length) :
+    audioSpectrumData(data), dataLength(length) {}
+};
+
+struct UserAudioSpectrumInfo  {
+  /**
+   * User ID of the speaker.
+   */
+  agora::rtc::uid_t uid;
+  /**
+   * The audio spectrum data of audio.
+   */
+  struct AudioSpectrumData spectrumData;
+
+  UserAudioSpectrumInfo() : uid(0) {}
+
+  UserAudioSpectrumInfo(agora::rtc::uid_t uid, const float* data, int length) : uid(uid), spectrumData(data, length) {}
+};
+
+/**
+ * The IAudioSpectrumObserver class.
+ */
+class IAudioSpectrumObserver {
+public:
+  virtual ~IAudioSpectrumObserver() {}
+
+  /**
+   * Reports the audio spectrum of local audio.
+   *
+   * This callback reports the audio spectrum data of the local audio at the moment
+   * in the channel.
+   *
+   * You can set the time interval of this callback using \ref ILocalUser::enableAudioSpectrumMonitor "enableAudioSpectrumMonitor".
+   *
+   * @param data The audio spectrum data of local audio.
+   * - true: Processed.
+   * - false: Not processed.
+   */
+  virtual bool onLocalAudioSpectrum(const AudioSpectrumData& data) = 0;
+  /**
+   * Reports the audio spectrum of remote user.
+   *
+   * This callback reports the IDs and audio spectrum data of the loudest speakers at the moment
+   * in the channel.
+   *
+   * You can set the time interval of this callback using \ref ILocalUser::enableAudioSpectrumMonitor "enableAudioSpectrumMonitor".
+   *
+   * @param spectrums The pointer to \ref agora::media::UserAudioSpectrumInfo "UserAudioSpectrumInfo", which is an array containing
+   * the user ID and audio spectrum data for each speaker.
+   * - This array contains the following members:
+   *   - `uid`, which is the UID of each remote speaker
+   *   - `spectrumData`, which reports the audio spectrum of each remote speaker.
+   * @param spectrumNumber The array length of the spectrums.
+   * - true: Processed.
+   * - false: Not processed.
+   */
+  virtual bool onRemoteAudioSpectrum(const UserAudioSpectrumInfo* spectrums, unsigned int spectrumNumber) = 0;
+};
+
+/**
+ * The IVideoEncodedFrameObserver class.
+ */
+class IVideoEncodedFrameObserver {
+ public:
+  /**
+   * Occurs each time the SDK receives an encoded video image.
+   * @param uid The user id of remote user.
+   * @param imageBuffer The pointer to the video image buffer.
+   * @param length The data length of the video image.
+   * @param videoEncodedFrameInfo The information of the encoded video frame: EncodedVideoFrameInfo.
+   * @return Determines whether to accept encoded video image.
+   * - true: Accept.
+   * - false: Do not accept.
+   */
+  virtual bool onEncodedVideoFrameReceived(rtc::uid_t uid, const uint8_t* imageBuffer, size_t length,
+                                           const rtc::EncodedVideoFrameInfo& videoEncodedFrameInfo) = 0;
+
+  virtual ~IVideoEncodedFrameObserver() {}
+};
+
+/**
+ * The IVideoFrameObserver class.
+ */
+class IVideoFrameObserver {
+ public:
+  typedef media::base::VideoFrame VideoFrame;
+  /**
+   * The process mode of the video frame:
+   */
+  enum VIDEO_FRAME_PROCESS_MODE {
+    /**
+     * Read-only mode.
+     * 
+     * In this mode, you do not modify the video frame. The video frame observer is a renderer.
+     */
+    PROCESS_MODE_READ_ONLY, // Observer works as a pure renderer and will not modify the original frame.
+    /**
+     * Read and write mode.
+     * 
+     * In this mode, you modify the video frame. The video frame observer is a video filter.
+     */
+    PROCESS_MODE_READ_WRITE, // Observer works as a filter that will process the video frame and affect the following frame processing in SDK.
+  };
+
+ public:
+  virtual ~IVideoFrameObserver() {}
+
+  /**
+   * Occurs each time the SDK receives a video frame captured by the local camera.
+   *
+   * After you successfully register the video frame observer, the SDK triggers this callback each time
+   * a video frame is received. In this callback, you can get the video data captured by the local
+   * camera. You can then pre-process the data according to your scenarios.
+   *
+   * After pre-processing, you can send the processed video data back to the SDK by setting the
+   * `videoFrame` parameter in this callback.
+   *
+   * @note
+   * - If you get the video data in RGBA color encoding format, Agora does not support using this callback to send the processed data in RGBA color encoding format back to the SDK.
+   * - The video data that this callback gets has not been pre-processed, such as watermarking, cropping content, rotating, or image enhancement.
+   *
+   * @param videoFrame A pointer to the video frame: VideoFrame
+   * @param sourceType source type of video frame. See #VIDEO_SOURCE_TYPE.
+   * @return Determines whether to ignore the current video frame if the pre-processing fails:
+   * - true: Do not ignore.
+   * - false: Ignore, in which case this method does not sent the current video frame to the SDK.
+  */
+  virtual bool onCaptureVideoFrame(agora::rtc::VIDEO_SOURCE_TYPE sourceType, VideoFrame& videoFrame) = 0;
+
+  /**
+   * Occurs each time the SDK receives a video frame before encoding.
+   *
+   * After you successfully register the video frame observer, the SDK triggers this callback each time
+   * when it receives a video frame. In this callback, you can get the video data before encoding. You can then
+   * process the data according to your particular scenarios.
+   *
+   * After processing, you can send the processed video data back to the SDK by setting the
+   * `videoFrame` parameter in this callback.
+   *
+   * @note
+   * - To get the video data captured from the second screen before encoding, you need to set (1 << 2) as a frame position through `getObservedFramePosition`.
+   * - The video data that this callback gets has been pre-processed, such as watermarking, cropping content, rotating, or image enhancement.
+   * - This callback does not support sending processed RGBA video data back to the SDK.
+   *
+   * @param videoFrame A pointer to the video frame: VideoFrame
+   * @param sourceType source type of video frame. See #VIDEO_SOURCE_TYPE.
+   * @return Determines whether to ignore the current video frame if the pre-processing fails:
+   * - true: Do not ignore.
+   * - false: Ignore, in which case this method does not sent the current video frame to the SDK.
+   */
+  virtual bool onPreEncodeVideoFrame(agora::rtc::VIDEO_SOURCE_TYPE sourceType, VideoFrame& videoFrame) = 0;
+
+  /**
+   * Occurs each time the SDK receives a video frame decoded by the MediaPlayer.
+   *
+   * After you successfully register the video frame observer, the SDK triggers this callback each
+   * time a video frame is decoded. In this callback, you can get the video data decoded by the
+   * MediaPlayer. You can then pre-process the data according to your scenarios.
+   *
+   * After pre-processing, you can send the processed video data back to the SDK by setting the
+   * `videoFrame` parameter in this callback.
+   * 
+   * @note
+   * - This callback will not be affected by the return values of \ref getVideoFrameProcessMode "getVideoFrameProcessMode", \ref getRotationApplied "getRotationApplied", \ref getMirrorApplied "getMirrorApplied", \ref getObservedFramePosition "getObservedFramePosition".
+   * - On Android, this callback is not affected by the return value of \ref getVideoFormatPreference "getVideoFormatPreference"
+   *
+   * @param videoFrame A pointer to the video frame: VideoFrame
+   * @param mediaPlayerId ID of the mediaPlayer.
+   * @return Determines whether to ignore the current video frame if the pre-processing fails:
+   * - true: Do not ignore.
+   * - false: Ignore, in which case this method does not sent the current video frame to the SDK.
+   */
+  virtual bool onMediaPlayerVideoFrame(VideoFrame& videoFrame, int mediaPlayerId) = 0;
+
+  /**
+   * Occurs each time the SDK receives a video frame sent by the remote user.
+   *
+   * After you successfully register the video frame observer, the SDK triggers this callback each time a
+   * video frame is received. In this callback, you can get the video data sent by the remote user. You
+   * can then post-process the data according to your scenarios.
+   *
+   * After post-processing, you can send the processed data back to the SDK by setting the `videoFrame`
+   * parameter in this callback.
+   * 
+   * @note This callback does not support sending processed RGBA video data back to the SDK.
+   *
+   * @param channelId The channel name
+   * @param remoteUid ID of the remote user who sends the current video frame.
+   * @param videoFrame A pointer to the video frame: VideoFrame
+   * @return Determines whether to ignore the current video frame if the post-processing fails:
+   * - true: Do not ignore.
+   * - false: Ignore, in which case this method does not sent the current video frame to the SDK.
+   */
+  virtual bool onRenderVideoFrame(const char* channelId, rtc::uid_t remoteUid, VideoFrame& videoFrame) = 0;
+
+  virtual bool onTranscodedVideoFrame(VideoFrame& videoFrame) = 0;
+
+  /**
+   * Occurs each time the SDK receives a video frame and prompts you to set the process mode of the video frame.
+   * 
+   * After you successfully register the video frame observer, the SDK triggers this callback each time it receives 
+   * a video frame. You need to set your preferred process mode in the return value of this callback.
+   * @return VIDEO_FRAME_PROCESS_MODE.
+   */
+  virtual VIDEO_FRAME_PROCESS_MODE getVideoFrameProcessMode() {
+    return PROCESS_MODE_READ_ONLY;
+  }
+
+  /**
+   * Sets the format of the raw video data output by the SDK.
+   *
+   * If you want to get raw video data in a color encoding format other than YUV 420, register this callback when 
+   * calling `registerVideoFrameObserver`. After you successfully register the video frame observer, the SDK triggers 
+   * this callback each time it receives a video frame. You need to set your preferred video data in the return value 
+   * of this callback.
+   * 
+   * @note If you want the video captured by the sender to be the original format, set the original video data format 
+   * to VIDEO_PIXEL_DEFAULT in the return value. On different platforms, the original video pixel format is also 
+   * different, for the actual video pixel format, see `VideoFrame`.
+   * 
+   * @return Sets the video format. See VIDEO_PIXEL_FORMAT.
+   */
+  virtual base::VIDEO_PIXEL_FORMAT getVideoFormatPreference() { return base::VIDEO_PIXEL_DEFAULT; }
+
+  /**
+   * Occurs each time the SDK receives a video frame, and prompts you whether to rotate the captured video.
+   * 
+   * If you want to rotate the captured video according to the rotation member in the `VideoFrame` class, register this 
+   * callback by calling `registerVideoFrameObserver`. After you successfully register the video frame observer, the 
+   * SDK triggers this callback each time it receives a video frame. You need to set whether to rotate the video frame 
+   * in the return value of this callback.
+   * 
+   * @note This function only supports video data in RGBA or YUV420.
+   *
+   * @return Determines whether to rotate.
+   * - `true`: Rotate the captured video.
+   * - `false`: (Default) Do not rotate the captured video.
+   */
+  virtual bool getRotationApplied() { return false; }
+
+  /**
+   * Occurs each time the SDK receives a video frame and prompts you whether or not to mirror the captured video.
+   * 
+   * If the video data you want to obtain is a mirror image of the original video, you need to register this callback 
+   * when calling `registerVideoFrameObserver`. After you successfully register the video frame observer, the SDK 
+   * triggers this callback each time it receives a video frame. You need to set whether or not to mirror the video 
+   * frame in the return value of this callback.
+   * 
+   * @note This function only supports video data in RGBA and YUV420 formats.
+   *
+   * @return Determines whether to mirror.
+   * - `true`: Mirror the captured video.
+   * - `false`: (Default) Do not mirror the captured video.
+   */
+  virtual bool getMirrorApplied() { return false; }
+
+  /**
+   * Sets the frame position for the video observer.
+   *
+   * After you successfully register the video observer, the SDK triggers this callback each time it receives
+   * a video frame. You can determine which position to observe by setting the return value. The SDK provides
+   * 3 positions for observer. Each position corresponds to a callback function:
+   *
+   * POSITION_POST_CAPTURER(1 << 0): The position after capturing the video data, which corresponds to the onCaptureVideoFrame callback.
+   * POSITION_PRE_RENDERER(1 << 1): The position before receiving the remote video data, which corresponds to the onRenderVideoFrame callback.
+   * POSITION_PRE_ENCODER(1 << 2): The position before encoding the video data, which corresponds to the onPreEncodeVideoFrame callback.
+   *
+   * To observe multiple frame positions, use '|' (the OR operator).
+   * This callback observes POSITION_POST_CAPTURER(1 << 0) and POSITION_PRE_RENDERER(1 << 1) by default.
+   * To conserve the system consumption, you can reduce the number of frame positions that you want to observe.
+   *
+   * @return A bit mask that controls the frame position of the video observer: VIDEO_OBSERVER_POSITION.
+   */
+  virtual uint32_t getObservedFramePosition() {
+    return base::POSITION_POST_CAPTURER | base::POSITION_PRE_RENDERER;
+  }
+
+  /**
+   * Indicate if the observer is for internal use.
+   * Note: Never override this function
+   * @return
+   * - true: the observer is for external use
+   * - false: the observer is for internal use
+   */
+  virtual bool isExternal() { return true; }
+};
+
+/**
+ * The external video source type.
+ */
+enum EXTERNAL_VIDEO_SOURCE_TYPE {
+  /**
+   * 0: non-encoded video frame.
+   */
+  VIDEO_FRAME = 0,
+  /**
+   * 1: encoded video frame.
+   */
+  ENCODED_VIDEO_FRAME,
+};
+
+/**
+ * The format of the recording file.
+ *
+ * @since v3.5.2
+ */
+enum MediaRecorderContainerFormat {
+  /**
+   * 1: (Default) MP4.
+   */
+  FORMAT_MP4 = 1,
+};
+/**
+ * The recording content.
+ *
+ * @since v3.5.2
+ */
+enum MediaRecorderStreamType {
+  /**
+   * Only audio.
+   */
+  STREAM_TYPE_AUDIO = 0x01,
+  /**
+   * Only video.
+   */
+  STREAM_TYPE_VIDEO = 0x02,
+  /**
+   * (Default) Audio and video.
+   */
+  STREAM_TYPE_BOTH = STREAM_TYPE_AUDIO | STREAM_TYPE_VIDEO,
+};
+/**
+ * The current recording state.
+ *
+ * @since v3.5.2
+ */
+enum RecorderState {
+  /**
+   * -1: An error occurs during the recording. See RecorderReasonCode for the reason.
+   */
+  RECORDER_STATE_ERROR = -1,
+  /**
+   * 2: The audio and video recording is started.
+   */
+  RECORDER_STATE_START = 2,
+  /**
+   * 3: The audio and video recording is stopped.
+   */
+  RECORDER_STATE_STOP = 3,
+};
+/**
+ * The reason for the state change
+ *
+ * @since v3.5.2
+ */
+enum RecorderReasonCode {
+  /**
+   * 0: No error occurs.
+   */
+  RECORDER_REASON_NONE = 0,
+  /**
+   * 1: The SDK fails to write the recorded data to a file.
+   */
+  RECORDER_REASON_WRITE_FAILED = 1,
+  /**
+   * 2: The SDK does not detect audio and video streams to be recorded, or audio and video streams are interrupted for more than five seconds during recording.
+   */
+  RECORDER_REASON_NO_STREAM = 2,
+  /**
+   * 3: The recording duration exceeds the upper limit.
+   */
+  RECORDER_REASON_OVER_MAX_DURATION = 3,
+  /**
+   * 4: The recording configuration changes.
+   */
+  RECORDER_REASON_CONFIG_CHANGED = 4,
+};
+/**
+ * Configurations for the local audio and video recording.
+ *
+ * @since v3.5.2
+ */
+struct MediaRecorderConfiguration {
+  /**
+   * The absolute path (including the filename extensions) of the recording file.
+   * For example, `C:\Users\<user_name>\AppData\Local\Agora\<process_name>\example.mp4` on Windows,
+   * `/App Sandbox/Library/Caches/example.mp4` on iOS, `/Library/Logs/example.mp4` on macOS, and
+   * `/storage/emulated/0/Android/data/<package name>/files/example.mp4` on Android.
+   *
+   * @note Ensure that the specified path exists and is writable.
+   */
+  const char* storagePath;
+  /**
+   * The format of the recording file. See \ref agora::rtc::MediaRecorderContainerFormat "MediaRecorderContainerFormat".
+   */
+  MediaRecorderContainerFormat containerFormat;
+  /**
+   * The recording content. See \ref agora::rtc::MediaRecorderStreamType "MediaRecorderStreamType".
+   */
+  MediaRecorderStreamType streamType;
+  /**
+   * The maximum recording duration, in milliseconds. The default value is 120000.
+   */
+  int maxDurationMs;
+  /**
+   * The interval (ms) of updating the recording information. The value range is
+   * [1000,10000]. Based on the set value of `recorderInfoUpdateInterval`, the
+   * SDK triggers the \ref IMediaRecorderObserver::onRecorderInfoUpdated "onRecorderInfoUpdated"
+   * callback to report the updated recording information.
+   */
+  int recorderInfoUpdateInterval;
+
+  MediaRecorderConfiguration() : storagePath(NULL), containerFormat(FORMAT_MP4), streamType(STREAM_TYPE_BOTH), maxDurationMs(120000), recorderInfoUpdateInterval(0) {}
+  MediaRecorderConfiguration(const char* path, MediaRecorderContainerFormat format, MediaRecorderStreamType type, int duration, int interval) : storagePath(path), containerFormat(format), streamType(type), maxDurationMs(duration), recorderInfoUpdateInterval(interval) {}
+};
+
+class IFaceInfoObserver {
+public:
+   /**
+    * Occurs when the face info is received.
+    * @param outFaceInfo The output face info.
+    * @return
+    * - true: The face info is valid.
+    * - false: The face info is invalid.
+   */
+   virtual bool onFaceInfo(const char* outFaceInfo) = 0;
+  
+   virtual ~IFaceInfoObserver() {}
+};
+
+/**
+ * Information for the recording file.
+ *
+ * @since v3.5.2
+ */
+struct RecorderInfo {
+  /**
+   * The absolute path of the recording file.
+   */
+  const char* fileName;
+  /**
+   * The recording duration, in milliseconds.
+   */
+  unsigned int durationMs;
+  /**
+   * The size in bytes of the recording file.
+   */
+  unsigned int fileSize;
+
+  RecorderInfo() : fileName(NULL), durationMs(0), fileSize(0) {}
+  RecorderInfo(const char* name, unsigned int dur, unsigned int size) : fileName(name), durationMs(dur), fileSize(size) {}
+};
+
+class IMediaRecorderObserver {
+ public:
+  /**
+   * Occurs when the recording state changes.
+   *
+   * @since v4.0.0
+   *
+   * When the local audio and video recording state changes, the SDK triggers this callback to report the current
+   * recording state and the reason for the change.
+   *
+   * @param channelId The channel name.
+   * @param uid ID of the user.
+   * @param state The current recording state. See \ref agora::media::RecorderState "RecorderState".
+   * @param reason The reason for the state change. See \ref agora::media::RecorderReasonCode "RecorderReasonCode".
+   */
+  virtual void onRecorderStateChanged(const char* channelId, rtc::uid_t uid, RecorderState state, RecorderReasonCode reason) = 0;
+  /**
+   * Occurs when the recording information is updated.
+   *
+   * @since v4.0.0
+   *
+   * After you successfully register this callback and enable the local audio and video recording, the SDK periodically triggers
+   * the `onRecorderInfoUpdated` callback based on the set value of `recorderInfoUpdateInterval`. This callback reports the
+   * filename, duration, and size of the current recording file.
+   *
+   * @param channelId The channel name.
+   * @param uid ID of the user.
+   * @param info Information about the recording file. See \ref agora::media::RecorderInfo "RecorderInfo".
+   *
+   */
+  virtual void onRecorderInfoUpdated(const char* channelId, rtc::uid_t uid, const RecorderInfo& info) = 0;
+
+  virtual ~IMediaRecorderObserver() {}
+};
+
+}  // namespace media
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/AgoraMediaPlayerTypes.h b/headers/rtc_4.3.2.11/include/AgoraMediaPlayerTypes.h
new file mode 100644
index 0000000..2df2a43
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/AgoraMediaPlayerTypes.h
@@ -0,0 +1,516 @@
+//
+//  Agora Engine SDK
+//
+//  Created by Sting Feng in 2020-05.
+//  Copyright (c) 2017 Agora.io. All rights reserved.
+
+#pragma once  // NOLINT(build/header_guard)
+
+#include <cstring>
+#include <stdint.h>
+
+#include "AgoraOptional.h"
+
+/**
+ * set analyze duration for real time stream
+ * @example "setPlayerOption(KEY_PLAYER_REAL_TIME_STREAM_ANALYZE_DURATION,1000000)"
+ */
+#define KEY_PLAYER_REAL_TIME_STREAM_ANALYZE_DURATION    "analyze_duration"
+
+/**
+ * make the player to enable audio or not
+ * @example  "setPlayerOption(KEY_PLAYER_ENABLE_AUDIO,0)"
+ */
+#define KEY_PLAYER_ENABLE_AUDIO                  "enable_audio"
+
+/**
+ * make the player to enable video or not
+ * @example  "setPlayerOption(KEY_PLAYER_ENABLE_VIDEO,0)"
+ */
+#define KEY_PLAYER_ENABLE_VIDEO                  "enable_video"
+
+/**
+ * set the player enable to search metadata
+ * @example  "setPlayerOption(KEY_PLAYER_DISABLE_SEARCH_METADATA,0)"
+ */
+#define KEY_PLAYER_ENABLE_SEARCH_METADATA         "enable_search_metadata"
+
+/**
+ * set the player sei filter type
+ * @example  "setPlayerOption(KEY_PLAYER_SEI_FILTER_TYPE,"5")"
+ */
+#define KEY_PLAYER_SEI_FILTER_TYPE         "set_sei_filter_type"
+
+namespace agora {
+
+namespace media {
+
+namespace base {
+static const uint8_t kMaxCharBufferLength = 50;
+/**
+ * @brief The playback state.
+ *
+ */
+enum MEDIA_PLAYER_STATE {
+  /** Default state.
+   */
+  PLAYER_STATE_IDLE = 0,
+  /** Opening the media file.
+   */
+  PLAYER_STATE_OPENING,
+  /** The media file is opened successfully.
+   */
+  PLAYER_STATE_OPEN_COMPLETED,
+  /** Playing the media file.
+   */
+  PLAYER_STATE_PLAYING,
+  /** The playback is paused.
+   */
+  PLAYER_STATE_PAUSED,
+  /** The playback is completed.
+   */
+  PLAYER_STATE_PLAYBACK_COMPLETED,
+  /** All loops are completed.
+   */
+  PLAYER_STATE_PLAYBACK_ALL_LOOPS_COMPLETED,
+  /** The playback is stopped.
+   */
+  PLAYER_STATE_STOPPED,
+  /** Player pausing (internal)
+   */
+  PLAYER_STATE_PAUSING_INTERNAL = 50,
+  /** Player stopping (internal)
+   */
+  PLAYER_STATE_STOPPING_INTERNAL,
+  /** Player seeking state (internal)
+   */
+  PLAYER_STATE_SEEKING_INTERNAL,
+  /** Player getting state (internal)
+   */
+  PLAYER_STATE_GETTING_INTERNAL,
+  /** None state for state machine (internal)
+   */
+  PLAYER_STATE_NONE_INTERNAL,
+  /** Do nothing state for state machine (internal)
+   */
+  PLAYER_STATE_DO_NOTHING_INTERNAL,
+  /** Player set track state (internal)
+   */
+  PLAYER_STATE_SET_TRACK_INTERNAL,
+  /** The playback fails.
+   */
+  PLAYER_STATE_FAILED = 100,
+};
+/**
+ * @brief Player error code
+ *
+ */
+enum MEDIA_PLAYER_REASON {
+  /** No error.
+   */
+  PLAYER_REASON_NONE = 0,
+  /** The parameter is invalid.
+   */
+  PLAYER_REASON_INVALID_ARGUMENTS = -1,
+  /** Internel error.
+   */
+  PLAYER_REASON_INTERNAL = -2,
+  /** No resource.
+   */
+  PLAYER_REASON_NO_RESOURCE = -3,
+  /** Invalid media source.
+   */
+  PLAYER_REASON_INVALID_MEDIA_SOURCE = -4,
+  /** The type of the media stream is unknown.
+   */
+  PLAYER_REASON_UNKNOWN_STREAM_TYPE = -5,
+  /** The object is not initialized.
+   */
+  PLAYER_REASON_OBJ_NOT_INITIALIZED = -6,
+  /** The codec is not supported.
+   */
+  PLAYER_REASON_CODEC_NOT_SUPPORTED = -7,
+  /** Invalid renderer.
+   */
+  PLAYER_REASON_VIDEO_RENDER_FAILED = -8,
+  /** An error occurs in the internal state of the player.
+   */
+  PLAYER_REASON_INVALID_STATE = -9,
+  /** The URL of the media file cannot be found.
+   */
+  PLAYER_REASON_URL_NOT_FOUND = -10,
+  /** Invalid connection between the player and the Agora server.
+   */
+  PLAYER_REASON_INVALID_CONNECTION_STATE = -11,
+  /** The playback buffer is insufficient.
+   */
+  PLAYER_REASON_SRC_BUFFER_UNDERFLOW = -12,
+  /** The audio mixing file playback is interrupted.
+   */
+  PLAYER_REASON_INTERRUPTED = -13,
+  /** The SDK does not support this function.
+   */
+  PLAYER_REASON_NOT_SUPPORTED = -14,
+  /** The token has expired.
+   */
+  PLAYER_REASON_TOKEN_EXPIRED = -15,
+  /** The ip has expired.
+   */
+  PLAYER_REASON_IP_EXPIRED = -16,
+  /** An unknown error occurs.
+   */
+  PLAYER_REASON_UNKNOWN = -17,
+};
+
+/**
+ * @brief The type of the media stream.
+ *
+ */
+enum MEDIA_STREAM_TYPE {
+  /** The type is unknown.
+   */
+  STREAM_TYPE_UNKNOWN = 0,
+  /** The video stream.
+   */
+  STREAM_TYPE_VIDEO = 1,
+  /** The audio stream.
+   */
+  STREAM_TYPE_AUDIO = 2,
+  /** The subtitle stream.
+   */
+  STREAM_TYPE_SUBTITLE = 3,
+};
+
+/**
+ * @brief The playback event.
+ *
+ */
+enum MEDIA_PLAYER_EVENT {
+  /** The player begins to seek to the new playback position.
+   */
+  PLAYER_EVENT_SEEK_BEGIN = 0,
+  /** The seek operation completes.
+   */
+  PLAYER_EVENT_SEEK_COMPLETE = 1,
+  /** An error occurs during the seek operation.
+   */
+  PLAYER_EVENT_SEEK_ERROR = 2,
+  /** The player changes the audio track for playback.
+   */
+  PLAYER_EVENT_AUDIO_TRACK_CHANGED = 5,
+  /** player buffer low
+   */
+  PLAYER_EVENT_BUFFER_LOW = 6,
+    /** player buffer recover
+   */
+  PLAYER_EVENT_BUFFER_RECOVER = 7,
+  /** The video or audio is interrupted
+   */
+  PLAYER_EVENT_FREEZE_START = 8,
+  /** Interrupt at the end of the video or audio
+   */
+  PLAYER_EVENT_FREEZE_STOP = 9,
+  /** switch source begin
+  */
+  PLAYER_EVENT_SWITCH_BEGIN = 10,
+  /** switch source complete
+  */
+  PLAYER_EVENT_SWITCH_COMPLETE = 11,
+  /** switch source error
+  */
+  PLAYER_EVENT_SWITCH_ERROR = 12,
+  /** An application can render the video to less than a second
+   */
+  PLAYER_EVENT_FIRST_DISPLAYED = 13,
+  /** cache resources exceed the maximum file count
+   */
+  PLAYER_EVENT_REACH_CACHE_FILE_MAX_COUNT = 14,
+  /** cache resources exceed the maximum file size
+   */
+  PLAYER_EVENT_REACH_CACHE_FILE_MAX_SIZE = 15,
+  /** Triggered when a retry is required to open the media
+   */
+  PLAYER_EVENT_TRY_OPEN_START = 16,
+  /** Triggered when the retry to open the media is successful
+   */
+  PLAYER_EVENT_TRY_OPEN_SUCCEED = 17,
+  /** Triggered when retrying to open media fails
+   */
+  PLAYER_EVENT_TRY_OPEN_FAILED = 18,
+};
+
+/**
+ * @brief The play preload another source event.
+ *
+ */
+enum PLAYER_PRELOAD_EVENT  {
+  /** preload source begin
+  */
+  PLAYER_PRELOAD_EVENT_BEGIN = 0,
+  /** preload source complete
+  */
+  PLAYER_PRELOAD_EVENT_COMPLETE = 1,
+  /** preload source error
+  */
+  PLAYER_PRELOAD_EVENT_ERROR = 2,
+};
+
+/**
+ * @brief The information of the media stream object.
+ *
+ */
+struct PlayerStreamInfo {
+  /** The index of the media stream. */
+  int streamIndex;
+
+  /** The type of the media stream. See {@link MEDIA_STREAM_TYPE}. */
+  MEDIA_STREAM_TYPE streamType;
+
+  /** The codec of the media stream. */
+  char codecName[kMaxCharBufferLength];
+
+  /** The language of the media stream. */
+  char language[kMaxCharBufferLength];
+
+  /** The frame rate (fps) if the stream is video. */
+  int videoFrameRate;
+
+  /** The video bitrate (bps) if the stream is video. */
+  int videoBitRate;
+
+  /** The video width (pixel) if the stream is video. */
+  int videoWidth;
+
+  /** The video height (pixel) if the stream is video. */
+  int videoHeight;
+
+  /** The rotation angle if the steam is video. */
+  int videoRotation;
+
+  /** The sample rate if the stream is audio. */
+  int audioSampleRate;
+
+  /** The number of audio channels if the stream is audio. */
+  int audioChannels;
+
+  /** The number of bits per sample if the stream is audio. */
+  int audioBitsPerSample;
+
+  /** The total duration (millisecond) of the media stream. */
+  int64_t duration;
+
+  PlayerStreamInfo() : streamIndex(0),
+                       streamType(STREAM_TYPE_UNKNOWN),
+                       videoFrameRate(0),
+                       videoBitRate(0),
+                       videoWidth(0),
+                       videoHeight(0),
+                       videoRotation(0),
+                       audioSampleRate(0),
+                       audioChannels(0),
+                       audioBitsPerSample(0),
+                       duration(0) {
+    memset(codecName, 0, sizeof(codecName));
+    memset(language, 0, sizeof(language));
+  }
+};
+
+/**
+ * @brief The information of the media stream object.
+ *
+ */
+struct SrcInfo {
+  /** The bitrate of the media stream. The unit of the number is kbps.
+   *
+   */
+  int bitrateInKbps;
+
+  /** The name of the media stream.
+   *
+  */
+  const char* name;
+
+};
+
+/**
+ * @brief The type of the media metadata.
+ *
+ */
+enum MEDIA_PLAYER_METADATA_TYPE {
+  /** The type is unknown.
+   */
+  PLAYER_METADATA_TYPE_UNKNOWN = 0,
+  /** The type is SEI.
+   */
+  PLAYER_METADATA_TYPE_SEI = 1,
+};
+
+struct CacheStatistics {
+  /**  total data size of uri
+   */
+  int64_t fileSize;
+  /**  data of uri has cached
+   */
+  int64_t cacheSize;
+  /**  data of uri has downloaded
+   */
+  int64_t downloadSize;
+};
+
+/**
+ * @brief The real time statistics of the media stream being played.
+ *
+ */
+struct PlayerPlaybackStats {
+  /**  Video fps.
+   */
+  int videoFps;
+  /**  Video bitrate (Kbps).
+   */
+  int videoBitrateInKbps;
+  /**  Audio bitrate (Kbps).
+   */
+  int audioBitrateInKbps;
+  /**  Total bitrate (Kbps).
+   */
+  int totalBitrateInKbps;
+};
+
+/**
+ * @brief The updated information of media player.
+ *
+ */
+struct PlayerUpdatedInfo {
+  /** @technical preview
+   */
+  const char* internalPlayerUuid;
+  /** The device ID of the playback device.
+   */
+  const char* deviceId;
+  /**  Video height.
+   */
+  int videoHeight;
+  /**  Video width.
+   */
+  int videoWidth;
+  /**  Audio sample rate.
+   */
+  int audioSampleRate;
+  /**  The audio channel number.
+   */
+  int audioChannels;
+  /**  The bit number of each audio sample.
+   */
+  int audioBitsPerSample;
+
+  PlayerUpdatedInfo()
+      : internalPlayerUuid(NULL),
+        deviceId(NULL),
+        videoHeight(0),
+        videoWidth(0),
+        audioSampleRate(0),
+        audioChannels(0),
+        audioBitsPerSample(0) {}
+};
+
+/**
+ * The custom data source provides a data stream input callback, and the player will continue to call back this interface, requesting the user to fill in the data that needs to be played.
+ */
+class IMediaPlayerCustomDataProvider {
+public:
+    
+    /**
+     * @brief The player requests to read the data callback, you need to fill the specified length of data into the buffer
+     * @param buffer the buffer pointer that you need to fill data.
+     * @param bufferSize the bufferSize need to fill of the buffer pointer.
+     * @return you need return offset value if succeed. return 0 if failed.
+     */
+    virtual int onReadData(unsigned char *buffer, int bufferSize) = 0;
+    
+    /**
+     * @brief The Player seek event callback, you need to operate the corresponding stream seek operation, You can refer to the definition of lseek() at https://man7.org/linux/man-pages/man2/lseek.2.html
+     * @param offset the value of seek offset.
+     * @param whence the postion of start seeking, the directive whence as follows:
+     * 0 - SEEK_SET : The file offset is set to offset bytes.
+     * 1 - SEEK_CUR : The file offset is set to its current location plus offset bytes.
+     * 2 - SEEK_END : The file offset is set to the size of the file plus offset bytes.
+     * 65536 - AVSEEK_SIZE : Optional. Passing this as the "whence" parameter to a seek function causes it to return the filesize without seeking anywhere.
+     * @return
+     * whence == 65536, return filesize if you need.
+     * whence >= 0 && whence < 3 , return offset value if succeed. return -1 if failed.
+     */
+    virtual int64_t onSeek(int64_t offset, int whence) = 0;
+    
+    virtual ~IMediaPlayerCustomDataProvider() {}
+};
+
+struct MediaSource {
+  /**
+   * The URL of the media file that you want to play.
+   */
+  const char* url;
+  /**
+   * The URI of the media file
+   *
+   * When caching is enabled, if the url cannot distinguish the cache file name,
+   * the uri must be able to ensure that the cache file name corresponding to the url is unique.
+   */
+  const char* uri;
+  /**
+   * Set the starting position for playback, in ms.
+   */
+  int64_t startPos;
+  /**
+   * Determines whether to autoplay after opening a media resource.
+   * - true: (Default) Autoplay after opening a media resource.
+   * - false: Do not autoplay after opening a media resource.
+   */
+  bool autoPlay;
+  /**
+   * Determines whether to enable cache streaming to local files. If enable cached, the media player will
+   * use the url or uri as the cache index.
+   *
+   * @note
+   * The local cache function only supports on-demand video/audio streams and does not support live streams.
+   * Caching video and audio files based on the HLS protocol (m3u8) to your local device is not supported.
+   *
+   * - true: Enable cache.
+   * - false: (Default) Disable cache.
+   */
+  bool enableCache;
+  /**
+   * Determines whether to enable multi-track audio stream decoding.
+   * Then you can select multi audio track of the media file for playback or publish to channel
+   *
+   * @note
+   * If you use the selectMultiAudioTrack API, you must set enableMultiAudioTrack to true.
+   *
+   * - true: Enable MultiAudioTrack;.
+   * - false: (Default) Disable MultiAudioTrack;.
+   */
+  bool enableMultiAudioTrack;
+  /**
+   * Determines whether the opened media resource is a stream through the Agora Broadcast Streaming Network(CDN).
+   * - true: It is a stream through the Agora Broadcast Streaming Network.
+   * - false: (Default) It is not a stream through the Agora Broadcast Streaming Network.
+   */
+  Optional<bool> isAgoraSource;
+  /**
+   * Determines whether the opened media resource is a live stream. If is a live stream, it can speed up the opening of media resources.
+   * - true: It is a live stream.
+   * - false: (Default) It is not is a live stream.
+   */
+  Optional<bool> isLiveSource;
+  /**
+   * External custom data source object
+   */
+  IMediaPlayerCustomDataProvider* provider;
+
+  MediaSource() : url(NULL), uri(NULL), startPos(0), autoPlay(true), enableCache(false),
+                  enableMultiAudioTrack(false), provider(NULL){
+  }
+};
+
+}  // namespace base
+}  // namespace media
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/AgoraOptional.h b/headers/rtc_4.3.2.11/include/AgoraOptional.h
new file mode 100644
index 0000000..a72e42b
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/AgoraOptional.h
@@ -0,0 +1,891 @@
+// Copyright (c) 2019 Agora.io. All rights reserved
+
+// This program is confidential and proprietary to Agora.io.
+// And may not be copied, reproduced, modified, disclosed to others, published
+// or used, in whole or in part, without the express prior written permission
+// of Agora.io.
+#pragma once
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#include <type_traits>
+#endif
+#include <utility>
+
+#ifndef CONSTEXPR
+#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+#endif  // !CONSTEXPR
+
+#ifndef NOEXCEPT
+#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+#define NOEXCEPT(Expr) noexcept(Expr)
+#else
+#define NOEXCEPT(Expr)
+#endif
+#endif  // !NOEXCEPT
+
+namespace agora {
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
+struct in_place_t {};
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
+struct nullopt_t {
+  CONSTEXPR explicit nullopt_t(int) {}
+};
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/in_place
+/*CONSTEXPR*/ const in_place_t in_place = {};
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt
+/*CONSTEXPR*/ const nullopt_t nullopt(0);
+
+// Forward declaration, which is refered by following helpers.
+template <typename T>
+class Optional;
+
+namespace internal {
+
+template <typename T>
+struct OptionalStorageBase {
+  // Initializing |empty_| here instead of using default member initializing
+  // to avoid errors in g++ 4.8.
+  CONSTEXPR OptionalStorageBase() : is_populated_(false), empty_('\0') {}
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <class... Args>
+  CONSTEXPR explicit OptionalStorageBase(in_place_t, Args&&... args)
+      : is_populated_(true), value_(std::forward<Args>(args)...) {}
+#else
+  CONSTEXPR explicit OptionalStorageBase(in_place_t, const T& _value)
+      : is_populated_(true), value_(_value) {}
+#endif
+  // When T is not trivially destructible we must call its
+  // destructor before deallocating its memory.
+  // Note that this hides the (implicitly declared) move constructor, which
+  // would be used for constexpr move constructor in OptionalStorage<T>.
+  // It is needed iff T is trivially move constructible. However, the current
+  // is_trivially_{copy,move}_constructible implementation requires
+  // is_trivially_destructible (which looks a bug, cf:
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 and
+  // http://cplusplus.github.io/LWG/lwg-active.html#2116), so it is not
+  // necessary for this case at the moment. Please see also the destructor
+  // comment in "is_trivially_destructible = true" specialization below.
+  ~OptionalStorageBase() {
+    if (is_populated_)
+      value_.~T();
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <class... Args>
+  void Init(Args&&... args) {
+    ::new (&value_) T(std::forward<Args>(args)...);
+    is_populated_ = true;
+  }
+#else
+  void Init(const T& _value) {
+    ::new (&value_) T(_value);
+    is_populated_ = true;
+  }
+#endif
+
+  bool is_populated_;
+
+  union {
+    // |empty_| exists so that the union will always be initialized, even when
+    // it doesn't contain a value. Union members must be initialized for the
+    // constructor to be 'constexpr'.
+    char empty_;
+    T value_;
+  };
+};
+
+// Implement conditional constexpr copy and move constructors. These are
+// constexpr if is_trivially_{copy,move}_constructible<T>::value is true
+// respectively. If each is true, the corresponding constructor is defined as
+// "= default;", which generates a constexpr constructor (In this case,
+// the condition of constexpr-ness is satisfied because the base class also has
+// compiler generated constexpr {copy,move} constructors). Note that
+// placement-new is prohibited in constexpr.
+template <typename T>
+struct OptionalStorage : OptionalStorageBase<T> {
+  // This is no trivially {copy,move} constructible case. Other cases are
+  // defined below as specializations.
+
+  // Accessing the members of template base class requires explicit
+  // declaration.
+  using OptionalStorageBase<T>::is_populated_;
+  using OptionalStorageBase<T>::value_;
+  using OptionalStorageBase<T>::Init;
+
+  // Inherit constructors (specifically, the in_place constructor).
+  //using OptionalStorageBase<T>::OptionalStorageBase;
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <class... Args>
+  CONSTEXPR explicit OptionalStorage(in_place_t in_place, Args&&... args)
+      : OptionalStorageBase<T>(in_place, std::forward<Args>(args)...) {}
+#else
+  CONSTEXPR explicit OptionalStorage(in_place_t in_place, const T& _value)
+      : OptionalStorageBase<T>(in_place, _value) {}
+#endif
+
+  // User defined constructor deletes the default constructor.
+  // Define it explicitly.
+  OptionalStorage() {}
+
+  OptionalStorage(const OptionalStorage& other) {
+    if (other.is_populated_)
+      Init(other.value_);
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  OptionalStorage(OptionalStorage&& other) NOEXCEPT(std::is_nothrow_move_constructible<T>::value) {
+    if (other.is_populated_)
+      Init(std::move(other.value_));
+  }
+#endif
+};
+
+// Base class to support conditionally usable copy-/move- constructors
+// and assign operators.
+template <typename T>
+class OptionalBase {
+  // This class provides implementation rather than public API, so everything
+  // should be hidden. Often we use composition, but we cannot in this case
+  // because of C++ language restriction.
+ protected:
+  CONSTEXPR OptionalBase() {}
+  CONSTEXPR OptionalBase(const OptionalBase& other) : storage_(other.storage_) {}
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR OptionalBase(OptionalBase&& other) : storage_(std::move(other.storage_)) {}
+
+  template <class... Args>
+  CONSTEXPR explicit OptionalBase(in_place_t, Args&&... args)
+      : storage_(in_place, std::forward<Args>(args)...) {}
+#else
+  CONSTEXPR explicit OptionalBase(in_place_t, const T& _value)
+      : storage_(in_place, _value) {}
+#endif
+
+  // Implementation of converting constructors.
+  template <typename U>
+  explicit OptionalBase(const OptionalBase<U>& other) {
+    if (other.storage_.is_populated_)
+      storage_.Init(other.storage_.value_);
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <typename U>
+  explicit OptionalBase(OptionalBase<U>&& other) {
+    if (other.storage_.is_populated_)
+      storage_.Init(std::move(other.storage_.value_));
+  }
+#endif
+
+  ~OptionalBase() {}
+
+  OptionalBase& operator=(const OptionalBase& other) {
+    CopyAssign(other);
+    return *this;
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  OptionalBase& operator=(OptionalBase&& other) NOEXCEPT(
+      std::is_nothrow_move_assignable<T>::value &&
+          std::is_nothrow_move_constructible<T>::value) {
+    MoveAssign(std::move(other));
+    return *this;
+  }
+#endif
+
+  template <typename U>
+  void CopyAssign(const OptionalBase<U>& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(other.storage_.value_);
+    else
+      FreeIfNeeded();
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <typename U>
+  void MoveAssign(OptionalBase<U>&& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(std::move(other.storage_.value_));
+    else
+      FreeIfNeeded();
+  }
+#endif
+
+  template <typename U>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  void InitOrAssign(U&& value) {
+    if (storage_.is_populated_)
+      storage_.value_ = std::forward<U>(value);
+    else
+      storage_.Init(std::forward<U>(value));
+  }
+#else
+  void InitOrAssign(const U& value) {
+    if (storage_.is_populated_)
+      storage_.value_ = value;
+    else
+      storage_.Init(value);
+  }
+#endif
+
+
+  void FreeIfNeeded() {
+    if (!storage_.is_populated_)
+      return;
+    storage_.value_.~T();
+    storage_.is_populated_ = false;
+  }
+
+  // For implementing conversion, allow access to other typed OptionalBase
+  // class.
+  template <typename U>
+  friend class OptionalBase;
+
+  OptionalStorage<T> storage_;
+};
+
+// The following {Copy,Move}{Constructible,Assignable} structs are helpers to
+// implement constructor/assign-operator overloading. Specifically, if T is
+// is not movable but copyable, Optional<T>'s move constructor should not
+// participate in overload resolution. This inheritance trick implements that.
+template <bool is_copy_constructible>
+struct CopyConstructible {};
+
+template <>
+struct CopyConstructible<false> {
+  CONSTEXPR CopyConstructible() {}
+  CopyConstructible& operator=(const CopyConstructible&) { return *this; }
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR CopyConstructible(CopyConstructible&&) {}
+  CopyConstructible& operator=(CopyConstructible&&) { return *this; }
+#endif
+ private:
+  CONSTEXPR CopyConstructible(const CopyConstructible&);
+};
+
+template <bool is_move_constructible>
+struct MoveConstructible {};
+
+template <>
+struct MoveConstructible<false> {
+  CONSTEXPR MoveConstructible() {}
+  CONSTEXPR MoveConstructible(const MoveConstructible&) {}
+  MoveConstructible& operator=(const MoveConstructible&) { return *this; }
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  MoveConstructible& operator=(MoveConstructible&&) { return *this; }
+ private:
+  CONSTEXPR MoveConstructible(MoveConstructible&&);
+#endif
+};
+
+template <bool is_copy_assignable>
+struct CopyAssignable {};
+
+template <>
+struct CopyAssignable<false> {
+  CONSTEXPR CopyAssignable() {}
+  CONSTEXPR CopyAssignable(const CopyAssignable&) {}
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR CopyAssignable(CopyAssignable&&) {}
+  CopyAssignable& operator=(CopyAssignable&&) { return *this; }
+#endif
+ private:
+  CopyAssignable& operator=(const CopyAssignable&);
+};
+
+template <bool is_move_assignable>
+struct MoveAssignable {};
+
+template <>
+struct MoveAssignable<false> {
+  CONSTEXPR MoveAssignable() {}
+  CONSTEXPR MoveAssignable(const MoveAssignable&) {}
+  MoveAssignable& operator=(const MoveAssignable&) { return *this; }
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR MoveAssignable(MoveAssignable&&) {}
+
+ private:
+  MoveAssignable& operator=(MoveAssignable&&);
+#endif
+};
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+// Helper to conditionally enable converting constructors and assign operators.
+template <typename T, typename U>
+struct IsConvertibleFromOptional
+    : std::integral_constant<
+          bool,
+          std::is_constructible<T, Optional<U>&>::value ||
+              std::is_constructible<T, const Optional<U>&>::value ||
+              std::is_constructible<T, Optional<U>&&>::value ||
+              std::is_constructible<T, const Optional<U>&&>::value ||
+              std::is_convertible<Optional<U>&, T>::value ||
+              std::is_convertible<const Optional<U>&, T>::value ||
+              std::is_convertible<Optional<U>&&, T>::value ||
+              std::is_convertible<const Optional<U>&&, T>::value> {};
+
+template <typename T, typename U>
+struct IsAssignableFromOptional
+    : std::integral_constant<
+          bool,
+          IsConvertibleFromOptional<T, U>::value ||
+              std::is_assignable<T&, Optional<U>&>::value ||
+              std::is_assignable<T&, const Optional<U>&>::value ||
+              std::is_assignable<T&, Optional<U>&&>::value ||
+              std::is_assignable<T&, const Optional<U>&&>::value> {};
+
+// Forward compatibility for C++17.
+// Introduce one more deeper nested namespace to avoid leaking using std::swap.
+namespace swappable_impl {
+using std::swap;
+
+struct IsSwappableImpl {
+  // Tests if swap can be called. Check<T&>(0) returns true_type iff swap
+  // is available for T. Otherwise, Check's overload resolution falls back
+  // to Check(...) declared below thanks to SFINAE, so returns false_type.
+  template <typename T>
+  static auto Check(int)
+      -> decltype(swap(std::declval<T>(), std::declval<T>()), std::true_type());
+
+  template <typename T>
+  static std::false_type Check(...);
+};
+}  // namespace swappable_impl
+template <typename T>
+struct IsSwappable : decltype(swappable_impl::IsSwappableImpl::Check<T&>(0)) {};
+#endif
+}  // namespace internal
+
+// On Windows, by default, empty-base class optimization does not work,
+// which means even if the base class is empty struct, it still consumes one
+// byte for its body. __declspec(empty_bases) enables the optimization.
+// cf)
+// https://blogs.msdn.microsoft.com/vcblog/2016/03/30/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/
+#if defined(_WIN32)
+#define OPTIONAL_DECLSPEC_EMPTY_BASES __declspec(empty_bases)
+#else
+#define OPTIONAL_DECLSPEC_EMPTY_BASES
+#endif
+
+// Optional is a Chromium version of the C++17 optional class:
+// std::optional documentation:
+// http://en.cppreference.com/w/cpp/utility/optional
+// Chromium documentation:
+// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
+//
+// These are the differences between the specification and the implementation:
+// - Constructors do not use 'constexpr' as it is a C++14 extension.
+// - 'constexpr' might be missing in some places for reasons specified locally.
+// - No exceptions are thrown, because they are banned from Chromium.
+//   Marked noexcept for only move constructor and move assign operators.
+// - All the non-members are in the 'base' namespace instead of 'std'.
+//
+// Note that T cannot have a constructor T(Optional<T>) etc. Optional<T> checks
+// T's constructor (specifically via IsConvertibleFromOptional), and in the
+// check whether T can be constructible from Optional<T>, which is recursive
+// so it does not work. As of Feb 2018, std::optional C++17 implementation in
+// both clang and gcc has same limitation. MSVC SFINAE looks to have different
+// behavior, but anyway it reports an error, too.
+template <typename T>
+class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
+    : public internal::OptionalBase<T>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+      , public internal::CopyConstructible<std::is_copy_constructible<T>::value>,
+      public internal::MoveConstructible<std::is_move_constructible<T>::value>,
+      public internal::CopyAssignable<std::is_copy_constructible<T>::value &&
+                                      std::is_copy_assignable<T>::value>,
+      public internal::MoveAssignable<std::is_move_constructible<T>::value &&
+                                      std::is_move_assignable<T>::value> 
+#endif
+{
+ public:
+#undef OPTIONAL_DECLSPEC_EMPTY_BASES
+
+  typedef T value_type;
+
+  // Defer default/copy/move constructor implementation to OptionalBase.
+  CONSTEXPR Optional() {}
+  CONSTEXPR Optional(const Optional& other) : internal::OptionalBase<T>(other) {}
+
+  CONSTEXPR Optional(nullopt_t) {}  // NOLINT(runtime/explicit)
+
+  // Converting copy constructor. "explicit" only if
+  // std::is_convertible<const U&, T>::value is false. It is implemented by
+  // declaring two almost same constructors, but that condition in enable_if_t
+  // is different, so that either one is chosen, thanks to SFINAE.
+  template <typename U>
+  Optional(const Optional<U>& other) : internal::OptionalBase<T>(other) {}
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  // Converting move constructor. Similar to converting copy constructor,
+  // declaring two (explicit and non-explicit) constructors.
+  template <typename U>
+  Optional(Optional<U>&& other) : internal::OptionalBase<T>(std::move(other)) {}
+
+  template <class... Args>
+  CONSTEXPR explicit Optional(in_place_t, Args&&... args)
+      : internal::OptionalBase<T>(in_place, std::forward<Args>(args)...) {}
+
+  template <class U, class... Args>
+  CONSTEXPR explicit Optional(in_place_t,
+                              std::initializer_list<U> il,
+                              Args&&... args)
+      : internal::OptionalBase<T>(in_place, il, std::forward<Args>(args)...) {}
+#else
+  CONSTEXPR explicit Optional(in_place_t, const T& _value)
+      : internal::OptionalBase<T>(in_place, _value) {}
+  template <class U>
+  CONSTEXPR explicit Optional(in_place_t,
+                              const U il[],
+                              const T& _value)
+      : internal::OptionalBase<T>(in_place, il, _value) {}
+#endif
+
+  // Forward value constructor. Similar to converting constructors,
+  // conditionally explicit.
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <typename U = value_type>
+  CONSTEXPR Optional(U&& value)
+      : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
+#else
+  template <typename U>
+  CONSTEXPR Optional(const U& value)
+      : internal::OptionalBase<T>(in_place, value) {}
+#endif
+
+  ~Optional() {}
+
+  // Defer copy-/move- assign operator implementation to OptionalBase.
+  Optional& operator=(const Optional& other) {
+    if (&other  == this) {
+      return *this;
+    }
+
+    internal::OptionalBase<T>::operator=(other);
+    return *this;
+  }
+
+  Optional& operator=(nullopt_t) {
+    FreeIfNeeded();
+    return *this;
+  }
+
+  // Perfect-forwarded assignment.
+  template <typename U>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  Optional& operator=(U&& value) {
+    InitOrAssign(std::forward<U>(value));
+    return *this;
+  }
+#else
+  Optional& operator=(const U& value) {
+    InitOrAssign(value);
+    return *this;
+  }
+#endif
+
+  // Copy assign the state of other.
+  template <typename U>
+  Optional& operator=(const Optional<U>& other) {
+    CopyAssign(other);
+    return *this;
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  // Move assign the state of other.
+  template <typename U>
+  Optional& operator=(Optional<U>&& other) {
+    MoveAssign(std::move(other));
+    return *this;
+  }
+#endif
+
+  const T* operator->() const {
+    return &storage_.value_;
+  }
+
+  T* operator->() {
+    return &storage_.value_;
+  }
+
+  const T& operator*() const {
+    return storage_.value_;
+  }
+
+  T& operator*() {
+    return storage_.value_;
+  }
+
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR explicit operator bool() const { return storage_.is_populated_; }
+#else
+  CONSTEXPR operator bool() const { return storage_.is_populated_; }
+#endif
+
+  CONSTEXPR bool has_value() const { return storage_.is_populated_; }
+
+#if 1
+  const T& value() const {
+    return storage_.value_;
+  }
+
+  template <class U>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR T value_or(U&& default_value) const {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_copy_constructible<T>::value,
+    //               "T must be copy constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? value()
+               : static_cast<T>(std::forward<U>(default_value));
+  }
+#else
+  CONSTEXPR T value_or(const U& default_value) const {
+    return storage_.is_populated_
+               ? value()
+               : static_cast<T>(default_value);
+  }
+#endif
+#else
+  const T& value() const & {
+    return storage_.value_;
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  const T&& value() const && {
+    return std::move(storage_.value_);
+  }
+#endif
+
+  template <class U>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR T value_or(U&& default_value) const & {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_copy_constructible<T>::value,
+    //               "T must be copy constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? value()
+               : static_cast<T>(std::forward<U>(default_value));
+  }
+#else
+  CONSTEXPR T value_or(const U& default_value) const & {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_copy_constructible<T>::value,
+    //               "T must be copy constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? value()
+               : static_cast<T>(default_value);
+  }
+#endif
+
+  template <class U>
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  CONSTEXPR T value_or(U&& default_value) const && {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_move_constructible<T>::value,
+    //               "T must be move constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? std::move(value())
+               : static_cast<T>(std::forward<U>(default_value));
+  }
+#endif
+#endif  // 1
+
+  void swap(Optional& other) {
+    if (!storage_.is_populated_ && !other.storage_.is_populated_)
+      return;
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+    if (storage_.is_populated_ != other.storage_.is_populated_) {
+      if (storage_.is_populated_) {
+        other.storage_.Init(std::move(storage_.value_));
+        FreeIfNeeded();
+      } else {
+        storage_.Init(std::move(other.storage_.value_));
+        other.FreeIfNeeded();
+      }
+      return;
+    }
+#endif
+    using std::swap;
+    swap(**this, *other);
+  }
+
+  void reset() { FreeIfNeeded(); }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  template <class... Args>
+  T& emplace(Args&&... args) {
+    FreeIfNeeded();
+    storage_.Init(std::forward<Args>(args)...);
+    return storage_.value_;
+  }
+
+  template <class U, class... Args>
+  T& emplace(std::initializer_list<U> il, Args&&... args) {
+    FreeIfNeeded();
+    storage_.Init(il, std::forward<Args>(args)...);
+    return storage_.value_;
+  }
+#else
+  T& emplace(const T& _value) {
+    FreeIfNeeded();
+    storage_.Init(_value);
+    return storage_.value_;
+  }
+  template <class U>
+  T& emplace(const U il[], const T& _value) {
+    FreeIfNeeded();
+    storage_.Init(il, _value);
+    return storage_.value_;
+  }
+#endif
+
+ private:
+  // Accessing template base class's protected member needs explicit
+  // declaration to do so.
+  using internal::OptionalBase<T>::CopyAssign;
+  using internal::OptionalBase<T>::FreeIfNeeded;
+  using internal::OptionalBase<T>::InitOrAssign;
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  using internal::OptionalBase<T>::MoveAssign;
+#endif
+  using internal::OptionalBase<T>::storage_;
+};
+
+// Here after defines comparation operators. The definition follows
+// http://en.cppreference.com/w/cpp/utility/optional/operator_cmp
+// while bool() casting is replaced by has_value() to meet the chromium
+// style guide.
+template <class T, class U>
+bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (lhs.has_value() != rhs.has_value())
+    return false;
+  if (!lhs.has_value())
+    return true;
+  return *lhs == *rhs;
+}
+
+template <class T, class U>
+bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (lhs.has_value() != rhs.has_value())
+    return true;
+  if (!lhs.has_value())
+    return false;
+  return *lhs != *rhs;
+}
+
+template <class T, class U>
+bool operator<(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!rhs.has_value())
+    return false;
+  if (!lhs.has_value())
+    return true;
+  return *lhs < *rhs;
+}
+
+template <class T, class U>
+bool operator<=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!lhs.has_value())
+    return true;
+  if (!rhs.has_value())
+    return false;
+  return *lhs <= *rhs;
+}
+
+template <class T, class U>
+bool operator>(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!lhs.has_value())
+    return false;
+  if (!rhs.has_value())
+    return true;
+  return *lhs > *rhs;
+}
+
+template <class T, class U>
+bool operator>=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!rhs.has_value())
+    return true;
+  if (!lhs.has_value())
+    return false;
+  return *lhs >= *rhs;
+}
+
+template <class T>
+CONSTEXPR bool operator==(const Optional<T>& opt, nullopt_t) {
+  return !opt;
+}
+
+template <class T>
+CONSTEXPR bool operator==(nullopt_t, const Optional<T>& opt) {
+  return !opt;
+}
+
+template <class T>
+CONSTEXPR bool operator!=(const Optional<T>& opt, nullopt_t) {
+  return opt.has_value();
+}
+
+template <class T>
+CONSTEXPR bool operator!=(nullopt_t, const Optional<T>& opt) {
+  return opt.has_value();
+}
+
+template <class T>
+CONSTEXPR bool operator<(const Optional<T>& , nullopt_t) {
+  return false;
+}
+
+template <class T>
+CONSTEXPR bool operator<(nullopt_t, const Optional<T>& opt) {
+  return opt.has_value();
+}
+
+template <class T>
+CONSTEXPR bool operator<=(const Optional<T>& opt, nullopt_t) {
+  return !opt;
+}
+
+template <class T>
+CONSTEXPR bool operator<=(nullopt_t, const Optional<T>& ) {
+  return true;
+}
+
+template <class T>
+CONSTEXPR bool operator>(const Optional<T>& opt, nullopt_t) {
+  return opt.has_value();
+}
+
+template <class T>
+CONSTEXPR bool operator>(nullopt_t, const Optional<T>& ) {
+  return false;
+}
+
+template <class T>
+CONSTEXPR bool operator>=(const Optional<T>& , nullopt_t) {
+  return true;
+}
+
+template <class T>
+CONSTEXPR bool operator>=(nullopt_t, const Optional<T>& opt) {
+  return !opt;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator==(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt == value : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator==(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value == *opt : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator!=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt != value : true;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator!=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value != *opt : true;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator<(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt < value : true;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator<(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value < *opt : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator<=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt <= value : true;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator<=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value <= *opt : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator>(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt > value : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator>(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value > *opt : true;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator>=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt >= value : false;
+}
+
+template <class T, class U>
+CONSTEXPR bool operator>=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value >= *opt : true;
+}
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+template <class T, class... Args>
+CONSTEXPR Optional<T> make_optional(Args&&... args) {
+  return Optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+template <class T, class U, class... Args>
+CONSTEXPR Optional<T> make_optional(std::initializer_list<U> il,
+                                    Args&&... args) {
+  return Optional<T>(in_place, il, std::forward<Args>(args)...);
+}
+#endif
+
+// Partial specialization for a function template is not allowed. Also, it is
+// not allowed to add overload function to std namespace, while it is allowed
+// to specialize the template in std. Thus, swap() (kind of) overloading is
+// defined in base namespace, instead.
+template <class T>
+void swap(Optional<T>& lhs, Optional<T>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace agora
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+namespace std {
+template <class T>
+struct hash<agora::Optional<T> > {
+  size_t operator()(const agora::Optional<T>& opt) const {
+    return opt == agora::nullopt ? 0 : std::hash<T>()(*opt);
+  }
+};
+}  // namespace std
+#endif
+#undef CONSTEXPR
+#undef NOEXCEPT
diff --git a/headers/rtc_4.3.2.11/include/AgoraRefPtr.h b/headers/rtc_4.3.2.11/include/AgoraRefPtr.h
new file mode 100644
index 0000000..d0ac17b
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/AgoraRefPtr.h
@@ -0,0 +1,156 @@
+
+// Copyright (c) 2019 Agora.io. All rights reserved
+
+// This program is confidential and proprietary to Agora.io.
+// And may not be copied, reproduced, modified, disclosed to others, published
+// or used, in whole or in part, without the express prior written permission
+// of Agora.io.
+
+#pragma once
+
+#include <memory>
+#if !(__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
+#include <cstddef>
+#endif
+#ifndef OPTIONAL_ENUM_CLASS
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_ENUM_CLASS enum class
+#else
+#define OPTIONAL_ENUM_CLASS enum
+#endif
+#endif
+
+namespace agora {
+
+OPTIONAL_ENUM_CLASS RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };
+
+// Interfaces where refcounting is part of the public api should
+// inherit this abstract interface. The implementation of these
+// methods is usually provided by the RefCountedObject template class,
+// applied as a leaf in the inheritance tree.
+class RefCountInterface {
+ public:
+  virtual void AddRef() const = 0;
+  virtual RefCountReleaseStatus Release() const = 0;
+  virtual bool HasOneRef() const = 0;
+
+  // Non-public destructor, because Release() has exclusive responsibility for
+  // destroying the object.
+ protected:
+  virtual ~RefCountInterface() {}
+};
+
+template <class T>
+class agora_refptr {
+ public:
+  agora_refptr() : ptr_(NULL) {}
+
+  agora_refptr(T* p) : ptr_(p) {
+    if (ptr_) ptr_->AddRef();
+  }
+
+  template<typename U>
+  agora_refptr(U* p) : ptr_(p) {
+    if (ptr_) ptr_->AddRef();
+  }
+
+  agora_refptr(const agora_refptr<T>& r) : ptr_(r.get()) {
+    if (ptr_) ptr_->AddRef();
+  }
+
+  template <typename U>
+  agora_refptr(const agora_refptr<U>& r) : ptr_(r.get()) {
+    if (ptr_) ptr_->AddRef();
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  agora_refptr(agora_refptr<T>&& r) : ptr_(r.move()) {}
+
+  template <typename U>
+  agora_refptr(agora_refptr<U>&& r) : ptr_(r.move()) {}
+#endif
+
+  ~agora_refptr() {
+    reset();
+  }
+
+  T* get() const { return ptr_; }
+  operator bool() const { return (ptr_ != NULL); }
+
+  T* operator->() const { return  ptr_; }
+  T& operator*() const { return *ptr_; }
+
+  // Returns the (possibly null) raw pointer, and makes the agora_refptr hold a
+  // null pointer, all without touching the reference count of the underlying
+  // pointed-to object. The object is still reference counted, and the caller of
+  // move() is now the proud owner of one reference, so it is responsible for
+  // calling Release() once on the object when no longer using it.
+  T* move() {
+    T* retVal = ptr_;
+    ptr_ = NULL;
+    return retVal;
+  }
+
+  agora_refptr<T>& operator=(T* p) {
+    if (ptr_ == p) return *this;
+
+    if (p) p->AddRef();
+    if (ptr_) ptr_->Release();
+    ptr_ = p;
+    return *this;
+  }
+
+  agora_refptr<T>& operator=(const agora_refptr<T>& r) {
+    return *this = r.get();
+  }
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+  agora_refptr<T>& operator=(agora_refptr<T>&& r) {
+    agora_refptr<T>(std::move(r)).swap(*this);
+    return *this;
+  }
+
+  template <typename U>
+  agora_refptr<T>& operator=(agora_refptr<U>&& r) {
+    agora_refptr<T>(std::move(r)).swap(*this);
+    return *this;
+  }
+#endif
+
+  // For working with std::find()
+  bool operator==(const agora_refptr<T>& r) const { return ptr_ == r.ptr_; }
+
+  // For working with std::set
+  bool operator<(const agora_refptr<T>& r) const { return ptr_ < r.ptr_; }
+
+  void swap(T** pp) {
+    T* p = ptr_;
+    ptr_ = *pp;
+    *pp = p;
+  }
+
+  void swap(agora_refptr<T>& r) { swap(&r.ptr_); }
+
+  void reset() {
+    if (ptr_) {
+      ptr_->Release();
+      ptr_ = NULL;
+    }
+  }
+
+ protected:
+  T* ptr_;
+};
+
+}  // namespace agora
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+namespace std {
+template <typename T>
+struct hash<agora::agora_refptr<T>> {
+  std::size_t operator()(const agora::agora_refptr<T>& k) const {
+    return reinterpret_cast<size_t>(k.get());
+  }
+};
+}  // namespace std
+#endif
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/include/IAgoraH265Transcoder.h b/headers/rtc_4.3.2.11/include/IAgoraH265Transcoder.h
new file mode 100644
index 0000000..3dd465b
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraH265Transcoder.h
@@ -0,0 +1,178 @@
+//
+//  Agora Media SDK
+//
+//  Copyright (c) 2022 Agora IO. All rights reserved.
+//
+
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+
+namespace agora{
+namespace rtc{
+
+/**
+ * The result of IH265Transcoder interface invoking.
+*/
+enum H265_TRANSCODE_RESULT {
+  /**
+   * -1: Unknown error.
+  */
+  H265_TRANSCODE_RESULT_UNKNOWN = -1,
+  /**
+   * 0: The request of operation is successfully.
+  */
+  H265_TRANSCODE_RESULT_SUCCESS = 0,
+  /**
+   * 1: This request is invalid. Possible reasons include incorrect parameters.
+  */
+  H265_TRANSCODE_RESULT_REQUEST_INVALID = 1,
+  /**
+   * 2: Authentication failed, please check for correctness of token.
+  */
+  H265_TRANSCODE_RESULT_UNAUTHORIZED = 2,
+  /**
+   * 3: The token is expired, please update token.
+  */
+  H265_TRANSCODE_RESULT_TOKEN_EXPIRED = 3,
+  /**
+   * 4: No permission to access the interface.
+  */
+  H265_TRANSCODE_RESULT_FORBIDDEN = 4,
+  /**
+   * 5: The url of request is not found.
+  */
+  H265_TRANSCODE_RESULT_NOT_FOUND = 5,
+  /**
+   * 6: The request encountered a conflict, please try again.
+  */
+  H265_TRANSCODE_RESULT_CONFLICTED = 6,
+  /**
+   * 7: Content type not supported.
+  */
+  H265_TRANSCODE_RESULT_NOT_SUPPORTED = 7,
+  /**
+   * 8: The requests are too frequent.
+  */
+  H265_TRANSCODE_RESULT_TOO_OFTEN = 8,
+  /**
+   * 9: Internal Server Error, you can try sending the request again.
+  */
+  H265_TRANSCODE_RESULT_SERVER_INTERNAL_ERROR = 9,
+  /**
+   * 10: Service is unavailable.
+  */
+  H265_TRANSCODE_RESULT_SERVICE_UNAVAILABLE = 10
+};
+
+/**
+ * The IH265TranscoderObserver class
+*/
+class IH265TranscoderObserver {
+ public:
+  virtual ~IH265TranscoderObserver() {};
+
+  /**
+   * Use to notify the result of invoking enableTranscode interface.
+   * @param result Result of invoking enableTranscode interface. There are some processing advice below of result.
+   * - H265_TRANSCODE_RESULT_REQUEST_INVALID: Channel or uid param have a mistake, you need to check them for correctness.
+   * - H265_TRANSCODE_RESULT_UNAUTHORIZED: Authentication failed, please check for correctness of token.
+   * - H265_TRANSCODE_RESULT_TOKEN_EXPIRED: The token has expired, you need to generate a new token.
+   * - H265_TRANSCODE_RESULT_FORBIDDEN: You need to contact agora staff to add the vid whitelist.
+   * - H265_TRANSCODE_RESULT_NOT_FOUND: Indicates that the network may be faulty.
+   * - H265_TRANSCODE_RESULT_TOO_OFTEN: Request is too often, please request again later.
+   * - H265_TRANSCODE_RESULT_SERVER_INTERNAL_ERROR: The service has an internal error. A request can be made again.
+  */
+  virtual void onEnableTranscode(H265_TRANSCODE_RESULT result) = 0;
+
+  /**
+   * Use to notify the result of invoking queryChannel interface.
+   * @param result Result of invoking queryChannel interface. There are some processing advice below of result.
+   * - H265_TRANSCODE_RESULT_UNAUTHORIZED: Authentication failed, please check for correctness of token.
+   * - H265_TRANSCODE_RESULT_TOKEN_EXPIRED: The token has expired, you need to generate a new token.
+   * - H265_TRANSCODE_RESULT_NOT_FOUND: Indicates that the network may be faulty or the channel param may be is empty.
+   * - H265_TRANSCODE_RESULT_TOO_OFTEN: Request is too often, please request again later.
+   * - H265_TRANSCODE_RESULT_SERVER_INTERNAL_ERROR: The service has an internal error. A request can be made again.
+   * 
+   * @param originChannel Origin channel id
+   * @param transcodeChannel Transcode channel id
+  */
+  virtual void onQueryChannel(H265_TRANSCODE_RESULT result, const char* originChannel, const char* transcodeChannel) = 0;
+  
+  /** Use to notify the result of invoking triggerTranscode interface.
+   * @param result Result of invoking triggerTranscode interface. There are some processing advice below of result.
+   * - H265_TRANSCODE_RESULT_UNAUTHORIZED: Authentication failed, please check for correctness of token.
+   * - H265_TRANSCODE_RESULT_TOKEN_EXPIRED: The token has expired, you need to generate a new token.
+   * - H265_TRANSCODE_RESULT_NOT_FOUND: Indicates that the network may be faulty or the channel param may be is empty.
+   * - H265_TRANSCODE_RESULT_CONFLICTED: The request of trigger transcode is conflicted, please try again.
+   * - H265_TRANSCODE_RESULT_TOO_OFTEN: Request is too often, please request again later
+   * - H265_TRANSCODE_RESULT_SERVER_INTERNAL_ERROR: The service has an internal error. A request can be made again.
+   * - H265_TRANSCODE_RESULT_SERVICE_UNAVAILABLE: May be the number of transcode service is over the limit.
+  */
+  virtual void onTriggerTranscode(H265_TRANSCODE_RESULT result) = 0;
+
+};
+
+/**
+ * The IH265Transcoder class
+*/
+class IH265Transcoder : public RefCountInterface {
+ public:
+  /**
+   * Enable transcoding for a channel.
+   * @param token The token for authentication.
+   * @param channel The unique channel name for the AgoraRTC session in the string format.
+   * @param uid  User ID.
+   * @return
+   * -  0: Success.
+   * - <0: Failure.
+  */
+  virtual int enableTranscode(const char *token, const char *channel, uid_t uid) = 0;
+
+  /**
+   * Query the transcoded channel of a channel.
+   * @param token The token for authentication.
+   * @param channel The unique channel name for the AgoraRTC session in the string format.
+   * @param uid  User ID.
+   * @return
+   * -  0: Success.
+   * - <0: Failure.
+  */
+  virtual int queryChannel(const char *token, const char *channel, uid_t uid) = 0;
+
+  /**
+   * Trigger channel transcoding.
+   * @param token The token for authentication.
+   * @param channel The unique channel name for the AgoraRTC session in the string format.
+   * @param uid  User ID.
+   * @return
+   * -  0: Success.
+   * - <0: Failure.
+  */
+  virtual int triggerTranscode(const char* token, const char* channel, uid_t uid) = 0;
+  /**
+   * Register a IH265TranscoderObserver object.
+   * @param observer IH265TranscoderObserver.
+   * @return
+   * -  0: Success.
+   * - <0: Failure.
+  */
+  virtual int registerTranscoderObserver(IH265TranscoderObserver *observer) = 0;
+  /**
+   * Unregister a IH265TranscoderObserver object.
+   * @param observer IH265TranscoderObserver.
+   * @return
+   * -  0: Success.
+   * - <0: Failure.
+  */
+  virtual int unregisterTranscoderObserver(IH265TranscoderObserver *observer) = 0;
+
+ 
+ protected:
+  virtual ~IH265Transcoder() {};
+
+};
+
+} // namespace rtc
+} // namespace agora
\ No newline at end of file
diff --git a/headers/rtc_4.3.2.11/include/IAgoraLog.h b/headers/rtc_4.3.2.11/include/IAgoraLog.h
new file mode 100644
index 0000000..876a75c
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraLog.h
@@ -0,0 +1,99 @@
+//
+//  Agora Media SDK
+//
+//  Copyright (c) 2015 Agora IO. All rights reserved.
+//
+#pragma once
+
+#include <cstdlib>
+#include <stdint.h>
+
+#ifndef OPTIONAL_ENUM_CLASS
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_ENUM_CLASS enum class
+#else
+#define OPTIONAL_ENUM_CLASS enum
+#endif
+#endif
+
+#ifndef OPTIONAL_LOG_LEVEL_SPECIFIER
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define OPTIONAL_LOG_LEVEL_SPECIFIER LOG_LEVEL::
+#else
+#define OPTIONAL_LOG_LEVEL_SPECIFIER
+#endif
+#endif
+
+namespace agora {
+namespace commons {
+
+/**
+ * Supported logging severities of SDK
+ */
+OPTIONAL_ENUM_CLASS LOG_LEVEL {
+  LOG_LEVEL_NONE = 0x0000,
+  LOG_LEVEL_INFO = 0x0001,
+  LOG_LEVEL_WARN = 0x0002,
+  LOG_LEVEL_ERROR = 0x0004,
+  LOG_LEVEL_FATAL = 0x0008,
+  LOG_LEVEL_API_CALL = 0x0010,
+  LOG_LEVEL_DEBUG = 0x0020,
+};
+
+/*
+The SDK uses ILogWriter class Write interface to write logs as application
+The application inherits the methods Write() to implentation their own  log writ
+
+Write has default implementation, it writes logs to files.
+Application can use setLogFile() to change file location, see description of set
+*/
+class ILogWriter {
+ public:
+  /** user defined log Write function
+  @param level log level
+  @param message log message content
+  @param length log message length
+  @return
+   - 0: success
+   - <0: failure
+  */
+  virtual int32_t writeLog(LOG_LEVEL level, const char* message, uint16_t length) = 0;
+
+  virtual ~ILogWriter() {}
+};
+
+enum LOG_FILTER_TYPE {
+  LOG_FILTER_OFF = 0,
+  LOG_FILTER_DEBUG = 0x080f,
+  LOG_FILTER_INFO = 0x000f,
+  LOG_FILTER_WARN = 0x000e,
+  LOG_FILTER_ERROR = 0x000c,
+  LOG_FILTER_CRITICAL = 0x0008,
+  LOG_FILTER_MASK = 0x80f,
+};
+
+const uint32_t MAX_LOG_SIZE = 20 * 1024 * 1024;  // 20MB
+const uint32_t MIN_LOG_SIZE = 128 * 1024;        // 128KB
+/** The default log size in kb
+ */
+const uint32_t DEFAULT_LOG_SIZE_IN_KB = 2048;
+
+/** Definition of LogConfiguration
+ */
+struct LogConfig {
+  /**The log file path, default is NULL for default log path
+   */
+  const char* filePath;
+  /** The log file size, KB , set 2048KB to use default log size
+   */
+  uint32_t fileSizeInKB;
+  /** The log level, set LOG_LEVEL_INFO to use default log level
+   */
+  LOG_LEVEL level;
+
+  LogConfig() : filePath(NULL), fileSizeInKB(DEFAULT_LOG_SIZE_IN_KB), level(OPTIONAL_LOG_LEVEL_SPECIFIER LOG_LEVEL_INFO) {}
+};
+}  // namespace commons
+}  // namespace agora
+
+#undef OPTIONAL_LOG_LEVEL_SPECIFIER
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaComponentFactory.h b/headers/rtc_4.3.2.11/include/IAgoraMediaComponentFactory.h
new file mode 100644
index 0000000..fb5708b
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaComponentFactory.h
@@ -0,0 +1,41 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2021 Agora.io. All rights reserved.
+//
+#pragma once  // NOLINT(build/header_guard)
+
+#include "AgoraBase.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace rtc {
+
+class IMediaPlayer;
+
+class IMediaComponentFactory {
+public:
+  /** This method creates media player.
+   */
+  virtual agora_refptr<IMediaPlayer> createMediaPlayer(
+      agora::media::base::MEDIA_PLAYER_SOURCE_TYPE type = agora::media::base::MEDIA_PLAYER_SOURCE_DEFAULT) = 0;
+
+protected:
+ virtual ~IMediaComponentFactory() {}
+};
+
+} //namespace rtc
+} // namespace agora
+
+/** \addtogroup createMediaComponentFactory
+ @{
+ */
+/**
+ * Creates an \ref agora::rtc::IMediaComponentFactory "IMediaComponentFactory" object and returns the pointer.
+ *
+ * @return
+ * - The pointer to \ref agora::rtc::IMediaComponentFactory "IMediaComponentFactory": Success.
+ * - A null pointer: Failure.
+ */
+AGORA_API agora::rtc::IMediaComponentFactory* AGORA_CALL createAgoraMediaComponentFactory();
+/** @} */
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaEngine.h b/headers/rtc_4.3.2.11/include/IAgoraMediaEngine.h
new file mode 100644
index 0000000..2ad93ee
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaEngine.h
@@ -0,0 +1,285 @@
+//
+//  Agora Media SDK
+//
+//  Copyright (c) 2015 Agora IO. All rights reserved.
+//
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace media {
+
+/** dual-mono music output mode
+ */
+enum AUDIO_MIXING_DUAL_MONO_MODE {
+  /* 0: Original mode */
+  AUDIO_MIXING_DUAL_MONO_AUTO = 0,
+  /* 1: Left channel mode */
+  AUDIO_MIXING_DUAL_MONO_L = 1,
+  /* 2: Right channel mode */
+  AUDIO_MIXING_DUAL_MONO_R = 2,
+  /* 3: Mixed channel mode */
+  AUDIO_MIXING_DUAL_MONO_MIX = 3
+};
+
+
+/**
+ * The IMediaEngine class.
+ */
+class IMediaEngine {
+ public:
+  /**
+   * Registers an audio frame observer object.
+   *
+   * @note
+   * Ensure that you call this method before \ref IRtcEngine::joinChannel "joinChannel".
+   *
+   * @param observer A pointer to the audio frame observer object: IAudioFrameObserver,
+   * nullptr means unregistering observer instead.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerAudioFrameObserver(IAudioFrameObserver* observer) = 0;
+  /**
+   * Registers a video frame observer object.
+   *
+   * @note
+   * - Ensure that you call this method before joining the channel.
+   * - If you register an observer for video raw video data, you cannot register an IVideoEncodedFrameObserver
+   * object.
+   *
+   * @param observer A pointer to the video frame observer: IVideoFrameObserver.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerVideoFrameObserver(IVideoFrameObserver* observer) = 0;
+  /**
+   * Registers a receiver object for the encoded video image.
+   *
+   * @note
+   * - Ensure that you call this method before joining the channel.
+   *
+   * @param observer A pointer to the observer of the encoded video image: \ref IVideoEncodedFrameObserver
+   * "IVideoEncodedFrameObserver".
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerVideoEncodedFrameObserver(IVideoEncodedFrameObserver* observer) = 0;
+  
+  /**
+   * Registers a face info observer object.
+   *
+   * @note
+   * Ensure that you call this method before \ref IRtcEngine::joinChannel "joinChannel".
+   *
+   * @param observer A pointer to the face info observer object: IFaceInfoObserver.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerFaceInfoObserver(IFaceInfoObserver* observer) = 0;
+  
+  /**
+   * Pushes the external audio data to the app.
+   *
+   * @param frame The audio buffer data.
+   * @param trackId The audio track ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+   
+  virtual int pushAudioFrame(IAudioFrameObserverBase::AudioFrame* frame, rtc::track_id_t trackId = 0) = 0;
+
+  /**
+   * Pulls the remote audio data.
+   *
+   * After a successful method call, the app pulls the decoded and mixed audio data for playback.
+   *
+   * The difference between this method and the \ref onPlaybackAudioFrame "onPlaybackAudioFrame" is as follows:
+   * - `onPlaybackAudioFrame`: The SDK sends the audio data to the app once every 10 ms. Any delay in processing
+   * the audio frames may result in audio jitter.
+   * - `pullAudioFrame`: The app pulls the remote audio data. After setting the audio data parameters, the
+   * SDK adjusts the frame buffer and avoids problems caused by jitter in the external audio playback.
+   *
+   * @param frame The pointer to the audio frame: AudioFrame.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pullAudioFrame(IAudioFrameObserverBase::AudioFrame* frame) = 0;
+
+  /**
+   * Sets the external video source.
+   *
+   * Once the external video source is enabled, the SDK prepares to accept the external video frame.
+   *
+   * @param enabled Determines whether to enable the external video source.
+   * - true: Enable the external video source. Once set, the SDK creates the external source and prepares
+   * video data from `pushVideoFrame` or `pushEncodedVideoImage`.
+   * - false: Disable the external video source.
+   * @param useTexture Determines whether to use textured video data.
+   * - true: Use texture, which is not supported now.
+   * - False: Do not use texture.
+   * @param sourceType Determines the type of external video source frame.
+   * - ENCODED_VIDEO_FRAME: The external video source is encoded.
+   * - VIDEO_FRAME: The external video source is not encoded.
+   * @param encodedVideoOption Video encoded track option, which is only used for ENCODED_VIDEO_FRAME.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExternalVideoSource(
+      bool enabled, bool useTexture, EXTERNAL_VIDEO_SOURCE_TYPE sourceType = VIDEO_FRAME,
+      rtc::SenderOptions encodedVideoOption = rtc::SenderOptions()) = 0;
+
+  /**
+   * Sets the external audio source.
+   *
+   * @note
+   * Ensure that you call this method before joining the channel.
+   *
+   * @deprecated This method is deprecated. Use createCustomAudioTrack(rtc::AUDIO_TRACK_TYPE trackType, const rtc::AudioTrackConfig& config) instead.
+   *
+   * @param enabled Determines whether to enable the external audio source:
+   * - true: Enable the external audio source.
+   * - false: (default) Disable the external audio source.
+   * @param sampleRate The Sample rate (Hz) of the external audio source, which can set be as
+   * 8000, 16000, 32000, 44100, or 48000.
+   * @param channels The number of channels of the external audio source, which can be set as 1 or 2:
+   * - 1: Mono.
+   * - 2: Stereo.
+   * @param localPlayback Enable/Disables the local playback of external audio track:
+   * - true: Enable local playback
+   * - false: (Default) Do not enable local playback
+   * @param publish Determines whether to publish the external audio track:
+   * - true: (Default) Publish the external audio track.
+   * - false: Don`t publish the external audio track.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExternalAudioSource(bool enabled, int sampleRate, int channels, bool localPlayback = false, bool publish = true) __deprecated = 0;
+
+  /**
+   * Create a custom audio track and get the audio track id.
+   *
+   * @note Ensure that you call this method before calling `joinChannel`.
+   *
+   * @param trackType The type of custom audio track
+   * See AUDIO_TRACK_TYPE.
+   *
+   * @param config The config of custom audio track
+   * See AudioTrackConfig.
+   *
+   * @return
+   * - If the call is successful, SDK returns audio track id.
+   * - If the call fails, SDK returns 0xffffffff.
+   */
+  virtual rtc::track_id_t createCustomAudioTrack(rtc::AUDIO_TRACK_TYPE trackType, const rtc::AudioTrackConfig& config) = 0;
+
+  /**
+   * Destroy custom audio track by trackId
+   *
+   * @param trackId The custom audio track id.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int destroyCustomAudioTrack(rtc::track_id_t trackId) = 0;
+
+  /**
+   * Sets the external audio sink.
+   *
+   * This method applies to scenarios where you want to use external audio
+   * data for playback. After calling the \ref IRtcEngine::initialize "initialize"
+   * method and pass value of false in the `enableAudioDevice` member in the RtcEngineContext struct, you can call
+   * the \ref agora::media::IMediaEngine::pullAudioFrame "pullAudioFrame" method to pull the remote audio data, process
+   * it, and play it with the audio effects that you want.
+   *
+   * @note
+   * Once you call the \ref IRtcEngine::initialize "initialize" method and pass value of false in the `enableAudioDevice`
+   * member in the RtcEngineContext struct, the app will not retrieve any audio data from the
+   * \ref agora::media::IAudioFrameObserver::onPlaybackAudioFrame "onPlaybackAudioFrame" callback.
+   *
+   * @param enabled Sets whether or not to the external audio sink
+   * - true: Enables the external audio sink.
+   * - false: Disables the external audio sink.
+   * @param sampleRate Sets the sample rate (Hz) of the external audio sink, which can be set as 16000, 32000, 44100 or 48000.
+   * @param channels Sets the number of audio channels of the external
+   * audio sink:
+   * - 1: Mono.
+   * - 2: Stereo.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExternalAudioSink(bool enabled, int sampleRate, int channels) = 0;
+
+  /**
+   * Sets the external audio track.
+   *
+   * @note
+   * Ensure that you call this method before joining the channel.
+   *
+   * @param trackId The custom audio track id.
+   * @param enabled Enable/Disables the local playback of external audio track:
+   * - true: Enable local playback
+   * - false: Do not enable local playback
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableCustomAudioLocalPlayback(rtc::track_id_t trackId, bool enabled) = 0;
+
+  /**
+   * Pushes the external video frame to the app.
+   *
+   * @param frame The external video frame: ExternalVideoFrame.
+   * @param videoTrackId The id of the video track.
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pushVideoFrame(base::ExternalVideoFrame* frame, unsigned int videoTrackId = 0) = 0;
+  /**
+   * Pushes the encoded video image to the app.
+   * @param imageBuffer A pointer to the video image.
+   * @param length The data length.
+   * @param videoEncodedFrameInfo The reference to the information of the encoded video frame:
+   * \ref agora::rtc::EncodedVideoFrameInfo "EncodedVideoFrameInfo".
+   * @param videoTrackId The id of the video track.
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pushEncodedVideoImage(const unsigned char* imageBuffer, size_t length,
+                                    const agora::rtc::EncodedVideoFrameInfo& videoEncodedFrameInfo,
+                                    unsigned int videoTrackId = 0) = 0;
+  /**
+   * @hide For internal usage only
+   */
+  virtual int addVideoFrameRenderer(IVideoFrameObserver *renderer) = 0;
+
+  /**
+   * @hide For internal usage only
+   */
+  virtual int removeVideoFrameRenderer(IVideoFrameObserver *renderer) = 0;
+
+  virtual void release() = 0;
+
+ protected:
+  virtual ~IMediaEngine() {}
+};
+
+}  // namespace media
+
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaPlayer.h b/headers/rtc_4.3.2.11/include/IAgoraMediaPlayer.h
new file mode 100644
index 0000000..07b7d34
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaPlayer.h
@@ -0,0 +1,644 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2020 Agora.io. All rights reserved.
+//
+#pragma once  // NOLINT(build/header_guard)
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "AgoraMediaPlayerTypes.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace base {
+class IAgoraService;
+}
+namespace rtc {
+
+class ILocalAudioTrack;
+class ILocalVideoTrack;
+class IMediaPlayerSourceObserver;
+class IMediaPlayerCustomDataProvider;
+
+/**
+ * The IMediaPlayerEntity class provides access to a media player entity. If yout want to playout
+ * multiple media sources simultaneously, create multiple media player source objects.
+ */
+class IMediaPlayer : public RefCountInterface {
+protected:
+  virtual ~IMediaPlayer() {}
+
+public:
+  virtual int initialize(base::IAgoraService* agora_service) = 0;
+
+  /**
+   * Get unique media player id of the media player entity.
+   * @return
+   * - >= 0: The source id of this media player entity.
+   * - < 0: Failure.
+   */
+  virtual int getMediaPlayerId() const = 0;
+
+  /**
+   * Opens a media file with a specified URL.
+   * @param url The URL of the media file that you want to play.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int open(const char* url, int64_t startPos) = 0;
+
+  /**
+   * @deprecated
+   * @brief Open media file or stream with custom soucrce.
+   * @param startPos Set the starting position for playback, in seconds
+   * @param observer dataProvider object
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithCustomSource(int64_t startPos,  media::base::IMediaPlayerCustomDataProvider* provider) __deprecated = 0;
+
+  /**
+   * @brief Open a media file with a media file source.
+   * @param source Media file source that you want to play, see `MediaSource`
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithMediaSource(const media::base::MediaSource &source) = 0;
+
+  /**
+   * Plays the media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int play() = 0;
+
+  /**
+   * Pauses playing the media file.
+   */
+  virtual int pause() = 0;
+
+  /**
+   * Stops playing the current media file.
+   */
+  virtual int stop() = 0;
+
+  /**
+   * Resumes playing the media file.
+   */
+  virtual int resume() = 0;
+
+  /**
+   * Sets the current playback position of the media file.
+   * @param newPos The new playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int seek(int64_t newPos) = 0;
+  
+  /** Sets the pitch of the current media file.
+   * @param pitch Sets the pitch of the local music file by chromatic scale. The default value is 0,
+   * which means keeping the original pitch. The value ranges from -12 to 12, and the pitch value between
+   * consecutive values is a chromatic value. The greater the absolute value of this parameter, the
+   * higher or lower the pitch of the local music file.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioPitch(int pitch) = 0;
+
+  /**
+   * Gets the duration of the media file.
+   * @param duration A reference to the duration of the media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDuration(int64_t& duration) = 0;
+
+  /**
+   * Gets the current playback position of the media file.
+   * @param currentPosition A reference to the current playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlayPosition(int64_t& pos) = 0;
+
+  virtual int getStreamCount(int64_t& count) = 0;
+
+  virtual int getStreamInfo(int64_t index, media::base::PlayerStreamInfo* info) = 0;
+
+  /**
+   * Sets whether to loop the media file for playback.
+   * @param loopCount the number of times looping the media file.
+   * - 0: Play the audio effect once.
+   * - 1: Play the audio effect twice.
+   * - -1: Play the audio effect in a loop indefinitely, until stopEffect() or stop() is called.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLoopCount(int loopCount) = 0;
+
+  /**
+   * Change playback speed
+   * @param speed the value of playback speed ref [50-400]
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackSpeed(int speed) = 0;
+
+  /**
+   * Slect playback audio track of the media file
+   * @param index the index of the audio track in media file
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int selectAudioTrack(int index) = 0;
+
+  /**
+   * Selects multi audio track of the media file for playback or publish to channel.
+   * @param playoutTrackIndex The index of the audio track in media file for local playback.
+   * @param publishTrackIndex The index of the audio track in the media file published to the remote.
+   * 
+   * @note
+   * You can obtain the streamIndex of the audio track by calling getStreamInfo..
+   * If you want to use selectMultiAudioTrack, you need to open the media file with openWithMediaSource and set enableMultiAudioTrack to true.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   * - -2: Invalid argument. Argument must be greater than or equal to zero.
+   * - -8: Invalid State.You must open the media file with openWithMediaSource and set enableMultiAudioTrack to true
+   */
+  virtual int selectMultiAudioTrack(int playoutTrackIndex, int publishTrackIndex) = 0;
+
+  /**
+   * change player option before play a file
+   * @param key the key of the option param
+   * @param value the value of option param
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlayerOption(const char* key, int value) = 0;
+
+  /**
+   * change player option before play a file
+   * @param key the key of the option param
+   * @param value the value of option param
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlayerOption(const char* key, const char* value) = 0;
+  /**
+   * take screenshot while playing  video
+   * @param filename the filename of screenshot file
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int takeScreenshot(const char* filename) = 0;
+
+  /**
+   * select internal subtitles in video
+   * @param index the index of the internal subtitles
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int selectInternalSubtitle(int index) = 0;
+
+  /**
+   * set an external subtitle for video
+   * @param url The URL of the subtitle file that you want to load.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExternalSubtitle(const char* url) = 0;
+
+  virtual media::base::MEDIA_PLAYER_STATE getState() = 0;
+
+  /**
+   * @brief Turn mute on or off
+   *
+   * @param muted Whether to mute on
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int mute(bool muted) = 0;
+
+  /**
+   * @brief Get mute state
+   *
+   * @param[out] muted Whether is mute on
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int getMute(bool& muted) = 0;
+
+  /**
+   * @brief Adjust playback volume
+   *
+   * @param volume The volume value to be adjusted
+   * The volume can be adjusted from 0 to 400:
+   * 0: mute;
+   * 100: original volume;
+   * 400: Up to 4 times the original volume (with built-in overflow protection).
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int adjustPlayoutVolume(int volume) = 0;
+
+  /**
+   * @brief Get the current playback volume
+   *
+   * @param[out] volume
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int getPlayoutVolume(int& volume) = 0;
+
+  /**
+   * @brief adjust publish signal volume
+   *
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int adjustPublishSignalVolume(int volume) = 0;
+
+  /**
+   * @brief get publish signal volume
+   *
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int getPublishSignalVolume(int& volume) = 0;
+
+  /**
+   * @brief Set video rendering view
+   *
+   * @param view view object, windows platform is HWND
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int setView(media::base::view_t view) = 0;
+
+  /**
+   * @brief Set video display mode
+   *
+   * @param renderMode Video display mode
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int setRenderMode(media::base::RENDER_MODE_TYPE renderMode) = 0;
+
+  /**
+   * Registers a media player source observer.
+   *
+   * Once the media player source observer is registered, you can use the observer to monitor the state change of the media player.
+   * @param observer The pointer to the IMediaPlayerSourceObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerPlayerSourceObserver(IMediaPlayerSourceObserver* observer) = 0;
+
+  /**
+   * Releases the media player source observer.
+   * @param observer The pointer to the IMediaPlayerSourceObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unregisterPlayerSourceObserver(IMediaPlayerSourceObserver* observer) = 0;
+
+  /**
+   * Register the audio frame observer.
+   *
+   * @param observer The pointer to the IAudioFrameObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer) = 0;
+
+  /**
+   * Registers an audio observer.
+   *
+   * @param observer The audio observer, reporting the reception of each audio
+   * frame. See
+   * \ref media::IAudioPcmFrameSink "IAudioFrameObserver" for
+   * details.
+   * @param mode Use mode of the audio frame. See #RAW_AUDIO_FRAME_OP_MODE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer,
+                                         RAW_AUDIO_FRAME_OP_MODE_TYPE mode) = 0;
+
+  /**
+   * Releases the audio frame observer.
+   * @param observer The pointer to the IAudioFrameObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unregisterAudioFrameObserver(media::IAudioPcmFrameSink* observer) = 0;
+
+  /**
+   * @brief Register the player video observer
+   *
+   * @param observer observer object
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int registerVideoFrameObserver(media::base::IVideoFrameObserver* observer) = 0;
+
+  /**
+   * @brief UnRegister the player video observer
+   *
+   * @param observer observer object
+   * @return int < 0 on behalf of an error, the value corresponds to one of MEDIA_PLAYER_REASON
+   */
+  virtual int unregisterVideoFrameObserver(agora::media::base::IVideoFrameObserver* observer) = 0;
+
+   /**
+   * Registers the audio frame spectrum observer.
+   *
+   * @param observer The pointer to the {@link media::base::IAudioSpectrumObserver  IAudioSpectrumObserver} object.
+   * @param intervalInMS Sets the time interval(ms) between two consecutive audio spectrum callback.
+   * The default value is 100. This param should be larger than 10.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerMediaPlayerAudioSpectrumObserver(media::IAudioSpectrumObserver* observer, int intervalInMS) = 0;
+
+  /**
+   * Releases the audio frame spectrum observer.
+   * @param observer The pointer to the {@link media::base::IAudioSpectrumObserver IAudioSpectrumObserver} object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. 
+   */
+   virtual int unregisterMediaPlayerAudioSpectrumObserver(media::IAudioSpectrumObserver* observer) = 0;
+
+  /**
+   * @brief Set dual-mono output mode of the music file.
+   * 
+   * @param mode dual mono mode.  See #agora::media::AUDIO_DUAL_MONO_MODE
+   */
+  virtual int setAudioDualMonoMode(agora::media::base::AUDIO_DUAL_MONO_MODE mode) = 0;
+
+  /**
+    * get sdk version and build number of player SDK.
+    * @return String of the SDK version.
+    * 
+    * @deprecated This method is deprecated.
+   */
+  virtual const char* getPlayerSdkVersion() = 0;
+
+  /**
+   * Get the current play src.
+   * @return
+   * - current play src of raw bytes.
+   */
+  virtual const char* getPlaySrc() = 0;
+
+
+    /**
+   * Open the Agora CDN media source.
+   * @param src The src of the media file that you want to play.
+   * @param startPos The  playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithAgoraCDNSrc(const char* src, int64_t startPos) = 0;
+
+  /**
+   * Gets the number of  Agora CDN lines.
+   * @return
+   * - > 0: number of CDN.
+   * - <= 0: Failure.
+   */
+  virtual int getAgoraCDNLineCount() = 0;
+
+  /**
+   * Switch Agora CDN lines.
+   * @param index Specific CDN line index.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchAgoraCDNLineByIndex(int index) = 0;
+
+  /**
+   * Gets the line of the current CDN.
+   * @return
+   * - >= 0: Specific line.
+   * - < 0: Failure.
+   */
+  virtual int getCurrentAgoraCDNIndex() = 0;
+
+  /**
+   * Enable automatic CDN line switching.
+   * @param enable Whether enable.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAutoSwitchAgoraCDN(bool enable) = 0;
+
+  /**
+   * Update the CDN source token and timestamp.
+   * @param token token.
+   * @param ts ts.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int renewAgoraCDNSrcToken(const char* token, int64_t ts) = 0;
+
+  /**
+   * Switch the CDN source when open a media through "openWithAgoraCDNSrc" API
+   * @param src Specific src.
+   * @param syncPts Live streaming must be set to false.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchAgoraCDNSrc(const char* src, bool syncPts = false) = 0;
+
+  /**
+   * Switch the media source when open a media through "open" API
+   * @param src Specific src.
+   * @param syncPts Live streaming must be set to false.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchSrc(const char* src, bool syncPts = true) = 0;
+
+  /**
+   * Preload a media source
+   * @param src Specific src.
+   * @param startPos The starting position (ms) for playback. Default value is 0.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int preloadSrc(const char* src, int64_t startPos) = 0;
+
+  /**
+   * Play a pre-loaded media source
+   * @param src Specific src.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int playPreloadedSrc(const char* src) = 0;
+
+  /**
+   * Unload a preloaded media source
+   * @param src Specific src.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unloadSrc(const char* src) = 0;
+
+  /**
+   * Set spatial audio params for the music file. It can be called after the media player
+   * was created.
+   *
+   * @param params See #agora::SpatialAudioParams. If it's
+   * not set, then the spatial audio will be disabled; or it will be enabled.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSpatialAudioParams(const SpatialAudioParams& params) = 0;
+
+  /**
+   * Set sound position params for the music file. It can be called after the media player
+   * was created.
+   *
+   *@param pan The sound position of the music file. The value ranges from -1.0 to 1.0:
+   *- 0.0: the music sound comes from the front.
+   *- -1.0: the music sound comes from the left.
+   *- 1.0: the music sound comes from the right.
+   *@param gain Gain of the music. The value ranges from 0.0 to 100.0. The default value is 100.0 (the original gain of the music). The smaller the value, the less the gain.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSoundPositionParams(float pan, float gain) = 0;
+
+};
+
+/**
+ * This class is used to set and manage the player cache, implemented in the
+ * form of a singleton, independent of the player.
+ */
+class IMediaPlayerCacheManager {
+public:
+  /**
+   * Delete the longest used cache file in order to release some of the cache file disk usage.
+   * (usually used when the cache quota notification is received)
+   * 
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int removeAllCaches() = 0;
+  /**
+   * Remove the latest media resource cache file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int removeOldCache() = 0;
+  /**
+   * Remove the cache file by uri, setting by MediaSource.
+   * @param uri URI,identify the uniqueness of the property, Set from `MeidaSource`
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int removeCacheByUri(const char *uri) = 0;
+  /**
+   * Set cache file path that files will be saved to.
+   * @param path file path.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setCacheDir(const char *path) = 0;
+  /**
+   * Set the maximum number of cached files.
+   * @param count maximum number of cached files.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setMaxCacheFileCount(int count) = 0;
+  /**
+   * Set the maximum size of cache file disk usage.
+   * @param cacheSize total size of the largest cache file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setMaxCacheFileSize(int64_t cacheSize) = 0;
+  /**
+   * Whether to automatically delete old cache files when the cache file usage reaches the limit.
+   * @param enable enable the player to automatically clear the cache.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAutoRemoveCache(bool enable) = 0;
+  /**
+   * Get the cache directory.
+   * @param path cache path, recieve a pointer to be copied to.
+   * @param length the length to be copied.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getCacheDir(char* path, int length) = 0;
+  /**
+   * Get the maximum number of cached files.
+   * @return
+   * > 0: file count.
+   * - < 0: Failure.
+   */
+  virtual int getMaxCacheFileCount() = 0;
+  /**
+   * Get the total size of the largest cache file
+   * @return
+   * > 0: file size.
+   * - < 0: Failure.
+   */
+  virtual int64_t getMaxCacheFileSize() = 0;
+  /**
+   * Get the number of all cache files.
+   * @return
+   * > 0: file count.
+   * - < 0: Failure.
+   */
+  virtual int getCacheFileCount() = 0;
+
+  virtual ~IMediaPlayerCacheManager(){};
+};
+
+} //namespace rtc
+} // namespace agora
+
+AGORA_API agora::rtc::IMediaPlayerCacheManager* AGORA_CALL getMediaPlayerCacheManager();
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaPlayerSource.h b/headers/rtc_4.3.2.11/include/IAgoraMediaPlayerSource.h
new file mode 100644
index 0000000..0f57f79
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaPlayerSource.h
@@ -0,0 +1,504 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2018 Agora.io. All rights reserved.
+//
+#pragma once  // NOLINT(build/header_guard)
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "AgoraMediaPlayerTypes.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace rtc {
+
+class IMediaPlayerSourceObserver;
+
+/**
+ * The IMediaPlayerSource class provides access to a media player source. To playout multiple media sources simultaneously,
+ * create multiple media player source objects.
+ */
+class IMediaPlayerSource : public RefCountInterface {
+protected:
+  virtual ~IMediaPlayerSource() {}
+
+public:
+
+  /**
+   * Gets the unique source ID of the media player source.
+   * @return
+   * - >=0: The source ID of this media player source.
+   * - < 0: Failure.
+   */
+  virtual int getSourceId() const = 0;
+
+  /**
+   * Opens a media file with a specified URL.
+   * @param url The path of the media file. Both the local path and online path are supported.
+   * @param startPos The starting position (ms) for playback. Default value is 0.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int open(const char* url, int64_t startPos) = 0;
+    
+  /**
+   * @deprecated
+   * @brief Open media file or stream with custom soucrce.
+   * @param startPos Set the starting position for playback, in seconds
+   * @param observer dataProvider object
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithCustomSource(int64_t startPos, media::base::IMediaPlayerCustomDataProvider* provider) __deprecated = 0;
+
+  /**
+   * Opens a media file with a media file source.
+   * @param source Media file source that you want to play, see `MediaSource`
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithMediaSource(const media::base::MediaSource &source) = 0;
+    
+  /**
+   * Plays the media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int play() = 0;
+
+  /**
+   * Pauses the playback.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pause() = 0;
+
+  /**
+   * Stops the playback.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stop() = 0;
+
+  /**
+   * Resumes the playback.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int resume() = 0;
+
+  /**
+   * Sets the playback position of the media file.
+   * @param newPos The new playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int seek(int64_t newPos) = 0;
+
+  /**
+   * Gets the duration of the media file.
+   * @param [out] duration A reference to the duration of the media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDuration(int64_t& duration) = 0;
+
+  /**
+   * Gets the current playback position of the media file.
+   * @param [out] pos A reference to the current playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int getPlayPosition(int64_t& pos) = 0;
+
+  /**
+   * Gets the number of the media streams in the media source.
+   * @param [out] count The number of the media streams in the media source.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int getStreamCount(int64_t& count) = 0;
+
+  /**
+   * Gets the detailed information of a media stream.
+   * @param index The index of the media stream.
+   * @param [out] info The detailed information of the media stream. See \ref media::base::PlayerStreamInfo "PlayerStreamInfo" for details.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int getStreamInfo(int64_t index, media::base::PlayerStreamInfo* info) = 0;
+
+  /**
+   * Sets whether to loop the media file for playback.
+   * @param loopCount The number of times of looping the media file.
+   * - 0: Play the media file once.
+   * - 1: Play the media file twice.
+   * - -1: Play the media file in a loop indefinitely, until {@link stop} is called.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int setLoopCount(int64_t loopCount) = 0;
+
+  /**
+   * Changes the playback speed.
+   * @param speed The playback speed ref [50-400].
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int setPlaybackSpeed(int speed) = 0;
+
+  /**
+   * Selects an audio track of the media file for playback.
+   * @param index The index of the audio track in media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int selectAudioTrack(int64_t index) = 0;
+
+  /**
+   * Selects multi audio track of the media file for playback or publish to channel.
+   * @param playoutTrackIndex The index of the audio track in media file for local playback.
+   * @param publishTrackIndex The index of the audio track in the media file published to the remote.
+   *
+   * @note
+   * You can obtain the streamIndex of the audio track by calling getStreamInfo..
+   * If you want to use selectMultiAudioTrack, you need to open the media file with openWithMediaSource and set enableMultiAudioTrack to true.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   * - -2: Invalid argument. Argument must be greater than or equal to zero.
+   * - -8: Invalid State.You must open the media file with openWithMediaSource and set enableMultiAudioTrack to true
+   */
+  virtual int selectMultiAudioTrack(int playoutTrackIndex, int publishTrackIndex) = 0;
+
+  /**
+   * Changes the player option before playing a file.
+   * @param key The key of the option paramemter.
+   * @param value The value of option parameter.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int setPlayerOption(const char* key, int64_t value) = 0;
+
+  /**
+   * Changes the player option before playing a file.
+   * @param key The key of the option paramemter.
+   * @param value The value of option parameter.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int setPlayerOption(const char* key, const char* value) = 0;
+
+  /**
+   * Takes a screenshot when playing a video file.
+   * @param filename The filename of the screenshot file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int takeScreenshot(const char* filename) = 0;
+
+  /**
+   * Selects internal subtitles for a video file.
+   * @param index The index of the internal subtitles.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int selectInternalSubtitle(int64_t index) = 0;
+
+  /**
+   * Sets an external subtitle file for a video file.
+   * @param url The URL of the subtitle file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int setExternalSubtitle(const char* url) = 0;
+
+  /**
+   * Gets the playback state.
+   * @return The current playback state. See {@link media::base::MEDIA_PLAYER_STATE MEDIA_PLAYER_STATE} for details.
+   */
+  virtual media::base::MEDIA_PLAYER_STATE getState() = 0;
+
+  /**
+   * Registers a media player source observer.
+   *
+   * Once the media player source observer is registered, you can use the observer to monitor the state change of the media player.
+   * @param observer The pointer to the IMediaPlayerSourceObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int registerPlayerSourceObserver(IMediaPlayerSourceObserver* observer) = 0;
+
+  /**
+   * Releases the media player source observer.
+   * @param observer The pointer to the IMediaPlayerSourceObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int unregisterPlayerSourceObserver(IMediaPlayerSourceObserver* observer) = 0;
+
+  /**
+   * Registers the audio frame observer.
+   *
+   * @param observer The pointer to the {@link media::IAudioPcmFrameSink observer} object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int registerAudioFrameObserver(media::IAudioPcmFrameSink* observer) = 0;
+
+  /**
+   * Releases the audio frame observer.
+   * @param observer The pointer to the {@link media::IAudioPcmFrameSink observer} object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual int unregisterAudioFrameObserver(media::IAudioPcmFrameSink* observer) = 0;
+
+  /**
+   * Open the Agora CDN media source.
+   * @param src The src of the media file that you want to play.
+   * @param startPos The  playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int openWithAgoraCDNSrc(const char* src, int64_t startPos) = 0;
+
+  /**
+   * Gets the number of  Agora CDN lines.
+   * @return
+   * - > 0: number of CDN.
+   * - <= 0: Failure.
+   */
+  virtual int getAgoraCDNLineCount() = 0;
+  
+
+  /**
+   * Switch Agora CDN lines.
+   * @param index Specific CDN line index.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchAgoraCDNLineByIndex(int index) = 0;
+
+  /**
+   * Gets the line of the current CDN.
+   * @return
+   * - >= 0: Specific line.
+   * - < 0: Failure.
+   */
+  virtual int getCurrentAgoraCDNIndex() = 0;
+
+  /**
+   * Enable automatic CDN line switching.
+   * @param enable Whether enable.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAutoSwitchAgoraCDN(bool enable) = 0;
+
+  /**
+   * Update the CDN source token and timestamp.
+   * @param token token.
+   * @param ts ts.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int renewAgoraCDNSrcToken(const char* token, int64_t ts) = 0;
+
+  /**
+   * Switch the CDN source when open a media through "openWithAgoraCDNSrc" API
+   * @param src Specific src.
+   * @param syncPts Live streaming must be set to false.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchAgoraCDNSrc(const char* src, bool syncPts = false) = 0;
+
+  /**
+   * Switch the media source when open a media through "open" API
+   * @param src Specific src.
+   * @param syncPts Live streaming must be set to false.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchSrc(const char* src, bool syncPts) = 0;
+
+  /**
+   * Preload a media source
+   * @param src Specific src.
+   * @param startPos The starting position (ms) for playback. Default value is 0.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int preloadSrc(const char* src, int64_t startPos) = 0;
+
+  /**
+   * Unload a preloaded media source
+   * @param src Specific src.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unloadSrc(const char* src) = 0;
+
+  /**
+   * Play a pre-loaded media source
+   * @param src Specific src.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int playPreloadedSrc(const char* src) = 0;
+
+};
+
+/**
+ * This class class reports runtime events to the applications.
+ */
+class IMediaPlayerSourceObserver {
+ public:
+  virtual ~IMediaPlayerSourceObserver() {}
+
+  /**
+   * @brief Reports the playback state change.
+   *
+   * When the state of the playback changes, the SDK triggers this callback to report the new playback state and the reason or error for the change.
+   * @param state The new playback state after change. See {@link media::base::MEDIA_PLAYER_STATE MEDIA_PLAYER_STATE}.
+   * @param reason The player's error code. See {@link media::base::MEDIA_PLAYER_REASON MEDIA_PLAYER_REASON}.
+   */
+  virtual void onPlayerSourceStateChanged(media::base::MEDIA_PLAYER_STATE state,
+                                          media::base::MEDIA_PLAYER_REASON reason) = 0;
+
+  /**
+   * @brief Reports current playback progress.
+   *
+   * The callback occurs once every one second during the playback and reports the current playback progress.
+   * @param positionMs Current playback progress (milisecond).
+   * @param timestampMs Current NTP(Network Time Protocol) time (milisecond).
+   */
+  virtual void onPositionChanged(int64_t positionMs, int64_t timestampMs) = 0;
+
+  /**
+   * @brief Reports the playback event.
+   *
+   * - After calling the `seek` method, the SDK triggers the callback to report the results of the seek operation.
+   * - After calling the `selectAudioTrack` method, the SDK triggers the callback to report that the audio track changes.
+   *
+   * @param eventCode The playback event. See {@link media::base::MEDIA_PLAYER_EVENT MEDIA_PLAYER_EVENT}.
+   * @param elapsedTime The playback elapsed time.
+   * @param message The playback message.
+   */
+  virtual void onPlayerEvent(media::base::MEDIA_PLAYER_EVENT eventCode, int64_t elapsedTime, const char* message) = 0;
+
+  /**
+   * @brief Occurs when the metadata is received.
+   *
+   * The callback occurs when the player receives the media metadata and reports the detailed information of the media metadata.
+   * @param data The detailed data of the media metadata.
+   * @param length The data length (bytes).
+   */
+  virtual void onMetaData(const void* data, int length) = 0;
+
+
+  /**
+   * @brief Triggered when play buffer updated, once every 1 second
+   *
+   * @param int cached buffer during playing, in milliseconds
+   */
+  virtual void onPlayBufferUpdated(int64_t playCachedBuffer) = 0;
+
+
+  /**
+   * @brief Triggered when the player preloadSrc
+   *
+   * @param event
+   */
+  virtual void onPreloadEvent(const char* src, media::base::PLAYER_PRELOAD_EVENT event) = 0;
+
+  /**
+   * @brief Occurs when one playback of the media file is completed.
+   */
+  virtual void onCompleted() = 0;
+
+  /**
+   * @brief AgoraCDN Token has expired and needs to be set up with renewAgoraCDNSrcToken(const char* src).
+   */
+  virtual void onAgoraCDNTokenWillExpire() = 0;
+
+  /**
+   * @brief Reports current playback source bitrate changed.
+   * @brief Reports current playback source info changed.
+   *
+   * @param from Streaming media information before the change.
+   * @param to Streaming media information after the change.
+   */
+  virtual void onPlayerSrcInfoChanged(const media::base::SrcInfo& from, const media::base::SrcInfo& to) = 0;
+
+   /**
+   * @brief Triggered when media player information updated.
+   *
+   * @param info Include information of media player.
+   */
+  virtual void onPlayerInfoUpdated(const media::base::PlayerUpdatedInfo& info) = 0;
+
+   /**
+   * @brief Triggered every 1 second, reports the statistics of the files being cached.
+   * 
+   * @param stats Cached file statistics.
+   */
+  virtual void onPlayerCacheStats(const media::base::CacheStatistics& stats) {
+    (void)stats;
+  }
+
+   /**
+   * @brief Triggered every 1 second, reports the statistics of the media stream being played.
+   * 
+   * @param stats The statistics of the media stream.
+   */
+  virtual void onPlayerPlaybackStats(const media::base::PlayerPlaybackStats& stats) {
+    (void)stats;
+  }
+  
+  /**
+   * @brief Triggered  every 200 millisecond ,update player current volume range [0,255]
+   *
+   * @param volume volume of current player.
+   */
+  virtual void onAudioVolumeIndication(int volume) = 0;
+};
+
+} //namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaRecorder.h b/headers/rtc_4.3.2.11/include/IAgoraMediaRecorder.h
new file mode 100644
index 0000000..33f5a30
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaRecorder.h
@@ -0,0 +1,90 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2022 Agora.io. All rights reserved.
+//
+#pragma once  // NOLINT(build/header_guard)
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "IAgoraRtcEngineEx.h"
+
+namespace agora {
+namespace rtc {
+
+class IMediaRecorder : public RefCountInterface {
+ protected:
+  virtual ~IMediaRecorder() {}
+
+ public:
+  /**
+   * Registers the IMediaRecorderObserver object.
+   *
+   * @since v4.0.0
+   *
+   * @note Call this method before the startRecording method.
+   *
+   * @param callback The callbacks for recording audio and video streams. See \ref IMediaRecorderObserver.
+   *
+   * @return
+   * - 0(ERR_OK): Success.
+   * - < 0: Failure:
+   */
+  virtual int setMediaRecorderObserver(media::IMediaRecorderObserver* callback) = 0;
+  /**
+   * Starts recording the local or remote audio and video.
+   *
+   * @since v4.0.0
+   *
+   * After successfully calling \ref IRtcEngine::createMediaRecorder "createMediaRecorder" to get the media recorder object
+   * , you can call this method to enable the recording of the local audio and video.
+   *
+   * This method can record the following content:
+   * - The audio captured by the local microphone and encoded in AAC format.
+   * - The video captured by the local camera and encoded by the SDK.
+   * - The audio received from remote users and encoded in AAC format.
+   * - The video received from remote users.
+   * 
+   * The SDK can generate a recording file only when it detects the recordable audio and video streams; when there are
+   * no audio and video streams to be recorded or the audio and video streams are interrupted for more than five
+   * seconds, the SDK stops recording and triggers the
+   * \ref IMediaRecorderObserver::onRecorderStateChanged "onRecorderStateChanged" (RECORDER_STATE_ERROR, RECORDER_ERROR_NO_STREAM)
+   * callback.
+   *
+   * @note Call this method after joining the channel.
+   *
+   * @param config The recording configurations. See MediaRecorderConfiguration.
+   *
+   * @return
+   * - 0(ERR_OK): Success.
+   * - < 0: Failure:
+   *    - `-1(ERR_FAILED)`: IRtcEngine does not support the request because the remote user did not subscribe to the target channel or the media streams published by the local user during remote recording.
+   *    - `-2(ERR_INVALID_ARGUMENT)`: The parameter is invalid. Ensure the following:
+   *      - The specified path of the recording file exists and is writable.
+   *      - The specified format of the recording file is supported.
+   *      - The maximum recording duration is correctly set.
+   *      - During remote recording, ensure the user whose media streams you want record did join the channel.
+   *    - `-4(ERR_NOT_SUPPORTED)`: IRtcEngine does not support the request due to one of the following reasons:
+   *      - The recording is ongoing.
+   *      - The recording stops because an error occurs.
+   *      - No \ref IMediaRecorderObserver object is registered.
+   */
+  virtual int startRecording(const media::MediaRecorderConfiguration& config) = 0;
+  /**
+   * Stops recording the audio and video.
+   *
+   * @since v4.0.0
+   *
+   * @note After calling \ref IMediaRecorder::startRecording "startRecording", if you want to stop the recording,
+   * you must call `stopRecording`; otherwise, the generated recording files might not be playable.
+   *
+   *
+   * @return
+   * - 0(ERR_OK): Success.
+   * - < 0: Failure:
+   */
+  virtual int stopRecording() = 0;
+};
+
+} //namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMediaStreamingSource.h b/headers/rtc_4.3.2.11/include/IAgoraMediaStreamingSource.h
new file mode 100644
index 0000000..7a0dbdf
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMediaStreamingSource.h
@@ -0,0 +1,332 @@
+//
+//  Agora SDK
+//  Copyright (c) 2019 Agora.io. All rights reserved.
+//
+//  Created by xiaohua.lu in 2020-03.
+//  CodeStyle: Google C++
+//
+
+#pragma once  // NOLINT(build/header_guard)
+
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "AgoraMediaPlayerTypes.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace rtc {
+
+
+class IMediaStreamingSourceObserver;
+
+
+/**
+ * @brief The error code of streaming source
+ *
+ */
+enum STREAMING_SRC_ERR {
+  STREAMING_SRC_ERR_NONE = 0,               ///< no error
+  STREAMING_SRC_ERR_UNKNOWN = 1,            ///< unknown error
+  STREAMING_SRC_ERR_INVALID_PARAM = 2,      ///< invalid parameter
+  STREAMING_SRC_ERR_BAD_STATE = 3,          ///< bad status
+  STREAMING_SRC_ERR_NO_MEM = 4,             ///< not enough memory
+  STREAMING_SRC_ERR_BUFFER_OVERFLOW = 5,    ///< buffer overflow
+  STREAMING_SRC_ERR_BUFFER_UNDERFLOW = 6,   ///< buffer underflow
+  STREAMING_SRC_ERR_NOT_FOUND = 7,          ///< buffer underflow
+  STREAMING_SRC_ERR_TIMEOUT = 8,            ///< buffer underflow
+  STREAMING_SRC_ERR_EXPIRED = 9,            ///< expired
+  STREAMING_SRC_ERR_UNSUPPORTED = 10,       ///< unsupported
+  STREAMING_SRC_ERR_NOT_EXIST = 11,         ///< component not exist
+  STREAMING_SRC_ERR_EXIST = 12,             ///< component already exist
+  STREAMING_SRC_ERR_OPEN = 13,              ///< fail to IO open
+  STREAMING_SRC_ERR_CLOSE = 14,             ///< fail to IO close
+  STREAMING_SRC_ERR_READ = 15,              ///< fail to IO read
+  STREAMING_SRC_ERR_WRITE = 16,             ///< fail to IO write
+  STREAMING_SRC_ERR_SEEK = 17,              ///< fail to IO seek
+  STREAMING_SRC_ERR_EOF = 18,               ///< reach to IO EOF, can do nothing
+  STREAMING_SRC_ERR_CODECOPEN = 19,         ///< fail to codec open
+  STREAMING_SRC_ERR_CODECCLOSE = 20,        ///< fail to codec close
+  STREAMING_SRC_ERR_CODECPROC = 21,         ///< fail to codec process
+};
+
+
+
+/**
+ * @brief The state machine of Streaming Source
+ *
+ */
+enum STREAMING_SRC_STATE {
+  STREAMING_SRC_STATE_CLOSED   = 0,  ///< streaming source still closed, can do nothing
+  STREAMING_SRC_STATE_OPENING  = 1,  ///< after call open() method and start parsing streaming source
+  STREAMING_SRC_STATE_IDLE     = 2,  ///< streaming source is ready waiting for play
+  STREAMING_SRC_STATE_PLAYING  = 3,  ///< after call play() method, playing & pushing the AV data
+  STREAMING_SRC_STATE_SEEKING  = 4,  ///< after call seek() method, start seeking poisition
+  STREAMING_SRC_STATE_EOF      = 5,  ///< The position is located at end, can NOT playing
+  STREAMING_SRC_STATE_ERROR    = 6,  ///< The error status and can do nothing except close
+};
+
+
+/**
+ * @brief The input SEI data
+ *
+ */
+struct InputSeiData {
+  int32_t         type;           ///< SEI type
+  int64_t         timestamp;      ///< the frame timestamp which be attached
+  int64_t         frame_index;    ///< the frame index which be attached
+  uint8_t*        private_data;   ///< SEI really data
+  int32_t         data_size;      ///< size of really data
+};
+
+
+
+/**
+ * @brief The IMediaStreamingSource class provides access to a media streaming source demuxer. 
+ *        To playout multiple stream sources simultaneously,
+ *        create multiple media stream source objects.
+ */
+class IMediaStreamingSource : public RefCountInterface {
+public:
+  virtual ~IMediaStreamingSource() {};
+
+
+  /**
+   * @brief Opens a media streaming source with a specified URL.
+   * @param url The path of the media file. Both the local path and online path are supported.
+   * @param startPos The starting position (ms) for pushing. Default value is 0.
+   * @param auto_play whether start playing after opened
+   * @return
+   * - 0: Success.
+   * - < 0: Failure
+   */
+  virtual int open(const char* url, int64_t start_pos, bool auto_play = true) = 0;
+
+  /**
+   * @brief Close current media streaming source
+   * @return
+   * - 0: Success.
+   * - < 0: Failure
+   */
+  virtual int close() = 0;
+
+  /**
+   * @brief Gets the unique source ID of the streaming source.
+   * @return
+   * - ≧ 0: The source ID of this media player source.
+   * - < 0: Failure. 
+   */
+  virtual int getSourceId() const = 0;
+
+  /**
+   * @brief Retrieve whether video stream is valid
+   * @return: valid or invalid
+   */
+  virtual bool isVideoValid() = 0;
+
+  /**
+   * @brief Retrieve whether audio stream is valid
+   * @return: valid or invalid
+   */
+  virtual bool isAudioValid() = 0;
+
+  /**
+   * @brief Gets the duration of the streaming source.
+   * @param [out] duration A reference to the duration of the media file.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int getDuration(int64_t& duration) = 0;
+
+  /**
+   * @brief Gets the number of the streming source
+   * @param [out] count The number of the media streams in the media source.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int getStreamCount(int64_t& count) = 0;
+
+  /**
+   * @brief Gets the detailed information of a media stream.
+   * @param index The index of the media stream.
+   * @param [out] out_info The detailed information of the media stream. See \ref media::base::PlayerStreamInfo "PlayerStreamInfo" for details.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int getStreamInfo(int64_t index, media::base::PlayerStreamInfo* out_info) = 0;
+
+  /**
+   * @brief Sets whether to loop the streaming source for playback.
+   * @param loop_count The number of times of looping the media file.
+   * - 1: Play the media file once.
+   * - 2: Play the media file twice.
+   * - <= 0: Play the media file in a loop indefinitely, until {@link stop} is called.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int setLoopCount(int64_t loop_count) = 0;
+
+  /**
+   * @brief Play & push the streaming source.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int play() = 0;
+
+  /**
+   * @brief Pauses the playing & pushing of the streaming source, Keep current position.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int pause() = 0;
+
+  /**
+   * @brief Stop the playing & pushing of the streaming source, set the position to 0.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int stop() = 0;
+
+  /**
+   * @brief Sets the playback position of the streaming source.
+   *        After seek done, it will return to previous status
+   * @param newPos The new playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int seek(int64_t new_pos) = 0;
+
+  /**
+   * @brief Gets the current playback position of the media file.
+   * @param [out] pos A reference to the current playback position (ms).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int getCurrPosition(int64_t& pos) = 0;
+
+  /**
+   * @breif Gets the status of current streaming source.
+   * @return The current state machine
+   */
+  virtual STREAMING_SRC_STATE getCurrState() = 0;
+
+  /**
+   * @brief append the SEI data which can be sent attached to video packet
+   * @param type  SEI type
+   * @param timestamp the video frame timestamp which attached to
+   * @param frame_index the video frame timestamp which attached to
+   * 
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int appendSeiData(const InputSeiData& inSeiData) = 0;
+
+  /**
+   * Registers a media player source observer.
+   *
+   * Once the media player source observer is registered, you can use the observer to monitor the state change of the media player.
+   * @param observer The pointer to the IMediaStreamingSource object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int registerObserver(IMediaStreamingSourceObserver* observer) = 0;
+
+  /**
+   * Releases the media player source observer.
+   * @param observer The pointer to the IMediaStreamingSource object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure. See {@link STREAMINGSRC_ERR}.
+   */
+  virtual int unregisterObserver(IMediaStreamingSourceObserver* observer) = 0;
+
+  /**
+   * @brief Parse a media information with a specified URL.
+   * @param url : The path of the media file. Both the local path and online path are supported.
+   * @param video_info : The output video information, It means no video track while video_info.streamIndex less than 0
+   * @param audio_info : The output audio information, It means no audio track while audio_info.streamIndex less than 0
+   * @return
+   * - 0: Success.
+   * - < 0: Failure
+   */
+   virtual int parseMediaInfo(const char* url, 
+                              media::base::PlayerStreamInfo& video_info,
+                              media::base::PlayerStreamInfo& audio_info) = 0;
+
+};
+
+
+
+/**
+  * @brief  This observer interface of media streaming source
+  */
+class IMediaStreamingSourceObserver {
+  public:
+    virtual ~IMediaStreamingSourceObserver() {};
+
+
+    /**
+     * @brief Reports the playback state change.
+     *     When the state of the playback changes, 
+     *     the SDK triggers this callback to report the new playback state 
+     *     and the reason or error for the change.
+     * @param state The new playback state after change. See {@link STREAMING_SRC_STATE}.
+     * @param ec The player's error code. See {@link STREAMINGSRC_ERR}.
+     */
+    virtual void onStateChanged(STREAMING_SRC_STATE state, STREAMING_SRC_ERR err_code) = 0;
+
+    /**
+     * @brief Triggered when file is opened
+     * @param err_code The error code
+     * @return None
+     */
+    virtual void onOpenDone(STREAMING_SRC_ERR err_code) = 0;
+
+    /**
+     * @brief Triggered when seeking is done
+     * @param err_code The error code
+     * @return None
+     */
+    virtual void onSeekDone(STREAMING_SRC_ERR err_code) = 0;
+
+    /**
+     * @brief Triggered when playing is EOF
+     * @param progress_ms the progress position
+     * @param repeat_count means repeated count of playing
+     */
+    virtual void onEofOnce(int64_t progress_ms, int64_t repeat_count) = 0;
+
+    /**
+     * @brief Reports current playback progress.
+     *        The callback triggered once every one second during the playing status 
+     * @param position_ms Current playback progress (millisecond).
+     */
+    virtual void onProgress(int64_t position_ms) = 0;
+
+    /**
+     * @brief Occurs when the metadata is received.
+     *       The callback occurs when the player receives the media metadata
+     *        and reports the detailed information of the media metadata.
+     * @param data The detailed data of the media metadata.
+     * @param length The data length (bytes).
+     */
+    virtual void onMetaData(const void* data, int length) = 0;
+
+};
+
+
+
+} //namespace rtc
+} // namespace agora
+
diff --git a/headers/rtc_4.3.2.11/include/IAgoraMusicContentCenter.h b/headers/rtc_4.3.2.11/include/IAgoraMusicContentCenter.h
new file mode 100644
index 0000000..76002d6
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraMusicContentCenter.h
@@ -0,0 +1,581 @@
+//
+//  Agora Media SDK
+//
+//  Created by FanYuanYuan in 2022-05.
+//  Copyright (c) 2022 Agora IO. All rights reserved.
+//
+
+#pragma once
+
+#include "AgoraRefPtr.h"
+#include "IAgoraMediaPlayer.h"
+
+namespace agora {
+namespace rtc {
+
+/**
+ * Modes for playing songs.
+ */
+typedef enum
+{
+    /**
+     * 0: The music player is in the origin mode, which means playing the original song.
+     */
+    kMusicPlayModeOriginal = 0,
+
+    /**
+     * 1: The music player is in the accompany mode, which means playing the accompaniment only.
+     */
+    kMusicPlayModeAccompany = 1,
+    
+    /**
+     * 2: The music player is in the lead sing mode, which means playing the lead vocals.
+     */
+    kMusicPlayModeLeadSing = 2,
+
+} MusicPlayMode;
+
+typedef enum
+{
+    /**
+     * 0: No error occurs and preload succeeds.
+     */
+    kPreloadStateCompleted = 0,
+
+    /**
+     * 1: A general error occurs.
+     */
+    kPreloadStateFailed = 1,
+
+    /**
+     * 2: The media file is preloading.
+     */
+    kPreloadStatePreloading = 2,
+        /**
+     * 3: The media file is removed.
+     */
+    kPreloadStateRemoved = 3,
+} PreloadState;
+
+typedef enum
+{
+    /**
+     * 0: No error occurs and request succeeds.
+     */
+    kMusicContentCenterReasonOk = 0,
+    /**
+     * 1: A general error occurs.
+     */
+    kMusicContentCenterReasonError = 1,
+    /**
+     * 2: The gateway error. There are several possible reasons:
+     *  - Token is expired. Check if your token is expired.
+     *  - Token is invalid. Check the type of token you passed in.
+     *  - Network error. Check your network.
+     */
+    kMusicContentCenterReasonGateway = 2,
+    /**
+     * 3: Permission and resource error. There are several possible reasons:
+     *  - Your appid may not have the mcc permission. Please contact technical support 
+     *  - The resource may not exist. Please contact technical support
+     */
+    kMusicContentCenterReasonPermissionAndResource = 3,
+    /**
+     * 4: Internal data parse error. Please contact technical support
+     */
+    kMusicContentCenterReasonInternalDataParse = 4,
+    /**
+     * 5: Music loading error. Please contact technical support
+     */
+    kMusicContentCenterReasonMusicLoading = 5,
+    /**
+     * 6: Music decryption error. Please contact technical support
+     */
+    kMusicContentCenterReasonMusicDecryption = 6, 
+    /**
+     * 7: Http internal error. Please retry later.
+     */
+    kMusicContentCenterReasonHttpInternalError = 7, 
+} MusicContentCenterStateReason;
+
+typedef struct 
+{
+    /**
+     * Name of the music chart
+     */
+    const char* chartName;
+    /**
+     * Id of the music chart, which is used to get music list
+     */
+    int32_t id;
+} MusicChartInfo;
+
+enum MUSIC_CACHE_STATUS_TYPE {
+    /**
+     * 0: Music is already cached.
+     */
+    MUSIC_CACHE_STATUS_TYPE_CACHED = 0,
+    /**
+     * 1: Music is being cached.
+     */
+    MUSIC_CACHE_STATUS_TYPE_CACHING = 1
+};
+
+struct MusicCacheInfo {
+    /**
+     * The songCode of music.
+     */
+    int64_t songCode;
+    /**
+     * The cache status of the music.
+     */
+    MUSIC_CACHE_STATUS_TYPE status;
+    MusicCacheInfo():songCode(0), status(MUSIC_CACHE_STATUS_TYPE_CACHED) {}
+};
+
+class MusicChartCollection : public RefCountInterface {
+public:
+    virtual int getCount() = 0;
+    virtual MusicChartInfo* get(int index) = 0;
+protected:
+    virtual ~MusicChartCollection() = default;
+};
+
+struct MvProperty
+{
+    /**
+     * The resolution of the mv
+     */
+    const char* resolution;
+    /**
+     * The bandwidth of the mv
+     */
+    const char* bandwidth;
+};
+
+struct ClimaxSegment
+{
+    /**
+     * The start time of climax segment
+     */
+    int32_t startTimeMs;
+    /**
+     * The end time of climax segment
+     */
+    int32_t endTimeMs;
+};
+
+struct Music
+{
+    /**
+     * The songCode of music
+     */
+    int64_t songCode;
+    /**
+     * The name of music
+     */
+    const char* name;
+    /**
+     * The singer of music
+     */
+    const char* singer;
+    /**
+     * The poster url of music
+     */
+    const char* poster;
+    /**
+     * The release time of music
+     */
+    const char* releaseTime;
+    /**
+     * The duration (in seconds) of music
+     */
+    int32_t durationS;
+    /**
+     * The type of music
+     * 1, mp3 with instrumental accompaniment and original
+     * 2, mp3 only with instrumental accompaniment
+     * 3, mp3 only with original
+     * 4, mp4 with instrumental accompaniment and original
+     * 5, mv only
+     * 6, new type mp4 with instrumental accompaniment and original
+     * detail at document of music media center
+     */
+    int32_t type;
+    /**
+     * The pitch type of music. 
+     * 1, xml lyric has pitch
+     * 2, lyric has no pitch
+     */
+    int32_t pitchType;
+    /**
+     * The number of lyrics available for the music
+     */
+    int32_t lyricCount;
+    /**
+     * The lyric list of music
+     * 0, xml
+     * 1, lrc
+     */
+    int32_t* lyricList;
+    /**
+     * The number of climax segments of the music
+     */
+    int32_t climaxSegmentCount;
+    /**
+     * The climax segment list of music
+     */
+    ClimaxSegment* climaxSegmentList;
+    /**
+     * The number of mv of the music
+     * If this value is greater than zero, it means the current music has MV resource
+     */
+    int32_t mvPropertyCount;
+    /**
+     * The mv property list of music
+     */
+    MvProperty* mvPropertyList;
+};
+
+class MusicCollection : public RefCountInterface {
+public:
+    virtual int getCount() = 0;
+    virtual int getTotal() = 0;
+    virtual int getPage() = 0;
+    virtual int getPageSize() = 0;
+    virtual Music* getMusic(int32_t index) = 0;
+protected:
+    virtual ~MusicCollection() = default;
+};
+
+
+class IMusicContentCenterEventHandler {
+public:
+    /**
+     * The music chart result callback; occurs when getMusicCharts method is called.
+     * 
+     * @param requestId The request id is same as that returned by getMusicCharts.
+     * @param result The result of music chart collection
+     * @param reason The status of the request. See MusicContentCenterStateReason
+     */
+    virtual void onMusicChartsResult(const char* requestId, agora_refptr<MusicChartCollection> result, MusicContentCenterStateReason reason) = 0;
+
+    /**
+     * Music collection, occurs when getMusicCollectionByMusicChartId or searchMusic method is called.
+     * 
+     * @param requestId The request id is same as that returned by getMusicCollectionByMusicChartId or searchMusic
+     * @param result The result of music collection
+     * @param reason The status of the request. See MusicContentCenterStateReason
+     */
+    virtual void onMusicCollectionResult(const char* requestId, agora_refptr<MusicCollection> result, MusicContentCenterStateReason reason) = 0;
+
+    /**
+     * Lyric url callback of getLyric, occurs when getLyric is called
+     * 
+     * @param requestId The request id is same as that returned by getLyric
+     * @param songCode Song code
+     * @param lyricUrl  The lyric url of this music
+     * @param reason The status of the request. See MusicContentCenterStateReason
+     */
+    virtual void onLyricResult(const char* requestId, int64_t songCode, const char* lyricUrl, MusicContentCenterStateReason reason) = 0;
+
+    /**
+     * Simple info callback of getSongSimpleInfo, occurs when getSongSimpleInfo is called
+     * 
+     * @param requestId The request id is same as that returned by getSongSimpleInfo.
+     * @param songCode Song code
+     * @param simpleInfo The metadata of the music.
+     * @param reason The status of the request. See MusicContentCenterStateReason
+     */
+    virtual void onSongSimpleInfoResult(const char* requestId, int64_t songCode, const char* simpleInfo, MusicContentCenterStateReason reason) = 0;
+
+    /**
+     * Preload process callback, occurs when preload is called
+     * 
+     * @param requestId The request id is same as that returned by preload.
+     * @param songCode Song code
+     * @param percent Preload progress (0 ~ 100)
+     * @param lyricUrl  The lyric url of this music
+     * @param state Preload state; see PreloadState.
+     * @param reason The status of the request. See MusicContentCenterStateReason
+     */
+    virtual void onPreLoadEvent(const char* requestId, int64_t songCode, int percent, const char* lyricUrl, PreloadState state, MusicContentCenterStateReason reason) = 0;
+
+    virtual ~IMusicContentCenterEventHandler() {};
+};
+
+struct MusicContentCenterConfiguration {
+    /**
+     * The app ID of the project that has enabled the music content center
+     */
+    const char *appId;
+    /**
+     * Music content center need token to connect with server
+     */
+    const char *token;
+    /**
+     * The user ID when using music content center. It can be different from that of the rtc product.
+     */
+    int64_t mccUid;
+    /**
+     * The max number which the music content center caches cannot exceed 50.
+     */
+    int32_t maxCacheSize;
+    /**
+    * @technical preview
+    */
+    const char* mccDomain;
+    /**
+     * Event handler to get callback result.
+     */
+    IMusicContentCenterEventHandler* eventHandler;
+    MusicContentCenterConfiguration():appId(nullptr),token(nullptr),eventHandler(nullptr),mccUid(0),maxCacheSize(10), mccDomain(nullptr){}
+    MusicContentCenterConfiguration(const char*appid,const char* token,int64_t id,IMusicContentCenterEventHandler* handler,int32_t maxSize = 10, const char* apiurl = nullptr):
+        appId(appid),token(token),mccUid(id),eventHandler(handler),maxCacheSize(maxSize), mccDomain(apiurl){}
+};
+
+class IMusicPlayer : public IMediaPlayer {
+protected:
+    virtual ~IMusicPlayer() {};
+
+public:
+    IMusicPlayer() {};
+    using IMediaPlayer::open;
+    /**
+    * Open a media file with specified parameters.
+    *
+    * @param songCode The identifier of the media file that you want to play.
+    * @param startPos The playback position (ms) of the music file.
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int open(int64_t songCode, int64_t startPos = 0) = 0;
+
+    /**
+    * Set the mode for playing songs.
+    * You can call this method to switch from original to accompaniment or lead vocals.
+    * If you do not call this method to set the mode, the SDK plays the accompaniment by default.
+    *
+    * @param model The playing mode.
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int setPlayMode(MusicPlayMode mode) = 0;
+};
+
+class IMusicContentCenter
+{
+protected:
+    virtual ~IMusicContentCenter(){};
+public:
+    IMusicContentCenter() {};
+
+    /**
+     * Initializes the IMusicContentCenter
+     * Set token of music content center and other params
+     *
+     * @param configuration
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int initialize(const MusicContentCenterConfiguration & configuration) = 0;
+
+    /**
+     * Renew token of music content center
+     * 
+     * @param token The new token.
+     * @return 
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int renewToken(const char* token) = 0;
+
+    /**
+     * release music content center resource.
+     * 
+     */
+    virtual void release() = 0;
+
+    /**
+    *  register event handler.
+    */
+    virtual int registerEventHandler(IMusicContentCenterEventHandler* eventHandler) = 0;
+    
+    /**
+    *  unregister event handler.
+    */
+    virtual int unregisterEventHandler() = 0;
+
+    /**
+     * Creates a music player source object and return its pointer.
+     * @return
+     * - The pointer to \ref rtc::IMusicPlayer "IMusicPlayer",
+     *   if the method call succeeds.
+     * - The empty pointer NULL, if the method call fails.
+     */
+    virtual agora_refptr<IMusicPlayer> createMusicPlayer() = 0;
+
+    /**
+     * Destroy a music player source object and return result.
+     * @param music_player The pointer to \ref rtc::IMusicPlayer "IMusicPlayer".
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int destroyMusicPlayer(agora_refptr<IMusicPlayer> music_player) = 0;
+    
+    /**
+     * Get music chart collection of music.
+     * If the method call succeeds, get result from the 
+     * \ref agora::rtc::IMusicContentCenterEventHandler::onMusicChartsResult
+     * "onMusicChartsResult" callback 
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int getMusicCharts(agora::util::AString& requestId) = 0;
+    
+    /**
+     * Get music collection of the music chart by musicChartId and page info. 
+     * If the method call success, get result from the 
+     * \ref agora::rtc::IMusicContentCenterEventHandler::onMusicCollectionResult
+     * "onMusicCollectionResult" callback 
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @param musicChartId The music chart id obtained from getMusicCharts.
+     * @param page The page of the music chart, starting from 1
+     * @param pageSize The page size, max is 50.
+     * @param jsonOption The ext param, default is null.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int getMusicCollectionByMusicChartId(agora::util::AString& requestId, int32_t musicChartId, int32_t page, int32_t pageSize, const char* jsonOption = nullptr) = 0;
+    
+    /**
+     * Search music by keyword and page info.
+     * If the method call success, get result from the 
+     * \ref agora::rtc::IMusicContentCenterEventHandler::onMusicCollectionResult
+     * "onMusicCollectionResult" callback 
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @param keyWord The key word to search.
+     * @param page The page of music search result , start from 1.
+     * @param pageSize The page size, max is 50.
+     * @param jsonOption The ext param, default is null.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int searchMusic(agora::util::AString& requestId, const char* keyWord, int32_t page, int32_t pageSize, const char* jsonOption = nullptr) = 0;
+    
+    /**
+     * Preload a media file with specified parameters.
+     *
+     * @deprecated This method is deprecated. Use preload(int64_t songCode) instead.
+     * 
+     * @param songCode The identifier of the media file that you want to play.
+     * @param jsonOption The ext param, default is null.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int preload(int64_t songCode, const char* jsonOption) __deprecated = 0;
+
+    /**
+     * Preload a media file with specified parameters.
+     * 
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @param songCode The identifier of the media file that you want to play.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int preload(agora::util::AString& requestId, int64_t songCode) = 0;
+
+    /**
+     * Remove a media file cache
+     *
+     * @param songCode The identifier of the media file that you want to play.
+     * @return
+     * - 0: Success; the cached media file is removed.
+     * - < 0: Failure.
+     */
+    virtual int removeCache(int64_t songCode) = 0;
+
+    /**
+     * Get cached media files.
+     * Before calling this API, you should allocate a memory buffer that stores the cached media file information, and pass the pointer of the buffer as the input parameter cacheInfo, and set the size of the memory buffer to cacheInfoSize.
+     * The sample code below illustrates how to request the cached media file information:
+     *
+     *    cacheInfoSize = 10                                                                    // Allocate a memory buffer of 10 MusicCacheInfo size
+     *    agora::rtc::MusicCacheInfo *infos = new agora::rtc::MusicCacheInfo[cacheInfoSize];
+     *    int ret = self.imcc->getCaches(infos, cacheInfoSize);
+     *    if (ret < 0) {                                                                        // error occurred!
+     *        return; 
+     *    }
+     *    std::cout << "the cache size:" << cacheInfoSize << std::endl;                         // The cache size: 5
+     *
+     *
+     * @param cacheInfo An output parameter; A pointer to the memory buffer that stores the cached media file information. The memory buffer pointed to by cacheInfo should be allocated by yourself before calling this API.
+     * @param cacheInfoSize
+     * - Input: The number of MusicCacheInfo's size that you get from the memory. 
+     * - Output: The actual number of MusicCacheInfo struct that is returned.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int getCaches(MusicCacheInfo *cacheInfo, int32_t* cacheInfoSize) = 0;
+
+    /**
+     * Check if the media file is preloaded
+     *
+     * @param songCode The identifier of the media file that you want to play.
+     * @return
+     * - 0: Success, file is preloaded.
+     * - < 0: Failure.
+     */
+    virtual int isPreloaded(int64_t songCode) = 0;
+
+    /**
+     * Get lyric of the music.
+     *
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @param songCode The identifier of the media file that you want to play.
+     * @param lyricType The type of the lyric file. 0:xml or 1:lrc.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int getLyric(agora::util::AString& requestId, int64_t songCode, int32_t lyricType = 0) = 0;
+
+    /**
+     * Gets the metadata of a specific music. Once this method is called, the SDK triggers the onSongSimpleInfoResult callback to report the metadata of the music.
+     *
+     * @param requestId The request id you will get of this query, format is uuid.
+     * @param songCode The identifier of the media file.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int getSongSimpleInfo(agora::util::AString& requestId, int64_t songCode) = 0;
+
+    /**
+     * Get internal songCodeKey from songCode and jsonOption
+     *
+     * @param songCode The identifier of the media file.
+     * @param jsonOption An extention parameter. The default value is null. it’s a json-format string and the `key` and `value` can be customized according to your scenarios.
+     * @param internalSongCode The identifier of internal 
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+
+    virtual int getInternalSongCode(int64_t songCode, const char* jsonOption, int64_t& internalSongCode) = 0;
+
+};
+
+}  // namespace rtc
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraParameter.h b/headers/rtc_4.3.2.11/include/IAgoraParameter.h
new file mode 100644
index 0000000..35f3564
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraParameter.h
@@ -0,0 +1,309 @@
+//
+//  Agora Engine SDK
+//
+//  Created by minbo in 2019-10.
+//  Copyright (c) 2019 Agora.io. All rights reserved.
+
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#pragma once  // NOLINT(build/header_guard)
+#include "AgoraRefPtr.h"
+
+// external key
+/**
+ * set the range of ports available for connection
+ * @example "{\"rtc.udp_port_range\":[4500, 5000]}"
+ */
+#define KEY_RTC_UDP_PORT_RANGE                       "rtc.udp_port_range"
+/**
+ * set the list of ports available for connection
+ * @example  "{\"rtc.udp_port_list\":[4501, 4502, 4503, 4504, 4505, 4506]}"
+ */
+#define KEY_RTC_UDP_PORT_LIST                        "rtc.udp_port_list"
+
+/**
+ * get the fd of sending socket available for connection
+ * note: set method is not supported.
+ */
+#define KEY_RTC_UDP_SEND_FD                          "rtc.udp_send_fd"
+
+ /**
+  * set the video encoder mode (hardware or software)
+  */
+#define KEY_RTC_VIDEO_ENABLED_HW_ENCODER             "engine.video.enable_hw_encoder"
+#define KEY_RTC_VIDEO_HARDWARE_ENCODEING             "che.hardware_encoding"
+#define KEY_RTC_VIDEO_H264_HWENC                     "che.video.h264.hwenc"
+ /**
+  * set the hardware video encoder provider (nv for nvidia or qsv for intel)
+  */
+#define KEY_RTC_VIDEO_HW_ENCODER_PROVIDER            "engine.video.hw_encoder_provider"
+
+  /**
+  * set the video decoder mode (hardware or software)
+  */
+#define KEY_RTC_VIDEO_ENABLED_HW_DECODER             "engine.video.enable_hw_decoder"
+#define KEY_RTC_VIDEO_HARDWARE_DECODING              "che.hardware_decoding"
+
+ /**
+  * set the hardware video decoder provider (h264_cuvid(default) or h264_qsv)
+  */
+#define KEY_RTC_VIDEO_HW_DECODER_PROVIDER            "engine.video.hw_decoder_provider"
+
+ /**
+  * override the lua policy
+  */
+#define KEY_RTC_VIDEO_OVERRIDE_SMALLVIDEO_NOT_USE_HWENC_POLICY  "engine.video.override_smallvideo_not_use_hwenc_policy"
+
+/**
+  * enable/disable video packet retransmission, enabled by default
+ */
+#define KEY_RTC_VIDEO_RESEND                         "rtc.video_resend"
+
+/**
+  * enable/disable audio packet retransmission, enabled by default
+*/
+#define KEY_RTC_AUDIO_RESEND                         "rtc.audio_resend"
+
+/**
+  * set the bitrate ratio for video
+*/
+#define KEY_RTC_VIDEO_BITRATE_ADJUST_RATIO           "rtc.video.bitrate_adjust_ratio"
+
+/**
+  * set the minbitrate / bitrate ratio for video
+*/
+#define KEY_RTC_VIDEO_MINBITRATE_RATIO               "rtc.video.minbitrate_ratio"
+
+/**
+  * set the degradation preference
+*/
+#define KEY_RTC_VIDEO_DEGRADATION_PREFERENCE         "rtc.video.degradation_preference"
+
+/**
+  * set the degradation fps down step
+*/
+
+#define KEY_RTC_VIDEO_DEGRADATION_FPS_DOWN_STEP      "rtc.video.degradation_fps_down_step"
+/**
+  * set the degradation fps up step
+*/
+#define KEY_RTC_VIDEO_DEGRADATION_FPS_UP_STEP        "rtc.video.degradation_fps_up_step"
+
+/**
+  * set the duration ms for connection lost callback
+*/
+#define KEY_RTC_CONNECTION_LOST_PERIOD               "rtc.connection_lost_period"
+
+/**
+  * set the local ip
+*/
+#define KEY_RTC_LOCAL_IP                             "rtc.local.ip"
+
+/**
+  * set the network interface
+*/
+#define KEY_RTC_NETWORK_INTERFACE                    "rtc.network.interface"
+
+/**
+ * set the video codec type, such as "H264", "JPEG"
+ */
+#define KEY_RTC_VIDEO_MINOR_STREAM_CODEC_INDEX        "engine.video.minor_stream_codec_index"
+#define KEY_RTC_VIDEO_CODEC_INDEX                     "che.video.videoCodecIndex"
+/**
+  * only use average QP for quality scaling
+*/
+#define KEY_RTC_VIDEO_QUALITY_SCALE_ONLY_ON_AVERAGE_QP "engine.video.quality_scale_only_on_average_qp"
+
+/**
+  * low bound for quality scaling
+*/
+#define KEY_RTC_VIDEO_H264_QP_THRESHOLD_LOW         "engine.video.h264_qp_thresholds_low"
+
+/**
+  * high bound for quality scaling
+*/
+#define KEY_RTC_VIDEO_H264_QP_THRESHOLD_HIGH        "engine.video.h264_qp_thresholds_high"
+
+
+namespace agora {
+
+namespace util {
+template <class T>
+class CopyableAutoPtr;
+
+class IString;
+typedef CopyableAutoPtr<IString> AString;
+}  // namespace util
+
+namespace base {
+
+class IAgoraParameter : public RefCountInterface {
+ public:
+  /**
+   * release the resource
+   */
+  virtual void release() = 0;
+
+  /**
+   * set bool value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setBool(const char* key, bool value) = 0;
+
+  /**
+   * set int value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setInt(const char* key, int value) = 0;
+
+  /**
+   * set unsigned int value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setUInt(const char* key, unsigned int value) = 0;
+
+  /**
+   * set double value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setNumber(const char* key, double value) = 0;
+
+  /**
+   * set string value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setString(const char* key, const char* value) = 0;
+
+  /**
+   * set object value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setObject(const char* key, const char* value) = 0;
+
+  /**
+   * set array value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int setArray(const char* key, const char* value) = 0;
+  /**
+   * get bool value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getBool(const char* key, bool& value) = 0;
+
+  /**
+   * get int value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getInt(const char* key, int& value) = 0;
+
+  /**
+   * get unsigned int value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getUInt(const char* key, unsigned int& value) = 0;
+
+  /**
+   * get double value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getNumber(const char* key, double& value) = 0;
+
+  /**
+   * get string value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getString(const char* key, agora::util::AString& value) = 0;
+
+  /**
+   * get a child object value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getObject(const char* key, agora::util::AString& value) = 0;
+
+  /**
+   * get array value of the json
+   * @param [in] key
+   *        the key name
+   * @param [in, out] value
+   *        the value
+   * @return return 0 if success or an error code
+   */
+  virtual int getArray(const char* key, const char* args, agora::util::AString& value) = 0;
+
+  /**
+   * set parameters of the sdk or engine
+   * @param [in] parameters
+   *        the parameters
+   * @return return 0 if success or an error code
+   */
+  virtual int setParameters(const char* parameters) = 0;
+
+  virtual int convertPath(const char* filePath, agora::util::AString& value) = 0;
+
+  protected:
+  virtual ~IAgoraParameter() {}
+};
+
+}  // namespace base
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraRhythmPlayer.h b/headers/rtc_4.3.2.11/include/IAgoraRhythmPlayer.h
new file mode 100644
index 0000000..f657b1c
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraRhythmPlayer.h
@@ -0,0 +1,92 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2021 Agora.io. All rights reserved.
+//
+
+#pragma once  // NOLINT(build/header_guard)
+
+#include "AgoraBase.h"
+#include "AgoraRefPtr.h"
+
+namespace agora {
+namespace base {
+class IAgoraService;
+}
+
+namespace rtc {
+class ILocalAudioTrack;
+class IRtcEngineEventHandler;
+
+/**
+ The states of the rhythm player.
+ */
+enum RHYTHM_PLAYER_STATE_TYPE {
+  /** 810: The rhythm player is idle. */
+  RHYTHM_PLAYER_STATE_IDLE = 810,
+  /** 811: The rhythm player is opening files. */
+  RHYTHM_PLAYER_STATE_OPENING,
+  /** 812: Files opened successfully, the rhythm player starts decoding files. */
+  RHYTHM_PLAYER_STATE_DECODING,
+  /** 813: Files decoded successfully, the rhythm player starts mixing the two files and playing back them locally. */
+  RHYTHM_PLAYER_STATE_PLAYING,
+  /** 814: The rhythm player is starting to fail, and you need to check the error code for detailed failure reasons. */
+  RHYTHM_PLAYER_STATE_FAILED,
+};
+
+/**
+ The reason codes of the rhythm player.
+ */
+enum RHYTHM_PLAYER_REASON {
+  /** 0: The rhythm player works well. */
+  RHYTHM_PLAYER_REASON_OK = 0,
+  /** 1: The rhythm player occurs a internal error. */
+  RHYTHM_PLAYER_REASON_FAILED = 1,
+  /** 801: The rhythm player can not open the file. */
+  RHYTHM_PLAYER_REASON_CAN_NOT_OPEN = 801,
+  /** 802: The rhythm player can not play the file. */
+  RHYTHM_PLAYER_REASON_CAN_NOT_PLAY,
+  /** 803: The file duration over the limit. The file duration limit is 1.2 seconds */
+  RHYTHM_PLAYER_REASON_FILE_OVER_DURATION_LIMIT,
+};
+
+/**
+ * The configuration of rhythm player,
+ * which is set in startRhythmPlayer or configRhythmPlayer.
+ */
+struct AgoraRhythmPlayerConfig {
+  /**
+   * The number of beats per measure. The range is 1 to 9.
+   * The default value is 4,
+   * which means that each measure contains one downbeat and three upbeats.
+   */
+  int beatsPerMeasure;
+  /*
+   * The range is 60 to 360.
+   * The default value is 60,
+   * which means that the rhythm player plays 60 beats in one minute.
+   */
+  int beatsPerMinute;
+
+  AgoraRhythmPlayerConfig() : beatsPerMeasure(4), beatsPerMinute(60) {}
+};
+
+/**
+ * The IRhythmPlayer class provides access to a rhythm player entity.
+ * A rhythm player synthesizes beats, plays them locally, and pushes them remotely.
+ */
+class IRhythmPlayer : public RefCountInterface {
+protected:
+  virtual ~IRhythmPlayer() {}
+  
+public:
+  static agora_refptr<IRhythmPlayer> Create();
+  virtual int initialize(base::IAgoraService* agora_service, IRtcEngineEventHandler* event_handler, bool is_pass_thru_mode) = 0;
+  virtual int playRhythm(const char* sound1, const char* sound2, const AgoraRhythmPlayerConfig& config) = 0;
+  virtual int stopRhythm() = 0;
+  virtual int configRhythmPlayer(const AgoraRhythmPlayerConfig& config) = 0;
+  virtual agora_refptr<ILocalAudioTrack> getRhythmPlayerTrack() = 0;
+};
+
+} //namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraRtcEngine.h b/headers/rtc_4.3.2.11/include/IAgoraRtcEngine.h
new file mode 100644
index 0000000..e98efee
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraRtcEngine.h
@@ -0,0 +1,8712 @@
+//
+//  Agora Rtc Engine SDK
+//
+//  Copyright (c) 2018 Agora.io. All rights reserved.
+//
+#pragma once
+
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "IAgoraLog.h"
+#include "AgoraOptional.h"
+#include "IAudioDeviceManager.h"
+#include "IAgoraRhythmPlayer.h"
+#include "IAgoraMediaEngine.h"
+#include "IAgoraH265Transcoder.h"
+
+namespace agora {
+namespace rtm {
+class IStreamChannel;
+}
+namespace rtc {
+
+template <typename T>
+static void SetFrom(Optional<T>* s, const Optional<T>& o) {
+  if (o) {
+    *s = o;
+  }
+}
+
+template <typename T>
+static void ReplaceBy(Optional<T>* s, const Optional<T>& o) {
+  *s = o;
+}
+
+//class IAudioDeviceManager;
+
+/**
+ * The media device types.
+ */
+enum MEDIA_DEVICE_TYPE {
+  /**
+   * -1: Unknown device type.
+   */
+  UNKNOWN_AUDIO_DEVICE = -1,
+  /**
+   * 0: The audio playback device.
+   */
+  AUDIO_PLAYOUT_DEVICE = 0,
+  /**
+   * 1: The audio recording device.
+   */
+  AUDIO_RECORDING_DEVICE = 1,
+  /**
+   * 2: The video renderer.
+   */
+  VIDEO_RENDER_DEVICE = 2,
+  /**
+   * 3: The video capturer.
+   */
+  VIDEO_CAPTURE_DEVICE = 3,
+  /**
+   * 4: The audio playback device of the app.
+   */
+  AUDIO_APPLICATION_PLAYOUT_DEVICE = 4,
+  /**
+   * 5: The virtual audio playback device.
+   */
+  AUDIO_VIRTUAL_PLAYOUT_DEVICE = 5,
+  /**
+   * 6: The virtual audio recording device.
+   */
+  AUDIO_VIRTUAL_RECORDING_DEVICE = 6,
+};
+
+/**
+ The playback state of the music file.
+ */
+enum AUDIO_MIXING_STATE_TYPE {
+ /** 710: The music file is playing. */
+  AUDIO_MIXING_STATE_PLAYING = 710,
+  /** 711: The music file pauses playing. */
+  AUDIO_MIXING_STATE_PAUSED = 711,
+  /** 713: The music file stops playing. */
+  AUDIO_MIXING_STATE_STOPPED = 713,
+  /** 714: An error occurs during the playback of the audio mixing file.
+   */
+  AUDIO_MIXING_STATE_FAILED = 714,
+};
+
+/**
+ The reson codes of the local user's audio mixing file.
+ */
+enum AUDIO_MIXING_REASON_TYPE {
+  /** 701: The SDK cannot open the audio mixing file. */
+  AUDIO_MIXING_REASON_CAN_NOT_OPEN = 701,
+  /** 702: The SDK opens the audio mixing file too frequently. */
+  AUDIO_MIXING_REASON_TOO_FREQUENT_CALL = 702,
+  /** 703: The audio mixing file playback is interrupted. */
+  AUDIO_MIXING_REASON_INTERRUPTED_EOF = 703,
+  /** 721: The audio mixing file is played once. */
+  AUDIO_MIXING_REASON_ONE_LOOP_COMPLETED = 721,
+  /** 723: The audio mixing file is all played out. */
+  AUDIO_MIXING_REASON_ALL_LOOPS_COMPLETED = 723,
+  /** 724: The audio mixing file stopped by user */
+  AUDIO_MIXING_REASON_STOPPED_BY_USER = 724,
+  /** 726: The audio mixing playback has resumed by user */
+  AUDIO_MIXING_REASON_RESUMED_BY_USER = 726,
+  /** 0: The SDK can open the audio mixing file. */
+  AUDIO_MIXING_REASON_OK = 0,
+};
+
+/**
+ * The status of importing an external video stream in a live broadcast.
+ */
+enum INJECT_STREAM_STATUS {
+  /**
+   * 0: The media stream is injected successfully.
+   */
+  INJECT_STREAM_STATUS_START_SUCCESS = 0,
+  /**
+   * 1: The media stream already exists.
+   */
+  INJECT_STREAM_STATUS_START_ALREADY_EXISTS = 1,
+  /**
+   * 2: The media stream injection is unauthorized.
+   */
+  INJECT_STREAM_STATUS_START_UNAUTHORIZED = 2,
+  /**
+   * 3: Timeout occurs when injecting a media stream.
+   */
+  INJECT_STREAM_STATUS_START_TIMEDOUT = 3,
+  /**
+   * 4: The media stream injection fails.
+   */
+  INJECT_STREAM_STATUS_START_FAILED = 4,
+  /**
+   * 5: The media stream stops being injected successfully.
+   */
+  INJECT_STREAM_STATUS_STOP_SUCCESS = 5,
+  /**
+   * 6: The media stream injection that you want to stop is found.
+   */
+  INJECT_STREAM_STATUS_STOP_NOT_FOUND = 6,
+  /**
+   * 7: You are not authorized to stop the media stream injection.
+   */
+  INJECT_STREAM_STATUS_STOP_UNAUTHORIZED = 7,
+  /**
+   * 8: Timeout occurs when you stop injecting the media stream.
+   */
+  INJECT_STREAM_STATUS_STOP_TIMEDOUT = 8,
+  /**
+   * 9: Stopping injecting the media stream fails.
+   */
+  INJECT_STREAM_STATUS_STOP_FAILED = 9,
+  /**
+   * 10: The media stream is broken.
+   */
+  INJECT_STREAM_STATUS_BROKEN = 10,
+};
+
+/**
+ * The audio equalization band frequency.
+ */
+enum AUDIO_EQUALIZATION_BAND_FREQUENCY {
+  /**
+   * 0: 31 Hz.
+   */
+  AUDIO_EQUALIZATION_BAND_31 = 0,
+  /**
+   * 1: 62 Hz.
+   */
+  AUDIO_EQUALIZATION_BAND_62 = 1,
+  /**
+   * 2: 125 Hz.
+   */
+  AUDIO_EQUALIZATION_BAND_125 = 2,
+  /**
+   * 3: 250 Hz.
+   */
+  AUDIO_EQUALIZATION_BAND_250 = 3,
+  /**
+   * 4: 500 Hz.
+   */
+  AUDIO_EQUALIZATION_BAND_500 = 4,
+  /**
+   * 5: 1 KHz.
+   */
+  AUDIO_EQUALIZATION_BAND_1K = 5,
+  /**
+   * 6: 2 KHz.
+   */
+  AUDIO_EQUALIZATION_BAND_2K = 6,
+  /**
+   * 7: 4 KHz.
+   */
+  AUDIO_EQUALIZATION_BAND_4K = 7,
+  /**
+   * 8: 8 KHz.
+   */
+  AUDIO_EQUALIZATION_BAND_8K = 8,
+  /**
+   * 9: 16 KHz.
+   */
+  AUDIO_EQUALIZATION_BAND_16K = 9,
+};
+
+/**
+ * The audio reverberation type.
+ */
+enum AUDIO_REVERB_TYPE {
+  /**
+   * 0: (-20 to 10 dB), the level of the dry signal.
+   */
+  AUDIO_REVERB_DRY_LEVEL = 0,
+  /**
+   * 1: (-20 to 10 dB), the level of the early reflection signal (wet signal).
+   */
+  AUDIO_REVERB_WET_LEVEL = 1,
+  /**
+   * 2: (0 to 100 dB), the room size of the reflection.
+   */
+  AUDIO_REVERB_ROOM_SIZE = 2,
+  /**
+   * 3: (0 to 200 ms), the length of the initial delay of the wet signal in ms.
+   */
+  AUDIO_REVERB_WET_DELAY = 3,
+  /**
+   * 4: (0 to 100), the strength of the late reverberation.
+   */
+  AUDIO_REVERB_STRENGTH = 4,
+};
+
+enum STREAM_FALLBACK_OPTIONS {
+  /** 0: No fallback operation to a lower resolution stream when the network
+     condition is poor. Fallback to Scalable Video Coding (e.g. SVC)
+     is still possible, but the resolution remains in high stream.
+     The stream quality cannot be guaranteed. */
+  STREAM_FALLBACK_OPTION_DISABLED = 0,
+  /** 1: (Default) Under poor network conditions, the receiver SDK will receive
+     agora::rtc::VIDEO_STREAM_LOW. You can only set this option in
+     RtcEngineParameters::setRemoteSubscribeFallbackOption. Nothing happens when
+     you set this in RtcEngineParameters::setLocalPublishFallbackOption. */
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LOW = 1,
+  /** 2: Under poor network conditions, the SDK may receive agora::rtc::VIDEO_STREAM_LOW first,
+     then agora::rtc::VIDEO_STREAM_LAYER_1 to agora::rtc::VIDEO_STREAM_LAYER_6 if the related layer exists.
+     If the network still does not allow displaying the video, the SDK will receive audio only. */
+  STREAM_FALLBACK_OPTION_AUDIO_ONLY = 2,
+  /** 3~8: If the receiver SDK uses RtcEngineParameters::setRemoteSubscribeFallbackOption,it will receive
+     one of the streams from agora::rtc::VIDEO_STREAM_LAYER_1 to agora::rtc::VIDEO_STREAM_LAYER_6
+     if the related layer exists when the network condition is poor. The lower bound of fallback depends on
+     the STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_X. */
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_1 = 3,
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_2 = 4,
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_3 = 5,
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_4 = 6,
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_5 = 7,
+  STREAM_FALLBACK_OPTION_VIDEO_STREAM_LAYER_6 = 8,
+};
+
+enum PRIORITY_TYPE {
+  /** 50: High priority.
+   */
+  PRIORITY_HIGH = 50,
+  /** 100: (Default) normal priority.
+   */
+  PRIORITY_NORMAL = 100,
+};
+
+struct RtcConnection;
+
+/** Statistics of the local video stream.
+ */
+struct LocalVideoStats
+{
+  /**
+  * ID of the local user.
+  */
+  uid_t uid;
+  /** The actual bitrate (Kbps) while sending the local video stream.
+    * @note This value does not include the bitrate for resending the video after packet loss.
+    */
+  int sentBitrate;
+  /** The actual frame rate (fps) while sending the local video stream.
+    * @note This value does not include the frame rate for resending the video after packet loss.
+    */
+  int sentFrameRate;
+  /** The capture frame rate (fps) of the local video.
+    */
+  int captureFrameRate;
+  /** The width of the capture frame (px).
+    */
+  int captureFrameWidth;
+  /** The height of the capture frame (px).
+    */
+  int captureFrameHeight;
+  /**
+    * The regulated frame rate of capture frame rate according to video encoder configuration.
+    */
+  int regulatedCaptureFrameRate;
+  /**
+    * The regulated frame width (pixel) of capture frame width according to video encoder configuration.
+    */
+  int regulatedCaptureFrameWidth;
+  /**
+    * The regulated frame height (pixel) of capture frame height according to video encoder configuration.
+    */
+  int regulatedCaptureFrameHeight;
+  /** The output frame rate (fps) of the local video encoder.
+    */
+  int encoderOutputFrameRate;
+  /** The width of the encoding frame (px).
+    */
+  int encodedFrameWidth;
+  /** The height of the encoding frame (px).
+    */
+  int encodedFrameHeight;
+  /** The output frame rate (fps) of the local video renderer.
+    */
+  int rendererOutputFrameRate;
+  /** The target bitrate (Kbps) of the current encoder. This is an estimate made by the SDK based on the current network conditions.
+    */
+  int targetBitrate;
+  /** The target frame rate (fps) of the current encoder.
+    */
+  int targetFrameRate;
+  /** Quality adaption of the local video stream in the reported interval (based on the target frame
+    * rate and target bitrate). See #QUALITY_ADAPT_INDICATION.
+    */
+  QUALITY_ADAPT_INDICATION qualityAdaptIndication;
+  /** The bitrate (Kbps) while encoding the local video stream.
+    * @note This value does not include the bitrate for resending the video after packet loss.
+    */
+  int encodedBitrate;
+  /** The number of the sent video frames, represented by an aggregate value.
+    */
+  int encodedFrameCount;
+  /** The codec type of the local video. See #VIDEO_CODEC_TYPE.
+    */
+  VIDEO_CODEC_TYPE codecType;
+  /**
+    * The video packet loss rate (%) from the local client to the Agora server before applying the anti-packet loss strategies.
+    */
+  unsigned short txPacketLossRate;
+  /** The brightness level of the video image captured by the local camera. See #CAPTURE_BRIGHTNESS_LEVEL_TYPE.
+    */
+  CAPTURE_BRIGHTNESS_LEVEL_TYPE captureBrightnessLevel;
+  /**
+    * Whether we send dual stream now.
+    */
+  bool dualStreamEnabled;
+  /** The hwEncoderAccelerating of the local video:
+    * - software = 0.
+    * - hardware = 1.
+    */
+  int hwEncoderAccelerating;
+  /** The dimensions of the simulcast streams's encoding frame.
+    */
+  VideoDimensions simulcastDimensions[SimulcastConfig::STREAM_LAYER_COUNT_MAX];
+};
+
+/**
+ * Audio statistics of the remote user.
+ */
+struct RemoteAudioStats
+{
+  /**
+   * User ID of the remote user sending the audio stream.
+   */
+  uid_t uid;
+  /**
+   * The quality of the remote audio: #QUALITY_TYPE.
+   */
+  int quality;
+  /**
+   * The network delay (ms) from the sender to the receiver.
+   */
+  int networkTransportDelay;
+  /**
+   * The network delay (ms) from the receiver to the jitter buffer.
+   * @note When the receiving end is an audience member and `audienceLatencyLevel` of `ClientRoleOptions`
+   * is 1, this parameter does not take effect.
+   */
+  int jitterBufferDelay;
+  /**
+   * The audio frame loss rate in the reported interval.
+   */
+  int audioLossRate;
+  /**
+   * The number of channels.
+   */
+  int numChannels;
+  /**
+   * The sample rate (Hz) of the remote audio stream in the reported interval.
+   */
+  int receivedSampleRate;
+  /**
+   * The average bitrate (Kbps) of the remote audio stream in the reported
+   * interval.
+   */
+  int receivedBitrate;
+  /**
+   * The total freeze time (ms) of the remote audio stream after the remote
+   * user joins the channel.
+   *
+   * In a session, audio freeze occurs when the audio frame loss rate reaches 4%.
+   */
+  int totalFrozenTime;
+  /**
+   * The total audio freeze time as a percentage (%) of the total time when the
+   * audio is available.
+   */
+  int frozenRate;
+  /**
+   * The quality of the remote audio stream as determined by the Agora
+   * real-time audio MOS (Mean Opinion Score) measurement method in the
+   * reported interval. The return value ranges from 0 to 500. Dividing the
+   * return value by 100 gets the MOS score, which ranges from 0 to 5. The
+   * higher the score, the better the audio quality.
+   *
+   * | MOS score       | Perception of audio quality                                                                                                                                 |
+   * |-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
+   * | Greater than 4  | Excellent. The audio sounds clear and smooth.                                                                                                               |
+   * | From 3.5 to 4   | Good. The audio has some perceptible impairment, but still sounds clear.                                                                                    |
+   * | From 3 to 3.5   | Fair. The audio freezes occasionally and requires attentive listening.                                                                                      |
+   * | From 2.5 to 3   | Poor. The audio sounds choppy and requires considerable effort to understand.                                                                               |
+   * | From 2 to 2.5   | Bad. The audio has occasional noise. Consecutive audio dropouts occur, resulting in some information loss. The users can communicate only with difficulty.  |
+   * | Less than 2     | Very bad. The audio has persistent noise. Consecutive audio dropouts are frequent, resulting in severe information loss. Communication is nearly impossible. |
+   */
+  int mosValue;
+  /**
+   * If the packet loss concealment (PLC) occurs for N consecutive times, freeze is considered as PLC occurring for M consecutive times.
+   * freeze cnt = (n_plc - n) / m
+   */
+  uint32_t frozenRateByCustomPlcCount;
+  /**
+   * The number of audio packet loss concealment
+   */
+  uint32_t plcCount;
+
+  /**
+   * The number of times the remote audio stream has experienced freezing.
+   */
+  uint32_t frozenCntByCustom;
+
+  /**
+   * The total duration (ms) that the remote audio stream has been in a frozen state.
+   */
+  uint32_t frozenTimeByCustom;
+
+  /**
+   * The total time (ms) when the remote user neither stops sending the audio
+   * stream nor disables the audio module after joining the channel.
+   */
+  int totalActiveTime;
+  /**
+   * The total publish duration (ms) of the remote audio stream.
+   */
+  int publishDuration;
+  /**
+   * Quality of experience (QoE) of the local user when receiving a remote audio stream. See #EXPERIENCE_QUALITY_TYPE.
+   */
+  int qoeQuality;
+  /**
+   * The reason for poor QoE of the local user when receiving a remote audio stream. See #EXPERIENCE_POOR_REASON.
+   */
+  int qualityChangedReason;
+  /**
+   * The total number of audio bytes received (bytes), inluding the FEC bytes, represented by an aggregate value.
+   */
+  unsigned int rxAudioBytes;
+  /**
+   * The end-to-end delay (ms) from the sender to the receiver.
+   */
+  int e2eDelay;
+
+  RemoteAudioStats()
+      : uid(0),
+        quality(0),
+        networkTransportDelay(0),
+        jitterBufferDelay(0),
+        audioLossRate(0),
+        numChannels(0),
+        receivedSampleRate(0),
+        receivedBitrate(0),
+        totalFrozenTime(0),
+        frozenRate(0),
+        mosValue(0),
+        frozenRateByCustomPlcCount(0),
+        plcCount(0),
+        frozenCntByCustom(0),
+        frozenTimeByCustom(0),
+        totalActiveTime(0),
+        publishDuration(0),
+        qoeQuality(0),
+        qualityChangedReason(0),
+        rxAudioBytes(0),
+        e2eDelay(0) {}
+};
+
+/**
+ * The statistics of the remote video stream.
+ */
+struct RemoteVideoStats {
+  /**
+   * ID of the remote user sending the video stream.
+   */
+  uid_t uid;
+  /**
+   * @deprecated Time delay (ms).
+   *
+   * In scenarios where audio and video is synchronized, you can use the
+   * value of `networkTransportDelay` and `jitterBufferDelay` in `RemoteAudioStats`
+   * to know the delay statistics of the remote video.
+   */
+  int delay __deprecated;
+  /**
+   * End-to-end delay from video capturer to video renderer. Hardware capture or render delay is excluded.
+   */
+  int e2eDelay;
+  /**
+   * The width (pixels) of the video stream.
+   */
+  int width;
+  /**
+   * The height (pixels) of the video stream.
+   */
+  int height;
+  /**
+   * Bitrate (Kbps) received since the last count.
+   */
+  int receivedBitrate;
+  /** The decoder input frame rate (fps) of the remote video.
+   */
+  int decoderInputFrameRate;
+  /** The decoder output frame rate (fps) of the remote video.
+   */
+  int decoderOutputFrameRate;
+  /** The render output frame rate (fps) of the remote video.
+   */
+  int rendererOutputFrameRate;
+  /** The video frame loss rate (%) of the remote video stream in the reported interval.
+   */
+  int frameLossRate;
+  /** Packet loss rate (%) of the remote video stream after using the anti-packet-loss method.
+   */
+  int packetLossRate;
+  /**
+   * The type of the remote video stream: #VIDEO_STREAM_TYPE.
+   */
+  VIDEO_STREAM_TYPE rxStreamType;
+  /**
+     The total freeze time (ms) of the remote video stream after the remote user joins the channel.
+     In a video session where the frame rate is set to no less than 5 fps, video freeze occurs when
+     the time interval between two adjacent renderable video frames is more than 500 ms.
+     */
+  int totalFrozenTime;
+  /**
+   The total video freeze time as a percentage (%) of the total time when the video is available.
+   */
+  int frozenRate;
+  /**
+   The offset (ms) between audio and video stream. A positive value indicates the audio leads the
+   video, and a negative value indicates the audio lags the video.
+   */
+  int avSyncTimeMs;
+  /**
+   * The total time (ms) when the remote user neither stops sending the audio
+   * stream nor disables the audio module after joining the channel.
+   */
+  int totalActiveTime;
+  /**
+   * The total publish duration (ms) of the remote audio stream.
+   */
+  int publishDuration;
+  /**
+   * The quality of the remote video stream in the reported interval. 
+   * The quality is determined by the Agora real-time video MOS (Mean Opinion Score) measurement method. 
+   * The return value range is [0, 500]. 
+   * Dividing the return value by 100 gets the MOS score, which ranges from 0 to 5. The higher the score, the better the video quality.
+   * @note For textured video data, this parameter always returns 0.
+   */
+  int mosValue;
+  /**
+   * The total number of video bytes received (bytes), inluding the FEC bytes, represented by an aggregate value.
+   */
+  unsigned int rxVideoBytes;
+};
+
+struct VideoCompositingLayout {
+  struct Region {
+    /** User ID of the user whose video is to be displayed in the region.
+     */
+    uid_t uid;
+    /** Horizontal position of the region on the screen.
+     */
+    double x;  // [0,1]
+    /** Vertical position of the region on the screen.
+     */
+    double y;  // [0,1]
+    /**
+     Actual width of the region.
+    */
+    double width;  // [0,1]
+    /** Actual height of the region. */
+    double height;  // [0,1]
+    /** 0 means the region is at the bottom, and 100 means the region is at the
+     * top.
+     */
+    int zOrder;  // optional, [0, 100] //0 (default): bottom most, 100: top most
+
+    /** 0 means the region is transparent, and 1 means the region is opaque. The
+     * default value is 1.0.
+     */
+    double alpha;
+
+    media::base::RENDER_MODE_TYPE renderMode;  // RENDER_MODE_HIDDEN: Crop, RENDER_MODE_FIT: Zoom to fit
+
+    Region()
+        : uid(0),
+          x(0),
+          y(0),
+          width(0),
+          height(0),
+          zOrder(0),
+          alpha(1.0),
+          renderMode(media::base::RENDER_MODE_HIDDEN) {}
+  };
+
+  /** Ignore this parameter. The width of the canvas is set by
+   agora::rtc::IRtcEngine::configPublisher, and not by
+   agora::rtc::VideoCompositingLayout::canvasWidth.
+  */
+  int canvasWidth;
+  /** Ignore this parameter. The height of the canvas is set by
+   agora::rtc::IRtcEngine::configPublisher, and not by
+   agora::rtc::VideoCompositingLayout::canvasHeight.
+  */
+  int canvasHeight;
+  /** Enter any of the 6-digit symbols defined in RGB.
+   */
+  const char* backgroundColor;  // e.g. "#C0C0C0" in RGB
+  /** Region array. Each host in the channel can have a region to display the
+   * video on the screen.
+   */
+  const Region* regions;
+  /** Number of users in the channel.
+   */
+  int regionCount;
+  /** User-defined data.
+   */
+  const char* appData;
+  /** Length of the user-defined data.
+   */
+  int appDataLength;
+
+  VideoCompositingLayout()
+      : canvasWidth(0),
+        canvasHeight(0),
+        backgroundColor(OPTIONAL_NULLPTR),
+        regions(NULL),
+        regionCount(0),
+        appData(OPTIONAL_NULLPTR),
+        appDataLength(0) {}
+};
+
+/** The definition of InjectStreamConfig.
+ */
+struct InjectStreamConfig {
+  /** Width of the stream to be added into the broadcast. The default value is
+  0; same width as the original stream.
+  */
+  int width;
+  /** Height of the stream to be added into the broadcast. The default value is
+  0; same height as the original stream.
+  */
+  int height;
+  /** Video GOP of the stream to be added into the broadcast. The default value
+  is 30.
+  */
+  int videoGop;
+  /** Video frame rate of the stream to be added into the broadcast. The
+  default value is 15 fps.
+  */
+  int videoFramerate;
+  /** Video bitrate of the stream to be added into the broadcast. The default
+  value is 400 Kbps.
+  */
+  int videoBitrate;
+  /** Audio-sampling rate of the stream to be added into the broadcast:
+  #AUDIO_SAMPLE_RATE_TYPE. The default value is 48000.
+  */
+  AUDIO_SAMPLE_RATE_TYPE audioSampleRate;
+  /** Audio bitrate of the stream to be added into the broadcast. The default
+  value is 48.
+  */
+  int audioBitrate;
+  /** Audio channels to be added into the broadcast. The default value is 1.
+  */
+  int audioChannels;
+
+  // width / height default set to 0 means pull the stream with its original
+  // resolution
+  InjectStreamConfig()
+      : width(0),
+        height(0),
+        videoGop(30),
+        videoFramerate(15),
+        videoBitrate(400),
+        audioSampleRate(AUDIO_SAMPLE_RATE_48000),
+        audioBitrate(48),
+        audioChannels(1) {}
+};
+
+/** The video stream lifecycle of CDN Live.
+ */
+enum RTMP_STREAM_LIFE_CYCLE_TYPE {
+  /** Bound to the channel lifecycle.
+  */
+  RTMP_STREAM_LIFE_CYCLE_BIND2CHANNEL = 1,
+  /** Bound to the owner identity of the RTMP stream.
+  */
+  RTMP_STREAM_LIFE_CYCLE_BIND2OWNER = 2,
+};
+
+/** The definition of PublisherConfiguration.
+*/
+struct PublisherConfiguration {
+  /** Width of the output data stream set for CDN Live. The default value is
+  360.
+  */
+  int width;
+  /** Height of the output data stream set for CDN Live. The default value is
+  640.
+  */
+  int height;
+  /** Frame rate of the output data stream set for CDN Live. The default value
+  is 15 fps.
+  */
+  int framerate;
+  /** Bitrate of the output data stream set for CDN Live. The default value is
+  500 Kbps.
+  */
+  int bitrate;
+  /** The default layout:
+  - 0: Tile horizontally
+  - 1: Layered windows
+  - 2: Tile vertically
+  */
+  int defaultLayout;
+  /** The video stream lifecycle of CDN Live: RTMP_STREAM_LIFE_CYCLE_TYPE
+  */
+  int lifecycle;
+  /** Whether the current user is the owner of the RTMP stream:
+  - True: Yes (default). The push-stream configuration takes effect.
+  - False: No. The push-stream configuration will not work.
+  */
+  bool owner;
+  /** Width of the stream to be injected. Set it as 0.
+  */
+  int injectStreamWidth;
+  /** Height of the stream to be injected. Set it as 0.
+  */
+  int injectStreamHeight;
+  /** URL address of the stream to be injected to the channel.
+  */
+  const char* injectStreamUrl;
+  /** Push-stream URL address for the picture-in-picture layouts. The default
+  value is NULL.
+  */
+  const char* publishUrl;
+  /** Push-stream URL address of the original stream which does not require
+  picture-blending. The default value is NULL.
+  */
+  const char* rawStreamUrl;
+  /** Reserved field. The default value is NULL.
+  */
+  const char* extraInfo;
+
+  PublisherConfiguration()
+      : width(640),
+        height(360),
+        framerate(15),
+        bitrate(500),
+        defaultLayout(1),
+        lifecycle(RTMP_STREAM_LIFE_CYCLE_BIND2CHANNEL),
+        owner(true),
+        injectStreamWidth(0),
+        injectStreamHeight(0),
+        injectStreamUrl(NULL),
+        publishUrl(NULL),
+        rawStreamUrl(NULL),
+        extraInfo(NULL) {}
+};
+
+/**
+ * The camera direction.
+ */
+enum CAMERA_DIRECTION {
+  /** The rear camera. */
+  CAMERA_REAR = 0,
+  /** The front camera. */
+  CAMERA_FRONT = 1,
+};
+
+/** The cloud proxy type.
+ *
+ * @since v3.3.0
+ */
+enum CLOUD_PROXY_TYPE {
+  /** 0: Do not use the cloud proxy.
+   */
+  NONE_PROXY = 0,
+  /** 1: The cloud proxy for the UDP protocol.
+   */
+  UDP_PROXY = 1,
+  /// @cond
+  /** 2: The cloud proxy for the TCP (encrypted) protocol.
+   */
+  TCP_PROXY = 2,
+  /// @endcond
+};
+
+/** Camera capturer configuration.*/
+struct CameraCapturerConfiguration {
+  /** Camera direction settings (for Android/iOS only). See: #CAMERA_DIRECTION. */
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  /**
+   * The camera direction.
+   */
+  Optional<CAMERA_DIRECTION> cameraDirection;
+
+  /*- CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_DEFAULT:
+  For iOS, if iPhone/iPad has 3 or 2 back camera, it means combination of triple (wide + ultra wide + telephoto) camera
+  or dual wide(wide + ultra wide) camera.In this situation, you can apply for ultra wide len by set smaller zoom fator
+  and bigger zoom fator for telephoto len.Otherwise, it always means wide back/front camera.
+
+  - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_WIDE_ANGLE:wide camera
+  - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_ULTRA_WIDE:ultra wide camera
+  - CAMERA_FOCAL_LENGTH_TYPE.CAMERA_FOCAL_LENGTH_TELEPHOTO:telephoto camera*/
+  Optional<CAMERA_FOCAL_LENGTH_TYPE> cameraFocalLengthType;
+#else
+  /** For windows. The device ID of the playback device. */
+  Optional<const char *> deviceId;
+#endif
+
+#if defined(__ANDROID__)
+  /**
+   * The camera id.
+   */
+  Optional<const char *> cameraId;
+#endif
+  Optional<bool> followEncodeDimensionRatio;
+    /** The video format. See VideoFormat. */
+  VideoFormat format;
+  CameraCapturerConfiguration() : format(VideoFormat(0, 0, 0)) {}
+};
+/**
+ * The configuration of the captured screen.
+ */
+struct ScreenCaptureConfiguration {
+  /**
+   * Whether to capture the window on the screen:
+   * - `true`: Capture the window.
+   * - `false`: (Default) Capture the screen, not the window.
+   */
+  bool isCaptureWindow; // true - capture window, false - capture display
+  /**
+   * (macOS only) The display ID of the screen.
+   */
+  uint32_t displayId;
+  /**
+   * (Windows only) The relative position of the shared screen to the virtual screen.
+   * @note This parameter takes effect only when you want to capture the screen on Windows.
+   */
+  Rectangle screenRect; //Windows only
+  /**
+   * (For Windows and macOS only) The window ID.
+   * @note This parameter takes effect only when you want to capture the window.
+   */
+  view_t windowId;
+  /**
+   * (For Windows and macOS only) The screen capture configuration. For details, see ScreenCaptureParameters.
+   */
+  ScreenCaptureParameters params;
+  /**
+   * (For Windows and macOS only) The relative position of the shared region to the whole screen. For details, see Rectangle.
+   *
+   * If you do not set this parameter, the SDK shares the whole screen. If the region you set exceeds the boundary of the
+   * screen, only the region within in the screen is shared. If you set width or height in Rectangle as 0, the whole
+   * screen is shared.
+   */
+  Rectangle regionRect;
+
+  ScreenCaptureConfiguration() : isCaptureWindow(false), displayId(0), windowId(0) {}
+};
+
+#if (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE)
+/** The size of the screen shot to the screen or window.
+ */
+struct SIZE {
+  /** The width of the screen shot.
+   */
+  int width;
+  /** The width of the screen shot.
+   */
+  int height;
+
+  SIZE() : width(0), height(0) {}
+  SIZE(int ww, int hh) : width(ww), height(hh) {}
+};
+#endif
+
+#if defined(_WIN32) || (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE)
+/**
+ * The image content of the thumbnail or icon.
+ * @note The default image is in the RGBA format. If you need to use another format, you need to convert the image on
+ * your own.
+ */
+struct ThumbImageBuffer {
+  /**
+   * The buffer of the thumbnail ot icon.
+   */
+  const char* buffer;
+  /**
+   * The buffer length of the thumbnail or icon, in bytes.
+   */
+  unsigned int length;
+  /**
+   * The actual width (px) of the thumbnail or icon.
+   */
+  unsigned int width;
+  /**
+   * The actual height (px) of the thumbnail or icon.
+   */
+  unsigned int height;
+  ThumbImageBuffer() : buffer(nullptr), length(0), width(0), height(0) {}
+};
+/**
+ * The type of the shared target. Set in ScreenCaptureSourceInfo.
+ */
+enum ScreenCaptureSourceType {
+  /** -1: Unknown type. */
+  ScreenCaptureSourceType_Unknown = -1,
+  /** 0: The shared target is a window.*/
+  ScreenCaptureSourceType_Window = 0,
+  /** 1: The shared target is a screen of a particular monitor.*/
+  ScreenCaptureSourceType_Screen = 1,
+  /** 2: Reserved parameter.*/
+  ScreenCaptureSourceType_Custom = 2,
+};
+/** The information about the specified shareable window or screen. It is returned in IScreenCaptureSourceList. */
+struct ScreenCaptureSourceInfo {
+  /**
+   * The type of the shared target. See \ref agora::rtc::ScreenCaptureSourceType "ScreenCaptureSourceType".
+   */
+  ScreenCaptureSourceType type;
+  /**
+   * The window ID for a window or the display ID for a screen.
+   */
+  view_t sourceId;
+  /**
+   * The name of the window or screen. UTF-8 encoding.
+   */
+  const char* sourceName;
+  /**
+   * The image content of the thumbnail. See ThumbImageBuffer.
+   */
+  ThumbImageBuffer thumbImage;
+  /**
+   * The image content of the icon. See ThumbImageBuffer.
+   */
+  ThumbImageBuffer iconImage;
+  /**
+   * The process to which the window belongs. UTF-8 encoding.
+   */
+  const char* processPath;
+  /**
+   * The title of the window. UTF-8 encoding.
+   */
+  const char* sourceTitle;
+  /**
+   * Determines whether the screen is the primary display:
+   * - true: The screen is the primary display.
+   * - false: The screen is not the primary display.
+   */
+  bool primaryMonitor;
+  bool isOccluded;
+  /**
+   * The relative position of the shared region to the screen space (A virtual space include all the screens). See Rectangle.
+   */
+  Rectangle position;
+#if defined(_WIN32)
+  /**
+   * Determines whether the window is minimized.
+   */
+  bool minimizeWindow;
+  /**
+   * The display ID to the window of interest.
+   * If the window intersects one or more display monitor rectangles, the return value is an valid
+   * ID to the display monitor that has the largest area of intersection with the window, Otherwise
+   * the return value is -2.
+   */
+  view_t sourceDisplayId;
+  ScreenCaptureSourceInfo() : type(ScreenCaptureSourceType_Unknown), sourceId(nullptr), sourceName(nullptr),
+                              processPath(nullptr), sourceTitle(nullptr), primaryMonitor(false), isOccluded(false), minimizeWindow(false), sourceDisplayId((view_t)-2) {}
+#else
+  ScreenCaptureSourceInfo() : type(ScreenCaptureSourceType_Unknown), sourceId(nullptr), sourceName(nullptr), processPath(nullptr), sourceTitle(nullptr), primaryMonitor(false), isOccluded(false) {}
+#endif
+};
+/**
+ * The IScreenCaptureSourceList class. This class is returned in the getScreenCaptureSources method.
+ */
+class IScreenCaptureSourceList {
+ protected:
+  virtual ~IScreenCaptureSourceList(){};
+
+ public:
+  /**
+   * Gets the number of shareable cpp and screens.
+   *
+   * @return The number of shareable cpp and screens.
+   */
+  virtual unsigned int getCount() = 0;
+  /**
+   * Gets information about the specified shareable window or screen.
+   *
+   * After you get IScreenCaptureSourceList, you can pass in the index value of the specified shareable window or
+   * screen to get information about that window or screen from ScreenCaptureSourceInfo.
+   *
+   * @param index The index of the specified shareable window or screen. The value range is [0, getCount()).
+   * @return ScreenCaptureSourceInfo The information of the specified window or screen.
+   */
+  virtual ScreenCaptureSourceInfo getSourceInfo(unsigned int index) = 0;
+  /**
+   * Releases IScreenCaptureSourceList.
+   *
+   * After you get the list of shareable cpp and screens, to avoid memory leaks, call this method to release
+   * IScreenCaptureSourceList instead of deleting IScreenCaptureSourceList directly.
+   */
+  virtual void release() = 0;
+};
+#endif // _WIN32 || (__APPLE__ && !TARGET_OS_IPHONE && TARGET_OS_MAC)
+/**
+ * The advanced options for audio.
+ */
+struct AdvancedAudioOptions {
+  /**
+   * Audio processing channels, only support 1 or 2.
+   */
+   Optional<int> audioProcessingChannels;
+
+   AdvancedAudioOptions() {}
+  ~AdvancedAudioOptions() {}
+};
+
+struct ImageTrackOptions {
+  const char* imageUrl;
+  int fps;
+  VIDEO_MIRROR_MODE_TYPE mirrorMode;
+  ImageTrackOptions() : imageUrl(NULL), fps(1), mirrorMode(VIDEO_MIRROR_MODE_DISABLED) {}
+};
+
+/**
+ * The channel media options.
+ *
+ * Agora supports publishing multiple audio streams and one video stream at the same time and in the same RtcConnection.
+ * For example, `publishAudioTrack`, `publishCustomAudioTrack` and `publishMediaPlayerAudioTrack` can be true at the same time;
+ * but only one of `publishCameraTrack`, `publishScreenTrack`, `publishCustomVideoTrack`, and `publishEncodedVideoTrack` can be
+ * true at the same time.
+ */
+struct ChannelMediaOptions {
+  /**
+   * Whether to publish the video of the camera track.
+   * - `true`: (Default) Publish the video track of the camera capturer.
+   * - `false`: Do not publish the video track of the camera capturer.
+   */
+  Optional<bool> publishCameraTrack;
+  /**
+   * Whether to publish the video of the secondary camera track.
+   * - `true`: Publish the video track of the secondary camera capturer.
+   * - `false`: (Default) Do not publish the video track of the secondary camera capturer.
+   */
+  Optional<bool> publishSecondaryCameraTrack;
+    /**
+   * Whether to publish the video of the third camera track.
+   * - `true`:  Publish the video track of the third camera capturer.
+   * - `false`: (Default) Do not publish the video track of the third camera capturer.
+   */
+  Optional<bool> publishThirdCameraTrack;
+  /**
+   * Whether to publish the video of the fourth camera track.
+   * - `true`:  Publish the video track of the fourth camera capturer.
+   * - `false`: (Default) Do not publish the video track of the fourth camera capturer.
+   */
+  Optional<bool> publishFourthCameraTrack;
+  /**
+   * Whether to publish the recorded audio.
+   * - `true`: (Default) Publish the recorded audio.
+   * - `false`: Do not publish the recorded audio.
+   */
+  Optional<bool> publishMicrophoneTrack;
+
+  #if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
+  /**
+   * Whether to publish the video track of the screen capturer:
+   * - `true`: Publish the video track of the screen capture.
+   * - `false`: (Default) Do not publish the video track of the screen capture.
+   */
+  Optional<bool> publishScreenCaptureVideo;
+  /**
+   * Whether to publish the audio track of the screen capturer:
+   * - `true`: Publish the video audio of the screen capturer.
+   * - `false`: (Default) Do not publish the audio track of the screen capturer.
+   */
+  Optional<bool> publishScreenCaptureAudio;
+  #else
+  /**
+   * Whether to publish the captured video from the screen:
+   * - `true`: PPublish the captured video from the screen.
+   * - `false`: (Default) Do not publish the captured video from the screen.
+   */
+  Optional<bool> publishScreenTrack;
+  /**
+   * Whether to publish the captured video from the secondary screen:
+   * - true: Publish the captured video from the secondary screen.
+   * - false: (Default) Do not publish the captured video from the secondary screen.
+   */
+  Optional<bool> publishSecondaryScreenTrack;
+    /**
+   * Whether to publish the captured video from the third screen:
+   * - true: Publish the captured video from the third screen.
+   * - false: (Default) Do not publish the captured video from the third screen.
+   */
+  Optional<bool> publishThirdScreenTrack;
+  /**
+   * Whether to publish the captured video from the fourth screen:
+   * - true: Publish the captured video from the fourth screen.
+   * - false: (Default) Do not publish the captured video from the fourth screen.
+   */
+  Optional<bool> publishFourthScreenTrack;
+  #endif
+
+  /**
+   * Whether to publish the captured audio from a custom source:
+   * - true: Publish the captured audio from a custom source.
+   * - false: (Default) Do not publish the captured audio from the custom source.
+   */
+  Optional<bool> publishCustomAudioTrack;
+  /**
+   * The custom audio track id. The default value is 0.
+   */
+  Optional<int> publishCustomAudioTrackId;
+  /**
+   * Whether to publish the captured video from a custom source:
+   * - `true`: Publish the captured video from a custom source.
+   * - `false`: (Default) Do not publish the captured video from the custom source.
+   */
+  Optional<bool> publishCustomVideoTrack;
+  /**
+   * Whether to publish the encoded video:
+   * - `true`: Publish the encoded video.
+   * - `false`: (Default) Do not publish the encoded video.
+   */
+  Optional<bool> publishEncodedVideoTrack;
+  /**
+  * Whether to publish the audio from the media player:
+  * - `true`: Publish the audio from the media player.
+  * - `false`: (Default) Do not publish the audio from the media player.
+  */
+  Optional<bool> publishMediaPlayerAudioTrack;
+  /**
+  * Whether to publish the video from the media player:
+  * - `true`: Publish the video from the media player.
+  * - `false`: (Default) Do not publish the video from the media player.
+  */
+  Optional<bool> publishMediaPlayerVideoTrack;
+  /**
+  * Whether to publish the local transcoded video track.
+  * - `true`: Publish the video track of local transcoded video track.
+  * - `false`: (Default) Do not publish the local transcoded video track.
+  */
+  Optional<bool> publishTranscodedVideoTrack;
+    /**
+  * Whether to publish the local mixed track.
+  * - `true`: Publish the audio track of local mixed track.
+  * - `false`: (Default) Do not publish the local mixed track.
+  */
+  Optional<bool> publishMixedAudioTrack;
+  /**
+   * Whether to publish the local lip sync video track.
+   * - `true`: Publish the video track of local lip sync  video track.
+   * - `false`: (Default) Do not publish the local lip sync  video track.
+   */
+  Optional<bool> publishLipSyncTrack;
+  /**
+   * Whether to automatically subscribe to all remote audio streams when the user joins a channel:
+   * - `true`: (Default) Subscribe to all remote audio streams.
+   * - `false`: Do not subscribe to any remote audio stream.
+   */
+  Optional<bool> autoSubscribeAudio;
+  /**
+   * Whether to subscribe to all remote video streams when the user joins the channel:
+   * - `true`: (Default) Subscribe to all remote video streams.
+   * - `false`: Do not subscribe to any remote video stream.
+   */
+  Optional<bool> autoSubscribeVideo;
+  /**
+   * Whether to enable audio capturing or playback.
+   * - `true`: (Default) Enable audio capturing and playback.
+   * - `false`: Do not enable audio capturing or playback.
+   */
+  Optional<bool> enableAudioRecordingOrPlayout;
+  /**
+  * The ID of the media player to be published. The default value is 0.
+  */
+  Optional<int> publishMediaPlayerId;
+  /**
+   * The client role type. See \ref CLIENT_ROLE_TYPE.
+   * Default is CLIENT_ROLE_AUDIENCE.
+   */
+  Optional<CLIENT_ROLE_TYPE> clientRoleType;
+  /**
+   * The audience latency level type. See #AUDIENCE_LATENCY_LEVEL_TYPE.
+   */
+  Optional<AUDIENCE_LATENCY_LEVEL_TYPE> audienceLatencyLevel;
+  /**
+   * The default video stream type. See \ref VIDEO_STREAM_TYPE.
+   * Default is VIDEO_STREAM_HIGH.
+   */
+  Optional<VIDEO_STREAM_TYPE> defaultVideoStreamType;
+  /**
+   * The channel profile. See \ref CHANNEL_PROFILE_TYPE.
+   * Default is CHANNEL_PROFILE_LIVE_BROADCASTING.
+   */
+  Optional<CHANNEL_PROFILE_TYPE> channelProfile;
+  /**
+   * The delay in ms for sending audio frames. This is used for explicit control of A/V sync.
+   * To switch off the delay, set the value to zero.
+   */
+  Optional<int> audioDelayMs;
+  /**
+   * The delay in ms for sending media player audio frames. This is used for explicit control of A/V sync.
+   * To switch off the delay, set the value to zero.
+   */
+  Optional<int> mediaPlayerAudioDelayMs;
+  /**
+   * (Optional) The token generated on your server for authentication.
+   * @note
+   * - This parameter takes effect only when calling `updateChannelMediaOptions` or `updateChannelMediaOptionsEx`.
+   * - Ensure that the App ID, channel name, and user name used for creating the token are the same ones as those
+   * used by the initialize method for initializing the RTC engine, and those used by the `joinChannel [2/2]`
+   * and `joinChannelEx` methods for joining the channel.
+   */
+  Optional<const char*> token;
+  /**
+   * Whether to enable media packet encryption:
+   * - `true`: Yes.
+   * - `false`: (Default) No.
+   *
+   * @note This parameter is ignored when calling `updateChannelMediaOptions`.
+   */
+  Optional<bool> enableBuiltInMediaEncryption;
+  /**
+   * Whether to publish the sound of the rhythm player to remote users:
+   * - `true`: (Default) Publish the sound of the rhythm player.
+   * - `false`: Do not publish the sound of the rhythm player.
+   */
+  Optional<bool> publishRhythmPlayerTrack;
+  /**
+   * Whether the user is an interactive audience member in the channel.
+   * - `true`: Enable low lentancy and smooth video when joining as an audience.
+   * - `false`: (Default) Use default settings for audience role.
+   * @note This mode is only used for audience. In PK mode, client might join one channel as broadcaster, and join
+   * another channel as interactive audience to achieve low lentancy and smooth video from remote user.
+   */
+  Optional<bool> isInteractiveAudience;
+  /**
+   * The custom video track id which will used to publish or preview.
+   * You can get the VideoTrackId after calling createCustomVideoTrack() of IRtcEngine.
+   */
+  Optional<video_track_id_t> customVideoTrackId;
+  /**
+   * Whether local audio stream can be filtered.
+   * - `true`: (Default) Can be filtered when audio level is low.
+   * - `false`: Do not Filter this audio stream.
+   */
+  Optional<bool> isAudioFilterable;
+
+  /** Provides the technical preview functionalities or special customizations by configuring the SDK with JSON options.
+      Pointer to the set parameters in a JSON string.
+    * @technical preview
+   */
+  Optional<const char*> parameters;
+
+  ChannelMediaOptions() {}
+  ~ChannelMediaOptions() {}
+
+  void SetAll(const ChannelMediaOptions& change) {
+#define SET_FROM(X) SetFrom(&X, change.X)
+
+      SET_FROM(publishCameraTrack);
+      SET_FROM(publishSecondaryCameraTrack);
+      SET_FROM(publishThirdCameraTrack);
+      SET_FROM(publishFourthCameraTrack);      
+      SET_FROM(publishMicrophoneTrack);
+#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
+      SET_FROM(publishScreenCaptureVideo);
+      SET_FROM(publishScreenCaptureAudio);
+#else
+      SET_FROM(publishScreenTrack);
+      SET_FROM(publishSecondaryScreenTrack);
+      SET_FROM(publishThirdScreenTrack);
+      SET_FROM(publishFourthScreenTrack);
+#endif
+      SET_FROM(publishTranscodedVideoTrack);
+      SET_FROM(publishMixedAudioTrack);
+      SET_FROM(publishLipSyncTrack);
+      SET_FROM(publishCustomAudioTrack);
+      SET_FROM(publishCustomAudioTrackId);
+      SET_FROM(publishCustomVideoTrack);
+      SET_FROM(publishEncodedVideoTrack);
+      SET_FROM(publishMediaPlayerAudioTrack);
+      SET_FROM(publishMediaPlayerVideoTrack);
+      SET_FROM(autoSubscribeAudio);
+      SET_FROM(autoSubscribeVideo);
+      SET_FROM(publishMediaPlayerId);
+      SET_FROM(enableAudioRecordingOrPlayout);
+      SET_FROM(clientRoleType);
+      SET_FROM(audienceLatencyLevel);
+      SET_FROM(defaultVideoStreamType);
+      SET_FROM(channelProfile);
+      SET_FROM(audioDelayMs);
+      SET_FROM(mediaPlayerAudioDelayMs);
+      SET_FROM(token);
+      SET_FROM(enableBuiltInMediaEncryption);
+      SET_FROM(publishRhythmPlayerTrack);
+      SET_FROM(customVideoTrackId);
+      SET_FROM(isAudioFilterable);
+      SET_FROM(isInteractiveAudience);
+      SET_FROM(parameters);
+#undef SET_FROM
+  }
+
+  bool operator==(const ChannelMediaOptions& o) const {
+#define BEGIN_COMPARE() bool b = true
+#define ADD_COMPARE(X) b = (b && (X == o.X))
+#define END_COMPARE()
+
+      BEGIN_COMPARE();
+      ADD_COMPARE(publishCameraTrack);
+      ADD_COMPARE(publishSecondaryCameraTrack);
+      ADD_COMPARE(publishThirdCameraTrack);
+      ADD_COMPARE(publishFourthCameraTrack);
+      ADD_COMPARE(publishMicrophoneTrack);
+#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
+      ADD_COMPARE(publishScreenCaptureVideo);
+      ADD_COMPARE(publishScreenCaptureAudio);
+#else
+      ADD_COMPARE(publishScreenTrack);
+      ADD_COMPARE(publishSecondaryScreenTrack);
+      ADD_COMPARE(publishThirdScreenTrack);
+      ADD_COMPARE(publishFourthScreenTrack);
+#endif
+      ADD_COMPARE(publishTranscodedVideoTrack);
+      ADD_COMPARE(publishMixedAudioTrack);
+      ADD_COMPARE(publishLipSyncTrack);
+      ADD_COMPARE(publishCustomAudioTrack);
+      ADD_COMPARE(publishCustomAudioTrackId);
+      ADD_COMPARE(publishCustomVideoTrack);
+      ADD_COMPARE(publishEncodedVideoTrack);
+      ADD_COMPARE(publishMediaPlayerAudioTrack);
+      ADD_COMPARE(publishMediaPlayerVideoTrack);
+      ADD_COMPARE(autoSubscribeAudio);
+      ADD_COMPARE(autoSubscribeVideo);
+      ADD_COMPARE(publishMediaPlayerId);
+      ADD_COMPARE(enableAudioRecordingOrPlayout);
+      ADD_COMPARE(clientRoleType);
+      ADD_COMPARE(audienceLatencyLevel);
+      ADD_COMPARE(defaultVideoStreamType);
+      ADD_COMPARE(channelProfile);
+      ADD_COMPARE(audioDelayMs);
+      ADD_COMPARE(mediaPlayerAudioDelayMs);
+      ADD_COMPARE(token);
+      ADD_COMPARE(enableBuiltInMediaEncryption);
+      ADD_COMPARE(publishRhythmPlayerTrack);
+      ADD_COMPARE(customVideoTrackId);
+      ADD_COMPARE(isAudioFilterable);
+      ADD_COMPARE(isInteractiveAudience);
+      ADD_COMPARE(parameters);
+      END_COMPARE();
+
+#undef BEGIN_COMPARE
+#undef ADD_COMPARE
+#undef END_COMPARE
+      return b;
+  }
+
+  ChannelMediaOptions& operator=(const ChannelMediaOptions& replace) {
+    if (this != &replace) {
+#define REPLACE_BY(X) ReplaceBy(&X, replace.X)
+
+        REPLACE_BY(publishCameraTrack);
+        REPLACE_BY(publishSecondaryCameraTrack);
+        REPLACE_BY(publishThirdCameraTrack);
+        REPLACE_BY(publishFourthCameraTrack);
+        REPLACE_BY(publishMicrophoneTrack);
+#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
+        REPLACE_BY(publishScreenCaptureVideo);
+        REPLACE_BY(publishScreenCaptureAudio);
+#else
+        REPLACE_BY(publishScreenTrack);
+        REPLACE_BY(publishSecondaryScreenTrack);
+        REPLACE_BY(publishThirdScreenTrack);
+        REPLACE_BY(publishFourthScreenTrack);
+#endif
+        REPLACE_BY(publishTranscodedVideoTrack);
+        REPLACE_BY(publishMixedAudioTrack);
+        REPLACE_BY(publishLipSyncTrack);
+        REPLACE_BY(publishCustomAudioTrack);
+        REPLACE_BY(publishCustomAudioTrackId);
+        REPLACE_BY(publishCustomVideoTrack);
+        REPLACE_BY(publishEncodedVideoTrack);
+        REPLACE_BY(publishMediaPlayerAudioTrack);
+        REPLACE_BY(publishMediaPlayerVideoTrack);
+        REPLACE_BY(autoSubscribeAudio);
+        REPLACE_BY(autoSubscribeVideo);
+        REPLACE_BY(publishMediaPlayerId);
+        REPLACE_BY(enableAudioRecordingOrPlayout);
+        REPLACE_BY(clientRoleType);
+        REPLACE_BY(audienceLatencyLevel);
+        REPLACE_BY(defaultVideoStreamType);
+        REPLACE_BY(channelProfile);
+        REPLACE_BY(audioDelayMs);
+        REPLACE_BY(mediaPlayerAudioDelayMs);
+        REPLACE_BY(token);
+        REPLACE_BY(enableBuiltInMediaEncryption);
+        REPLACE_BY(publishRhythmPlayerTrack);
+        REPLACE_BY(customVideoTrackId);
+        REPLACE_BY(isAudioFilterable);
+        REPLACE_BY(isInteractiveAudience);
+        REPLACE_BY(parameters);
+#undef REPLACE_BY
+    }
+    return *this;
+  }
+};
+
+enum PROXY_TYPE {
+  /** 0: Do not use the cloud proxy.
+   */
+  NONE_PROXY_TYPE = 0,
+  /** 1: The cloud proxy for the UDP protocol.
+   */
+  UDP_PROXY_TYPE = 1,
+  /** 2: The cloud proxy for the TCP (encrypted) protocol.
+   */
+  TCP_PROXY_TYPE = 2,
+  /** 3: The local proxy.
+   */
+  LOCAL_PROXY_TYPE = 3,
+  /** 4: auto fallback to tcp cloud proxy
+   */
+  TCP_PROXY_AUTO_FALLBACK_TYPE = 4,
+  /** 5: The http proxy.
+   */
+  HTTP_PROXY_TYPE = 5,
+  /** 6: The https proxy.
+   */
+  HTTPS_PROXY_TYPE = 6,
+};
+
+enum FeatureType {
+  VIDEO_VIRTUAL_BACKGROUND = 1,
+  VIDEO_BEAUTY_EFFECT = 2,
+};
+
+/**
+ * The options for leaving a channel.
+ */
+struct LeaveChannelOptions {
+  /**
+   * Whether to stop playing and mixing the music file when a user leaves the channel.
+   * - `true`: (Default) Stop playing and mixing the music file.
+   * - `false`: Do not stop playing and mixing the music file.
+   */
+  bool stopAudioMixing;
+  /**
+   * Whether to stop playing all audio effects when a user leaves the channel.
+   * - `true`: (Default) Stop playing all audio effects.
+   * - `false`: Do not stop playing any audio effect.
+   */
+  bool stopAllEffect;
+  /**
+   * Whether to stop microphone recording when a user leaves the channel.
+   * - `true`: (Default) Stop microphone recording.
+   * - `false`: Do not stop microphone recording.
+   */
+  bool stopMicrophoneRecording;
+
+  LeaveChannelOptions() : stopAudioMixing(true), stopAllEffect(true), stopMicrophoneRecording(true) {}
+};
+
+/**
+ * The IRtcEngineEventHandler class.
+ *
+ * The SDK uses this class to send callback event notifications to the app, and the app inherits
+ * the methods in this class to retrieve these event notifications.
+ *
+ * All methods in this class have their default (empty)  implementations, and the app can inherit
+ * only some of the required events instead of all. In the callback methods, the app should avoid
+ * time-consuming tasks or calling blocking APIs, otherwise the SDK may not work properly.
+ */
+class IRtcEngineEventHandler {
+ public:
+  virtual ~IRtcEngineEventHandler() {}
+
+  virtual const char* eventHandlerType() const { return "event_handler"; }
+
+  /**
+   * Occurs when a user joins a channel.
+   *
+   * This callback notifies the application that a user joins a specified channel.
+   *
+   * @param channel The channel name.
+   * @param uid The ID of the user who joins the channel.
+   * @param elapsed The time elapsed (ms) from the local user calling joinChannel until the SDK triggers this callback.
+   */
+  virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
+    (void)channel;
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a user rejoins the channel.
+   *
+   * When a user loses connection with the server because of network problems, the SDK automatically tries to reconnect
+   * and triggers this callback upon reconnection.
+   *
+   * @param channel The channel name.
+   * @param uid The ID of the user who rejoins the channel.
+   * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method until this callback is triggered.
+   */
+  virtual void onRejoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
+    (void)channel;
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /** Occurs when join success after calling \ref IRtcEngine::setLocalAccessPoint "setLocalAccessPoint" or \ref IRtcEngine::setCloudProxy "setCloudProxy"
+  @param channel Channel name.
+  @param uid  User ID of the user joining the channel.
+  @param proxyType type of proxy agora sdk connected, proxyType will be NONE_PROXY_TYPE if not connected to proxy(fallback).
+  @param localProxyIp local proxy ip. if not join local proxy, it will be "".
+  @param elapsed Time elapsed (ms) from the user calling the \ref IRtcEngine::joinChannel "joinChannel" method until the SDK triggers this callback.
+   */
+  virtual void onProxyConnected(const char* channel, uid_t uid, PROXY_TYPE proxyType, const char* localProxyIp, int elapsed) {
+    (void)channel;
+    (void)uid;
+    (void)proxyType;
+    (void)localProxyIp;
+    (void)elapsed;
+  }
+
+  /** An error occurs during the SDK runtime.
+
+  @param err The error code: #ERROR_CODE_TYPE.
+  @param msg The detailed error message.
+  */
+  virtual void onError(int err, const char* msg) {
+    (void)err;
+    (void)msg;
+  }
+
+  /** Reports the statistics of the audio stream from each remote
+  user/broadcaster.
+
+  @deprecated This callback is deprecated. Use onRemoteAudioStats instead.
+
+  The SDK triggers this callback once every two seconds to report the audio
+  quality of each remote user/host sending an audio stream. If a channel has
+  multiple remote users/hosts sending audio streams, the SDK triggers this
+  callback as many times.
+
+  @param uid The user ID of the remote user sending the audio stream.
+  @param quality The audio quality of the user: #QUALITY_TYPE
+  @param delay The network delay (ms) from the sender to the receiver, including the delay caused by audio sampling pre-processing, network transmission, and network jitter buffering.
+  @param lost The audio packet loss rate (%) from the sender to the receiver.
+  */
+  virtual void onAudioQuality(uid_t uid, int quality, unsigned short delay, unsigned short lost) __deprecated {
+    (void)uid;
+    (void)quality;
+    (void)delay;
+    (void)lost;
+  }
+
+  /** Reports the result of the last-mile network probe result.
+   *
+   * The SDK triggers this callback within 30 seconds after the app calls the `startLastmileProbeTest` method.
+   * @param result The uplink and downlink last-mile network probe test result: LastmileProbeResult.
+   */
+  virtual void onLastmileProbeResult(const LastmileProbeResult& result) {
+      (void)result;
+  }
+
+  /**
+   * Reports the volume information of users.
+   *
+   * By default, this callback is disabled. You can enable it by calling `enableAudioVolumeIndication`. Once this
+   * callback is enabled and users send streams in the channel, the SDK triggers the `onAudioVolumeIndication`
+   * callback at the time interval set in `enableAudioVolumeIndication`. The SDK triggers two independent
+   * `onAudioVolumeIndication` callbacks simultaneously, which separately report the volume information of the
+   * local user who sends a stream and the remote users (up to three) whose instantaneous volume is the highest.
+   *
+   * @note After you enable this callback, calling muteLocalAudioStream affects the SDK's behavior as follows:
+   * - If the local user stops publishing the audio stream, the SDK stops triggering the local user's callback.
+   * - 20 seconds after a remote user whose volume is one of the three highest stops publishing the audio stream,
+   * the callback excludes this user's information; 20 seconds after all remote users stop publishing audio streams,
+   * the SDK stops triggering the callback for remote users.
+   *
+   * @param speakers The volume information of the users, see AudioVolumeInfo. An empty `speakers` array in the
+   * callback indicates that no remote user is in the channel or sending a stream at the moment.
+   * @param speakerNumber The total number of speakers.
+   * - In the local user's callback, when the local user sends a stream, `speakerNumber` is 1.
+   * - In the callback for remote users, the value range of speakerNumber is [0,3]. If the number of remote users who
+   * send streams is greater than or equal to three, the value of `speakerNumber` is 3.
+   * @param totalVolume The volume of the speaker. The value ranges between 0 (lowest volume) and 255 (highest volume).
+   * - In the local user's callback, `totalVolume` is the volume of the local user who sends a stream.
+   * - In the remote users' callback, `totalVolume` is the sum of all remote users (up to three) whose instantaneous
+   * volume is the highest. If the user calls `startAudioMixing`, `totalVolume` is the volume after audio mixing.
+   */
+  virtual void onAudioVolumeIndication(const AudioVolumeInfo* speakers, unsigned int speakerNumber,
+                                       int totalVolume) {
+    (void)speakers;
+    (void)speakerNumber;
+    (void)totalVolume;
+  }
+
+  /**
+   * Occurs when a user leaves a channel.
+   *
+   * This callback notifies the app that the user leaves the channel by calling `leaveChannel`. From this callback,
+   * the app can get information such as the call duration and quality statistics.
+   *
+   * @param stats The statistics on the call: RtcStats.
+   */
+  virtual void onLeaveChannel(const RtcStats& stats) { (void)stats; }
+
+  /**
+   * Reports the statistics of the current call.
+   *
+   * The SDK triggers this callback once every two seconds after the user joins the channel.
+   *
+   * @param stats The statistics of the current call: RtcStats.
+   */
+  virtual void onRtcStats(const RtcStats& stats) { (void)stats; }
+
+  /** Occurs when the audio device state changes.
+
+   This callback notifies the application that the system's audio device state
+   is changed. For example, a headset is unplugged from the device.
+
+   @param deviceId The device ID.
+   @param deviceType The device type: #MEDIA_DEVICE_TYPE.
+   @param deviceState The device state:
+   - On macOS:
+     - 0: The device is ready for use.
+     - 8: The device is not connected.
+   - On Windows: #MEDIA_DEVICE_STATE_TYPE.
+   */
+  virtual void onAudioDeviceStateChanged(const char* deviceId, int deviceType, int deviceState) {
+    (void)deviceId;
+    (void)deviceType;
+    (void)deviceState;
+  }
+
+  /**
+   * @brief Reports current AudioMixing progress.
+   *
+   * The callback occurs once every one second during the playback and reports the current playback progress.
+   * @param position Current AudioMixing progress (millisecond).
+   */
+  virtual void onAudioMixingPositionChanged(int64_t position) {}
+
+  /** Occurs when the audio mixing file playback finishes.
+   @deprecated This method is deprecated, use onAudioMixingStateChanged instead.
+
+   After you call startAudioMixing to play a local music file, this callback occurs when the playback finishes.
+   If the startAudioMixing method call fails, the SDK returns the error code 701.
+   */
+  virtual void onAudioMixingFinished() __deprecated {}
+
+  /**
+   * Occurs when the playback of the local audio effect file finishes.
+   *
+   * This callback occurs when the local audio effect file finishes playing.
+   *
+   * @param soundId The audio effect ID. The ID of each audio effect file is unique.
+   */
+  virtual void onAudioEffectFinished(int soundId) {}
+
+  /** Occurs when the video device state changes.
+
+   This callback notifies the application that the system's video device state
+   is changed.
+
+   @param deviceId Pointer to the device ID.
+   @param deviceType Device type: #MEDIA_DEVICE_TYPE.
+   @param deviceState Device state: #MEDIA_DEVICE_STATE_TYPE.
+   */
+  virtual void onVideoDeviceStateChanged(const char* deviceId, int deviceType, int deviceState) {
+    (void)deviceId;
+    (void)deviceType;
+    (void)deviceState;
+  }
+
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  virtual void onPipStateChanged(PIP_STATE state) {
+    (void)state;
+  };
+#endif
+
+  /**
+   * Reports the last mile network quality of each user in the channel.
+   *
+   * This callback reports the last mile network conditions of each user in the channel. Last mile refers to the
+   * connection between the local device and Agora's edge server.
+   *
+   * The SDK triggers this callback once every two seconds. If a channel includes multiple users, the SDK triggers
+   * this callback as many times.
+   *
+   * @note `txQuality` is UNKNOWN when the user is not sending a stream; `rxQuality` is UNKNOWN when the user is not
+   * receiving a stream.
+   *
+   * @param uid The user ID. The network quality of the user with this user ID is reported.
+   * @param txQuality Uplink network quality rating of the user in terms of the transmission bit rate, packet loss rate,
+   * average RTT (Round-Trip Time) and jitter of the uplink network. This parameter is a quality rating helping you
+   * understand how well the current uplink network conditions can support the selected video encoder configuration.
+   * For example, a 1000 Kbps uplink network may be adequate for video frames with a resolution of 640 × 480 and a frame
+   * rate of 15 fps in the LIVE_BROADCASTING profile, but may be inadequate for resolutions higher than 1280 × 720.
+   * See #QUALITY_TYPE.
+   * @param rxQuality Downlink network quality rating of the user in terms of packet loss rate, average RTT, and jitter
+   * of the downlink network. See #QUALITY_TYPE.
+   */
+  virtual void onNetworkQuality(uid_t uid, int txQuality, int rxQuality) {
+    (void)uid;
+    (void)txQuality;
+    (void)rxQuality;
+  }
+
+  /**
+   * Occurs when intra request from remote user is received.
+   *
+   * This callback is triggered once remote user needs one Key frame.
+   *
+   */
+  virtual void onIntraRequestReceived() {}
+
+  /**
+   * Occurs when uplink network info is updated.
+   *
+   * The SDK triggers this callback when the uplink network information changes.
+   *
+   * @note This callback only applies to scenarios where you push externally encoded
+   * video data in H.264 format to the SDK.
+   *
+   * @param info The uplink network information. See UplinkNetworkInfo.
+   */
+  virtual void onUplinkNetworkInfoUpdated(const UplinkNetworkInfo& info) {
+    (void)info;
+  }
+
+  /**
+   * Occurs when downlink network info is updated.
+   *
+   * This callback is used for notifying user to switch major/minor stream if needed.
+   *
+   * @param info The downlink network info collections.
+   */
+  virtual void onDownlinkNetworkInfoUpdated(const DownlinkNetworkInfo& info) {
+    (void)info;
+  }
+
+  /**
+   * Reports the last-mile network quality of the local user.
+   *
+   * This callback reports the last-mile network conditions of the local user before the user joins
+   * the channel. Last mile refers to the connection between the local device and Agora's edge server.
+   *
+   * When the user is not in a channel and the last-mile network test is enabled
+   * (by calling `startLastmileProbeTest`), this callback function is triggered
+   * to update the app on the network connection quality of the local user.
+   *
+   * @param quality The last mile network quality. See #QUALITY_TYPE.
+   */
+  virtual void onLastmileQuality(int quality) { (void)quality; }
+
+  /** Occurs when the first local video frame is rendered on the local video view.
+   *
+   * @param source The video source: #VIDEO_SOURCE_TYPE.
+   * @param width The width (px) of the first local video frame.
+   * @param height The height (px) of the first local video frame.
+   * @param elapsed Time elapsed (ms) from the local user calling the `joinChannel`
+   * method until the SDK triggers this callback. If you call the `startPreview` method before calling
+   * the `joinChannel` method, then `elapsed` is the time elapsed from calling the
+   * `startPreview` method until the SDK triggers this callback.
+   */
+  virtual void onFirstLocalVideoFrame(VIDEO_SOURCE_TYPE source, int width, int height, int elapsed) {
+    (void)source;
+    (void)width;
+    (void)height;
+    (void)elapsed;
+  }
+
+  /** Occurs when the first local video frame is published.
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The local client enables the video module and calls `joinChannel` successfully.
+   * - The local client calls `muteLocalVideoStream(true)` and muteLocalVideoStream(false) in sequence.
+   * - The local client calls `disableVideo` and `enableVideo` in sequence.
+   * - The local client calls `pushVideoFrame` to successfully push the video frame to the SDK.
+   * @param source The video source type.
+   * @param elapsed The time elapsed (ms) from the local user calling joinChannel` to the SDK triggers
+   * this callback.
+  */
+  virtual void onFirstLocalVideoFramePublished(VIDEO_SOURCE_TYPE source, int elapsed) {
+    (void)source;
+    (void)elapsed;
+  }
+
+  /** Occurs when the first remote video frame is received and decoded.
+
+  The SDK triggers this callback under one of the following circumstances:
+  - The remote user joins the channel and sends the video stream.
+  - The remote user stops sending the video stream and re-sends it after 15 seconds. Reasons for such an interruption include:
+   - The remote user leaves the channel.
+   - The remote user drops offline.
+   - The remote user calls `muteLocalVideoStream` to stop sending the video stream.
+   - The remote user calls `disableVideo` to disable video.
+
+  @param uid The user ID of the remote user sending the video stream.
+  @param width The width (pixels) of the video stream.
+  @param height The height (pixels) of the video stream.
+  @param elapsed The time elapsed (ms) from the local user calling `joinChannel`
+  until the SDK triggers this callback.
+  */
+  virtual void onFirstRemoteVideoDecoded(uid_t uid, int width, int height, int elapsed) __deprecated {
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when the local or remote video size or rotation has changed.
+   * @param sourceType The video source type: #VIDEO_SOURCE_TYPE.
+   * @param uid The user ID. 0 indicates the local user.
+   * @param width The new width (pixels) of the video.
+   * @param height The new height (pixels) of the video.
+   * @param rotation The rotation information of the video.
+   */
+  virtual void onVideoSizeChanged(VIDEO_SOURCE_TYPE sourceType, uid_t uid, int width, int height, int rotation) {
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)rotation;
+  }  
+
+  /** Occurs when the local video stream state changes.
+   *
+   * When the state of the local video stream changes (including the state of the video capture and
+   * encoding), the SDK triggers this callback to report the current state. This callback indicates
+   * the state of the local video stream, including camera capturing and video encoding, and allows
+   * you to troubleshoot issues when exceptions occur.
+   *
+   * The SDK triggers the onLocalVideoStateChanged callback with the state code of `LOCAL_VIDEO_STREAM_STATE_FAILED`
+   * and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE` in the following situations:
+   * - The app switches to the background, and the system gets the camera resource.
+   * - The camera starts normally, but does not output video for four consecutive seconds.
+   *
+   * When the camera outputs the captured video frames, if the video frames are the same for 15
+   * consecutive frames, the SDK triggers the `onLocalVideoStateChanged` callback with the state code
+   * of `LOCAL_VIDEO_STREAM_STATE_CAPTURING` and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE`.
+   * Note that the video frame duplication detection is only available for video frames with a resolution
+   * greater than 200 × 200, a frame rate greater than or equal to 10 fps, and a bitrate less than 20 Kbps.
+   *
+   * @note For some device models, the SDK does not trigger this callback when the state of the local
+   * video changes while the local video capturing device is in use, so you have to make your own
+   * timeout judgment.
+   *
+   * @param source The video source type: #VIDEO_SOURCE_TYPE.
+   * @param state The state of the local video. See #LOCAL_VIDEO_STREAM_STATE.
+   * @param reason The detailed error information. See #LOCAL_VIDEO_STREAM_REASON.
+   */
+  virtual void onLocalVideoStateChanged(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_REASON reason) {
+    (void)source;
+    (void)state;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the remote video state changes.
+   *
+   * @note This callback does not work properly when the number of users (in the voice/video call
+   * channel) or hosts (in the live streaming channel) in the channel exceeds 17.
+   *
+   * @param uid The ID of the user whose video state has changed.
+   * @param state The remote video state: #REMOTE_VIDEO_STATE.
+   * @param reason The reason of the remote video state change: #REMOTE_VIDEO_STATE_REASON.
+   * @param elapsed The time elapsed (ms) from the local client calling `joinChannel` until this callback is triggered.
+   */
+  virtual void onRemoteVideoStateChanged(uid_t uid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed) {
+    (void)uid;
+    (void)state;
+    (void)reason;
+    (void)elapsed;
+  }
+
+  /** Occurs when the renderer receives the first frame of the remote video.
+   *
+   * @param uid The user ID of the remote user sending the video stream.
+   * @param width The width (px) of the video frame.
+   * @param height The height (px) of the video stream.
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` until the SDK triggers this callback.
+   */
+  virtual void onFirstRemoteVideoFrame(uid_t uid, int width, int height, int elapsed) {
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a remote user or broadcaster joins the channel.
+   *
+   * - In the COMMUNICATION channel profile, this callback indicates that a remote user joins the channel.
+   * The SDK also triggers this callback to report the existing users in the channel when a user joins the
+   * channel.
+   * In the LIVE_BROADCASTING channel profile, this callback indicates that a host joins the channel. The
+   * SDK also triggers this callback to report the existing hosts in the channel when a host joins the
+   * channel. Agora recommends limiting the number of hosts to 17.
+   *
+   * The SDK triggers this callback under one of the following circumstances:
+   * - A remote user/host joins the channel by calling the `joinChannel` method.
+   * - A remote user switches the user role to the host after joining the channel.
+   * - A remote user/host rejoins the channel after a network interruption.
+   *
+   * @param uid The ID of the remote user or broadcaster joining the channel.
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` or `setClientRole`
+   * until this callback is triggered.
+  */
+  virtual void onUserJoined(uid_t uid, int elapsed) {
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a remote user or broadcaster goes offline.
+   *
+   * There are two reasons for a user to go offline:
+   * - Leave the channel: When the user leaves the channel, the user sends a goodbye message. When this
+   * message is received, the SDK determines that the user leaves the channel.
+   * - Drop offline: When no data packet of the user is received for a certain period of time, the SDK assumes
+   * that the user drops offline. A poor network connection may lead to false detection, so we recommend using
+   * the RTM SDK for reliable offline detection.
+   * - The user switches the user role from a broadcaster to an audience.
+   *
+   * @param uid The ID of the remote user or broadcaster who leaves the channel or drops offline.
+   * @param reason The reason why the remote user goes offline: #USER_OFFLINE_REASON_TYPE.
+   */
+  virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) {
+    (void)uid;
+    (void)reason;
+  }
+
+  /** Occurs when a remote user's audio stream playback pauses/resumes.
+
+     The SDK triggers this callback when the remote user stops or resumes sending the audio stream by
+     calling the `muteLocalAudioStream` method.
+
+     @note This callback can be inaccurate when the number of users (in the `COMMUNICATION` profile) or hosts (in the `LIVE_BROADCASTING` profile) in the channel exceeds 17.
+
+     @param uid The user ID.
+     @param muted Whether the remote user's audio stream is muted/unmuted:
+     - true: Muted.
+     - false: Unmuted.
+     */
+  virtual void onUserMuteAudio(uid_t uid, bool muted) {
+    (void)uid;
+    (void)muted;
+  }
+
+  /** Occurs when a remote user pauses or resumes sending the video stream.
+   *
+   * When a remote user calls `muteLocalVideoStream` to stop or resume publishing the video stream, the
+   * SDK triggers this callback to report the state of the remote user's publishing stream to the local
+   * user.
+
+   @note This callback is invalid when the number of users or broadacasters in a
+   channel exceeds 20.
+
+   @param userId ID of the remote user.
+   @param muted Whether the remote user stops publishing the video stream:
+   - true: The remote user has paused sending the video stream.
+   - false: The remote user has resumed sending the video stream.
+   */
+  virtual void onUserMuteVideo(uid_t uid, bool muted) {
+    (void)uid;
+    (void)muted;
+  }
+
+  /** Occurs when a remote user enables or disables the video module.
+
+  Once the video function is disabled, the users cannot see any video.
+
+  The SDK triggers this callback when a remote user enables or disables the video module by calling the
+  `enableVideo` or `disableVideo` method.
+
+  @param uid The ID of the remote user.
+  @param enabled Whether the video of the remote user is enabled:
+  - true: The remote user has enabled video.
+  - false: The remote user has disabled video.
+  */
+  virtual void onUserEnableVideo(uid_t uid, bool enabled) {
+    (void)uid;
+    (void)enabled;
+  }
+
+  /**
+   * Occurs when the remote user audio or video state is updated.
+   * @param uid The uid of the remote user.
+   * @param state The remote user's audio or video state: #REMOTE_USER_STATE.
+   */
+  virtual void onUserStateChanged(uid_t uid, REMOTE_USER_STATE state) {
+    (void)uid;
+    (void)state;
+  }
+
+  /** Occurs when a remote user enables or disables local video capturing.
+
+  The SDK triggers this callback when the remote user resumes or stops capturing the video stream by
+  calling the `enableLocalVideo` method.
+
+  @param uid The ID of the remote user.
+  @param enabled Whether the specified remote user enables/disables local video:
+  - `true`: The remote user has enabled local video capturing.
+  - `false`: The remote user has disabled local video capturing.
+  */
+  virtual void onUserEnableLocalVideo(uid_t uid, bool enabled) __deprecated {
+    (void)uid;
+    (void)enabled;
+  }
+
+  /** Reports the statistics of the audio stream from each remote user/host.
+
+   The SDK triggers this callback once every two seconds for each remote user who is sending audio
+   streams. If a channel includes multiple remote users, the SDK triggers this callback as many times.
+
+   @param stats Statistics of the received remote audio streams. See RemoteAudioStats.
+   */
+  virtual void onRemoteAudioStats(const RemoteAudioStats& stats) {
+    (void)stats;
+  }
+
+  /** Reports the statistics of the local audio stream.
+   *
+   * The SDK triggers this callback once every two seconds.
+   *
+   * @param stats The statistics of the local audio stream.
+   * See LocalAudioStats.
+   */
+  virtual void onLocalAudioStats(const LocalAudioStats& stats) {
+    (void)stats;
+  }
+
+  /** Reports the statistics of the local video stream.
+   *
+   * The SDK triggers this callback once every two seconds for each
+   * user/host. If there are multiple users/hosts in the channel, the SDK
+   * triggers this callback as many times.
+   *
+   * @note If you have called the `enableDualStreamMode`
+   * method, this callback reports the statistics of the high-video
+   * stream (high bitrate, and high-resolution video stream).
+   *
+   * @param source The video source type. See #VIDEO_SOURCE_TYPE.
+   * @param stats Statistics of the local video stream. See LocalVideoStats.
+   */
+  virtual void onLocalVideoStats(VIDEO_SOURCE_TYPE source, const LocalVideoStats& stats) {
+    (void)source;
+    (void)stats;
+  }
+
+  /** Reports the statistics of the video stream from each remote user/host.
+   *
+   * The SDK triggers this callback once every two seconds for each remote user. If a channel has
+   * multiple users/hosts sending video streams, the SDK triggers this callback as many times.
+   *
+   * @param stats Statistics of the remote video stream. See
+   * RemoteVideoStats.
+   */
+  virtual void onRemoteVideoStats(const RemoteVideoStats& stats) {
+    (void)stats;
+  }
+
+  /**
+   * Occurs when the camera turns on and is ready to capture the video.
+   * @deprecated Use `LOCAL_VIDEO_STREAM_STATE_CAPTURING(1)` in onLocalVideoStateChanged instead.
+   * This callback indicates that the camera has been successfully turned on and you can start to capture video.
+   */
+  virtual void onCameraReady() __deprecated {}
+
+  /**
+   * Occurs when the camera focus area changes.
+   *
+   * @note This method is for Andriod and iOS only.
+   *
+   * @param x The x coordinate of the changed camera focus area.
+   * @param y The y coordinate of the changed camera focus area.
+   * @param width The width of the changed camera focus area.
+   * @param height The height of the changed camera focus area.
+   */
+  virtual void onCameraFocusAreaChanged(int x, int y, int width, int height) {
+    (void)x;
+    (void)y;
+    (void)width;
+    (void)height;
+  }
+  /**
+   * Occurs when the camera exposure area changes.
+   *
+   * @param x The x coordinate of the changed camera exposure area.
+   * @param y The y coordinate of the changed camera exposure area.
+   * @param width The width of the changed camera exposure area.
+   * @param height The height of the changed exposure area.
+   */
+  virtual void onCameraExposureAreaChanged(int x, int y, int width, int height) {
+    (void)x;
+    (void)y;
+    (void)width;
+    (void)height;
+  }
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  /**
+   * Reports the face detection result of the local user.
+   *
+   * Once you enable face detection by calling enableFaceDetection(true), you can get the following
+   * information on the local user in real-time:
+   * - The width and height of the local video.
+   * - The position of the human face in the local view.
+   * - The distance between the human face and the screen.
+   *
+   * This value is based on the fitting calculation of the local video size and the position of the human face.
+   *
+   * @note
+   * - This callback is for Android and iOS only.
+   * - When it is detected that the face in front of the camera disappears, the callback will be
+   * triggered immediately. In the state of no face, the trigger frequency of the callback will be
+   * reduced to save power consumption on the local device.
+   * - The SDK stops triggering this callback when a human face is in close proximity to the screen.
+   * On Android, the value of `distance` reported in this callback may be slightly different from the
+   * actual distance. Therefore, Agora does not recommend using it for accurate calculation.
+   *
+   * @param imageWidth The width (px) of the video image captured by the local camera.
+   * @param imageHeight The height (px) of the video image captured by the local camera.
+   * @param vecRectangle A Rectangle array of length 'numFaces', which represents the position and size of the human face on the local video:
+   * - x: The x-coordinate (px) of the human face in the local view. Taking the top left corner of the view as the origin, the x-coordinate represents the horizontal position of the human face relative to the origin.
+   * - y: The y-coordinate (px) of the human face in the local view. Taking the top left corner of the view as the origin, the y-coordinate represents the vertical position of the human face relative to the origin.
+   * - width: The width (px) of the human face in the captured view.
+   * - height: The height (px) of the human face in the captured view.
+   * @param vecDistance An int array of length 'numFaces', which represents distance (cm) between the human face and the screen.
+   * @param numFaces The number of faces detected. If the value is 0, it means that no human face is detected.
+   */
+  virtual void onFacePositionChanged(int imageWidth, int imageHeight,
+                                     const Rectangle* vecRectangle, const int* vecDistance,
+                                     int numFaces) {
+    (void) imageWidth;
+    (void) imageHeight;
+    (void) vecRectangle;
+    (void) vecDistance;
+    (void) numFaces;
+  }
+#endif
+  /**
+   * Occurs when the video stops playing.
+   * @deprecated Use `LOCAL_VIDEO_STREAM_STATE_STOPPED(0)` in the onLocalVideoStateChanged callback instead.
+   *
+   * The app can use this callback to change the configuration of the view (for example, displaying
+   * other pictures in the view) after the video stops playing.
+   */
+  virtual void onVideoStopped() __deprecated {}
+
+  /** Occurs when the playback state of the music file changes.
+   *
+   * This callback occurs when the playback state of the music file changes, and reports the current state and error code.
+
+   @param state The playback state of the music file. See #AUDIO_MIXING_STATE_TYPE.
+   @param reason The reason for the change of the music file playback state. See #AUDIO_MIXING_REASON_TYPE.
+   */
+  virtual void onAudioMixingStateChanged(AUDIO_MIXING_STATE_TYPE state, AUDIO_MIXING_REASON_TYPE reason) {
+    (void)state;
+    (void)reason;
+  }
+
+  /** Occurs when the state of the rhythm player changes.
+   When you call the \ref IRtcEngine::startRhythmPlayer "startRhythmPlayer"
+   method and the state of rhythm player changes, the SDK triggers this
+   callback.
+
+   @param state The state code. See #RHYTHM_PLAYER_STATE_TYPE.
+   @param reason The error code. See #RHYTHM_PLAYER_REASON.
+   */
+  virtual void onRhythmPlayerStateChanged(RHYTHM_PLAYER_STATE_TYPE state, RHYTHM_PLAYER_REASON reason) {
+    (void)state;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the SDK cannot reconnect to the server 10 seconds after its connection to the server is
+   * interrupted.
+   *
+   * The SDK triggers this callback when it cannot connect to the server 10 seconds after calling
+   * `joinChannel`, regardless of whether it is in the channel or not. If the SDK fails to rejoin
+   * the channel 20 minutes after being disconnected from Agora's edge server, the SDK stops rejoining the channel.
+   */
+  virtual void onConnectionLost() {}
+
+  /** Occurs when the connection between the SDK and the server is interrupted.
+   * @deprecated Use `onConnectionStateChanged` instead.
+
+  The SDK triggers this callback when it loses connection with the serer for more
+  than 4 seconds after the connection is established. After triggering this
+  callback, the SDK tries to reconnect to the server. If the reconnection fails
+  within a certain period (10 seconds by default), the onConnectionLost()
+  callback is triggered. If the SDK fails to rejoin the channel 20 minutes after
+  being disconnected from Agora's edge server, the SDK stops rejoining the channel.
+
+  */
+  virtual void onConnectionInterrupted() __deprecated {}
+
+  /** Occurs when your connection is banned by the Agora Server.
+   * @deprecated Use `onConnectionStateChanged` instead.
+   */
+  virtual void onConnectionBanned() __deprecated {}
+
+  /** Occurs when the local user receives the data stream from the remote user.
+   *
+   * The SDK triggers this callback when the user receives the data stream that another user sends
+   * by calling the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
+   *
+   * @param uid ID of the user who sends the data stream.
+   * @param streamId The ID of the stream data.
+   * @param data The data stream.
+   * @param length The length (byte) of the data stream.
+   * @param sentTs The time when the data stream sent.
+   */
+  virtual void onStreamMessage(uid_t uid, int streamId, const char* data, size_t length, uint64_t sentTs) {
+    (void)uid;
+    (void)streamId;
+    (void)data;
+    (void)length;
+    (void)sentTs;
+  }
+
+  /** Occurs when the local user does not receive the data stream from the remote user.
+   *
+   * The SDK triggers this callback when the user fails to receive the data stream that another user sends
+   * by calling the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
+   *
+   * @param uid ID of the user who sends the data stream.
+   * @param streamId The ID of the stream data.
+   * @param code The error code.
+   * @param missed The number of lost messages.
+   * @param cached The number of incoming cached messages when the data stream is
+   * interrupted.
+   */
+  virtual void onStreamMessageError(uid_t uid, int streamId, int code, int missed, int cached) {
+    (void)uid;
+    (void)streamId;
+    (void)code;
+    (void)missed;
+    (void)cached;
+  }
+
+  /**
+   * Occurs when the token expires.
+   *
+   * When the token expires during a call, the SDK triggers this callback to remind the app to renew the token.
+   *
+   * Upon receiving this callback, generate a new token at your app server and call
+   * `joinChannel` to pass the new token to the SDK.
+   *
+   */
+  virtual void onRequestToken() {}
+
+  /**
+   * Occurs when the token will expire in 30 seconds.
+   *
+   * When the token is about to expire in 30 seconds, the SDK triggers this callback to remind the app to renew the token.
+
+   * Upon receiving this callback, generate a new token at your app server and call
+   * \ref IRtcEngine::renewToken "renewToken" to pass the new Token to the SDK.
+   *
+   *
+   * @param token The token that will expire in 30 seconds.
+   */
+  virtual void onTokenPrivilegeWillExpire(const char* token) {
+    (void)token;
+  }
+
+  /**
+   * Occurs when connection license verification fails.
+   *
+   * You can know the reason accordding to error code
+   */
+  virtual void onLicenseValidationFailure(LICENSE_ERROR_TYPE error) {
+    (void)error;
+  }
+
+  /** Occurs when the first local audio frame is published.
+   *
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The local client enables the audio module and calls `joinChannel` successfully.
+   * - The local client calls `muteLocalAudioStream(true)` and `muteLocalAudioStream(false)` in sequence.
+   * - The local client calls `disableAudio` and `enableAudio` in sequence.
+   * - The local client calls `pushAudioFrame` to successfully push the audio frame to the SDK.
+   *
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` to the SDK triggers this callback.
+   */
+  virtual void onFirstLocalAudioFramePublished(int elapsed) {
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when the SDK decodes the first remote audio frame for playback.
+   *
+   * @deprecated Use `onRemoteAudioStateChanged` instead.
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The remote user joins the channel and sends the audio stream for the first time.
+   * - The remote user's audio is offline and then goes online to re-send audio. It means the local user cannot
+   * receive audio in 15 seconds. Reasons for such an interruption include:
+   *   - The remote user leaves channel.
+   *   - The remote user drops offline.
+   *   - The remote user calls muteLocalAudioStream to stop sending the audio stream.
+   *   - The remote user calls disableAudio to disable audio.
+   * @param uid User ID of the remote user sending the audio stream.
+   * @param elapsed The time elapsed (ms) from the loca user calling `joinChannel`
+   * until this callback is triggered.
+   */
+  virtual void onFirstRemoteAudioDecoded(uid_t uid, int elapsed) __deprecated {
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /** Occurs when the SDK receives the first audio frame from a specific remote user.
+   * @deprecated Use `onRemoteAudioStateChanged` instead.
+   *
+   * @param uid ID of the remote user.
+   * @param elapsed The time elapsed (ms) from the loca user calling `joinChannel`
+   * until this callback is triggered.
+   */
+  virtual void onFirstRemoteAudioFrame(uid_t uid, int elapsed) __deprecated {
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /** Occurs when the local audio state changes.
+   *
+   * When the state of the local audio stream changes (including the state of the audio capture and encoding), the SDK
+   * triggers this callback to report the current state. This callback indicates the state of the local audio stream,
+   * and allows you to troubleshoot issues when audio exceptions occur.
+   *
+   * @note
+   * When the state is `LOCAL_AUDIO_STREAM_STATE_FAILED(3)`, see the `error`
+   * parameter for details.
+   *
+   * @param state State of the local audio. See #LOCAL_AUDIO_STREAM_STATE.
+   * @param reason The reason information of the local audio.
+   * See #LOCAL_AUDIO_STREAM_REASON.
+   */
+  virtual void onLocalAudioStateChanged(LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_REASON reason) {
+    (void)state;
+    (void)reason;
+  }
+
+  /** Occurs when the remote audio state changes.
+   *
+   * When the audio state of a remote user (in the voice/video call channel) or host (in the live streaming channel)
+   * changes, the SDK triggers this callback to report the current state of the remote audio stream.
+   *
+   * @note This callback does not work properly when the number of users (in the voice/video call channel) or hosts
+   * (in the live streaming channel) in the channel exceeds 17.
+   *
+   * @param uid ID of the remote user whose audio state changes.
+   * @param state State of the remote audio. See #REMOTE_AUDIO_STATE.
+   * @param reason The reason of the remote audio state change.
+   * See #REMOTE_AUDIO_STATE_REASON.
+   * @param elapsed Time elapsed (ms) from the local user calling the
+   * `joinChannel` method until the SDK
+   * triggers this callback.
+   */
+  virtual void onRemoteAudioStateChanged(uid_t uid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed) {
+    (void)uid;
+    (void)state;
+    (void)reason;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when an active speaker is detected.
+   *
+   * After a successful call of `enableAudioVolumeIndication`, the SDK continuously detects which remote user has the
+   * loudest volume. During the current period, the remote user, who is detected as the loudest for the most times,
+   * is the most active user.
+   *
+   * When the number of users is no less than two and an active remote speaker exists, the SDK triggers this callback and reports the uid of the most active remote speaker.
+   * - If the most active remote speaker is always the same user, the SDK triggers the `onActiveSpeaker` callback only once.
+   * - If the most active remote speaker changes to another user, the SDK triggers this callback again and reports the uid of the new active remote speaker.
+   *
+   * @param userId The ID of the active speaker. A `uid` of 0 means the local user.
+   */
+  virtual void onActiveSpeaker(uid_t uid) { 
+    (void)uid;
+  }
+
+  /** Reports result of content inspection.
+   *
+   * @param result The result of content inspection: #CONTENT_INSPECT_RESULT.
+   */
+  virtual void onContentInspectResult(media::CONTENT_INSPECT_RESULT result) { (void)result; }
+
+  /** Reports the result of taking a video snapshot.
+   *
+   * After a successful `takeSnapshot` method call, the SDK triggers this callback to report whether the snapshot is
+   * successfully taken, as well as the details for that snapshot.
+   *
+   * @param uid The user ID. A `uid` of 0 indicates the local user.
+   * @param filePath The local path of the snapshot.
+   * @param width The width (px) of the snapshot.
+   * @param height The height (px) of the snapshot.
+   * @param errCode The message that confirms success or gives the reason why the snapshot is not successfully taken:
+   * - 0: Success.
+   * - &lt; 0: Failure.
+   *   - -1: The SDK fails to write data to a file or encode a JPEG image.
+   *   - -2: The SDK does not find the video stream of the specified user within one second after the `takeSnapshot` method call succeeds.
+   *   - -3: Calling the `takeSnapshot` method too frequently. Call the `takeSnapshot` method after receiving the `onSnapshotTaken`
+   * callback from the previous call.
+   */
+  virtual void onSnapshotTaken(uid_t uid, const char* filePath, int width, int height, int errCode) {
+    (void)uid;
+    (void)filePath;
+    (void)width;
+    (void)height;
+    (void)errCode;
+  }
+
+  /**
+   * Occurs when the user role switches in the interactive live streaming.
+   *
+   * @param oldRole The old role of the user: #CLIENT_ROLE_TYPE.
+   * @param newRole The new role of the user: #CLIENT_ROLE_TYPE.
+   * @param newRoleOptions The client role options of the new role: #ClientRoleOptions.
+   */
+  virtual void onClientRoleChanged(CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole, const ClientRoleOptions& newRoleOptions) {
+    (void)oldRole;
+    (void)newRole;
+    (void)newRoleOptions;
+  }
+
+  /**
+   * Occurs when the user role in a Live-Broadcast channel fails to switch, for example, from a broadcaster
+   * to an audience or vice versa.
+   *
+   * @param reason The reason for failing to change the client role: #CLIENT_ROLE_CHANGE_FAILED_REASON.
+   * @param currentRole The current role of the user: #CLIENT_ROLE_TYPE.
+   */
+  virtual void onClientRoleChangeFailed(CLIENT_ROLE_CHANGE_FAILED_REASON reason, CLIENT_ROLE_TYPE currentRole) {
+    (void)reason;
+    (void)currentRole;
+  }
+
+  /** Occurs when the audio device volume changes.
+   @param deviceType The device type, see #MEDIA_DEVICE_TYPE
+   @param volume The volume of the audio device.
+   @param muted Whether the audio device is muted:
+   - true: The audio device is muted.
+   - false: The audio device is not muted.
+   */
+  virtual void onAudioDeviceVolumeChanged(MEDIA_DEVICE_TYPE deviceType, int volume, bool muted) {
+    (void)deviceType;
+    (void)volume;
+    (void)muted;
+  }
+
+  /**
+   * Occurs when the state of the RTMP streaming changes.
+   *
+   * When the media push state changes, the SDK triggers this callback and reports the URL address and the current state
+   * of the media push. This callback indicates the state of the media push. When exceptions occur, you can troubleshoot
+   * issues by referring to the detailed error descriptions in the error code.
+   *
+   * @param url The URL address where the state of the media push changes.
+   * @param state The current state of the media push: #RTMP_STREAM_PUBLISH_STATE.
+   * @param reason The detailed error information for the media push: #RTMP_STREAM_PUBLISH_REASON.
+   */
+  virtual void onRtmpStreamingStateChanged(const char* url, RTMP_STREAM_PUBLISH_STATE state,
+                                           RTMP_STREAM_PUBLISH_REASON reason) {
+    (void)url;
+    (void)state;
+    (void)reason;
+  }
+
+  /** Reports events during the media push.
+   *
+   * @param url The URL for media push.
+   * @param eventCode The event code of media push. See RTMP_STREAMING_EVENT for details.
+   */
+  virtual void onRtmpStreamingEvent(const char* url, RTMP_STREAMING_EVENT eventCode) {
+    (void)url;
+    (void)eventCode;
+  }
+
+  /**
+   * Occurs when the publisher's transcoding settings are updated.
+   *
+   * When the `LiveTranscoding` class in \ref IRtcEngine::setLiveTranscoding "setLiveTranscoding"
+   * updates, the SDK triggers this callback to report the update information.
+   *
+   * @note
+   * If you call the `setLiveTranscoding` method to set the `LiveTranscoding` class for the first time, the SDK
+   * does not trigger this callback.
+   */
+  virtual void onTranscodingUpdated() {}
+
+  /** Occurs when the local audio route changes (for Android, iOS, and macOS only).
+
+   The SDK triggers this callback when the local audio route switches to an
+   earpiece, speakerphone, headset, or Bluetooth device.
+   @param routing The current audio output routing:
+   - -1: Default.
+   - 0: Headset.
+   - 1: Earpiece.
+   - 2: Headset with no microphone.
+   - 3: Speakerphone.
+   - 4: Loudspeaker.
+   - 5: Bluetooth headset.
+   */
+  virtual void onAudioRoutingChanged(int routing) { (void)routing; }
+
+  /**
+   * Occurs when the state of the media stream relay changes.
+   *
+   * The SDK reports the state of the current media relay and possible error messages in this
+   * callback.
+   *
+   * @param state The state code:
+   * - `RELAY_STATE_IDLE(0)`: The SDK is initializing.
+   * - `RELAY_STATE_CONNECTING(1)`: The SDK tries to relay the media stream to the destination
+   * channel.
+   * - `RELAY_STATE_RUNNING(2)`: The SDK successfully relays the media stream to the destination
+   * channel.
+   * - `RELAY_STATE_FAILURE(3)`: A failure occurs. See the details in `code`.
+   * @param code The error code:
+   * - `RELAY_OK(0)`: The state is normal.
+   * - `RELAY_ERROR_SERVER_ERROR_RESPONSE(1)`: An error occurs in the server response.
+   * - `RELAY_ERROR_SERVER_NO_RESPONSE(2)`: No server response. You can call the leaveChannel method
+   * to leave the channel.
+   * - `RELAY_ERROR_NO_RESOURCE_AVAILABLE(3)`: The SDK fails to access the service, probably due to
+   * limited resources of the server.
+   * - `RELAY_ERROR_FAILED_JOIN_SRC(4)`: Fails to send the relay request.
+   * - `RELAY_ERROR_FAILED_JOIN_DEST(5)`: Fails to accept the relay request.
+   * - `RELAY_ERROR_FAILED_PACKET_RECEIVED_FROM_SRC(6)`: The server fails to receive the media
+   * stream.
+   * - `RELAY_ERROR_FAILED_PACKET_SENT_TO_DEST(7)`: The server fails to send the media stream.
+   * - `RELAY_ERROR_SERVER_CONNECTION_LOST(8)`: The SDK disconnects from the server due to poor
+   * network connections. You can call the leaveChannel method to leave the channel.
+   * - `RELAY_ERROR_INTERNAL_ERROR(9)`: An internal error occurs in the server.
+   * - `RELAY_ERROR_SRC_TOKEN_EXPIRED(10)`: The token of the source channel has expired.
+   * - `RELAY_ERROR_DEST_TOKEN_EXPIRED(11)`: The token of the destination channel has expired.
+   */
+  virtual void onChannelMediaRelayStateChanged(int state, int code) {
+    (void)state;
+    (void)code;
+  }
+
+  /**
+   * Occurs when the published media stream falls back to an audio-only stream due to poor network conditions or
+   * switches back to video stream after the network conditions improve.
+   *
+   * If you call `setLocalPublishFallbackOption` and set `option` as `STREAM_FALLBACK_OPTION_AUDIO_ONLY(2)`, this
+   * callback is triggered when the locally published stream falls back to audio-only mode due to poor uplink
+   * conditions, or when the audio stream switches back to the video after the uplink network condition improves.
+   * Once the published stream falls back to audio only, the remote app receives the `onRemoteVideoStateChanged` callback.
+   *
+   * @param isFallbackOrRecover Whether the published stream fell back to audio-only or switched back to the video:
+   * - `true`: The published stream fell back to audio-only due to poor network conditions.
+   * - `false`: The published stream switched back to the video after the network conditions improved.
+   */
+  virtual void onLocalPublishFallbackToAudioOnly(bool isFallbackOrRecover) {
+    (void)isFallbackOrRecover;
+  }
+
+  /**
+   * Occurs when the remote media stream falls back to audio-only stream due to poor network conditions or
+   * switches back to video stream after the network conditions improve.
+   *
+   * If you call `setRemoteSubscribeFallbackOption` and set `option` as `STREAM_FALLBACK_OPTION_AUDIO_ONLY(2)`, this
+   * callback is triggered when the remotely subscribed media stream falls back to audio-only mode due to poor downlink
+   * conditions, or when the remotely subscribed media stream switches back to the video after the downlink network
+   * condition improves.
+   *
+   * @note Once the remote media stream is switched to the low stream due to poor network conditions, you can monitor
+   * the stream switch between a high and low stream in the `onRemoteVideoStats` callback.
+   *
+   * @param uid ID of the remote user sending the stream.
+   * @param isFallbackOrRecover Whether the remote media stream fell back to audio-only or switched back to the video:
+   * - `true`: The remote media stream fell back to audio-only due to poor network conditions.
+   * - `false`: The remote media stream switched back to the video stream after the network conditions improved.
+   */
+  virtual void onRemoteSubscribeFallbackToAudioOnly(uid_t uid, bool isFallbackOrRecover) {
+    (void)uid;
+    (void)isFallbackOrRecover;
+  }
+
+  /** Reports the transport-layer statistics of each remote audio stream.
+   * @deprecated Use `onRemoteAudioStats` instead.
+
+  This callback reports the transport-layer statistics, such as the packet loss rate and network time delay, once every
+  two seconds after the local user receives an audio packet from a remote user. During a call, when the user receives
+  the audio packet sent by the remote user/host, the callback is triggered every 2 seconds.
+
+  @param uid ID of the remote user whose audio data packet is received.
+  @param delay The network time delay (ms) from the sender to the receiver.
+  @param lost The Packet loss rate (%) of the audio packet sent from the remote
+  user.
+  @param rxKBitRate Received bitrate (Kbps) of the audio packet sent from the
+  remote user.
+  */
+  virtual void onRemoteAudioTransportStats(uid_t uid, unsigned short delay, unsigned short lost, unsigned short rxKBitRate) __deprecated {
+    (void)uid;
+    (void)delay;
+    (void)lost;
+    (void)rxKBitRate;
+  }
+
+  /** Reports the transport-layer statistics of each remote video stream.
+   * @deprecated Use `onRemoteVideoStats` instead.
+
+  This callback reports the transport-layer statistics, such as the packet loss rate and network time
+  delay, once every two seconds after the local user receives a video packet from a remote user.
+
+  During a call, when the user receives the video packet sent by the remote user/host, the callback is
+  triggered every 2 seconds.
+
+  @param uid ID of the remote user whose video packet is received.
+  @param delay The network time delay (ms) from the remote user sending the
+  video packet to the local user.
+  @param lost The packet loss rate (%) of the video packet sent from the remote
+  user.
+  @param rxKBitRate The bitrate (Kbps) of the video packet sent from
+  the remote user.
+  */
+  virtual void onRemoteVideoTransportStats(uid_t uid, unsigned short delay, unsigned short lost, unsigned short rxKBitRate) __deprecated {
+    (void)uid;
+    (void)delay;
+    (void)lost;
+    (void)rxKBitRate;
+  }
+
+  /** Occurs when the network connection state changes.
+   *
+   * When the network connection state changes, the SDK triggers this callback and reports the current
+   * connection state and the reason for the change.
+
+  @param state The current connection state. See #CONNECTION_STATE_TYPE.
+  @param reason The reason for a connection state change. See #CONNECTION_CHANGED_REASON_TYPE.
+   */
+  virtual void onConnectionStateChanged(
+      CONNECTION_STATE_TYPE state, CONNECTION_CHANGED_REASON_TYPE reason) {
+    (void)state;
+    (void)reason;
+  }
+
+  /** Occurs when the WIFI message need be sent to the user.
+   *
+   * @param reason The reason of notifying the user of a message.
+   * @param action Suggest an action for the user.
+   * @param wlAccMsg The message content of notifying the user.
+   */
+  virtual void onWlAccMessage(WLACC_MESSAGE_REASON reason, WLACC_SUGGEST_ACTION action, const char* wlAccMsg) {
+    (void)reason;
+    (void)action;
+    (void)wlAccMsg;
+  }
+
+  /** Occurs when SDK statistics wifi acceleration optimization effect.
+   *
+   * @param currentStats Instantaneous value of optimization effect.
+   * @param averageStats Average value of cumulative optimization effect.
+   */
+  virtual void onWlAccStats(const WlAccStats& currentStats, const WlAccStats& averageStats) {
+    (void)currentStats;
+    (void)averageStats;
+  }
+
+  /** Occurs when the local network type changes.
+   *
+   * This callback occurs when the connection state of the local user changes. You can get the
+   * connection state and reason for the state change in this callback. When the network connection
+   * is interrupted, this callback indicates whether the interruption is caused by a network type
+   * change or poor network conditions.
+
+  @param type The type of the local network connection. See #NETWORK_TYPE.
+   */
+  virtual void onNetworkTypeChanged(NETWORK_TYPE type) {
+    (void)type;
+  }
+
+  /** Reports the built-in encryption errors.
+   *
+   * When encryption is enabled by calling `enableEncryption`, the SDK triggers this callback if an
+   * error occurs in encryption or decryption on the sender or the receiver side.
+
+  @param errorType The error type. See #ENCRYPTION_ERROR_TYPE.
+   */
+  virtual void onEncryptionError(ENCRYPTION_ERROR_TYPE errorType) {
+    (void)errorType;
+  }
+
+  /** Occurs when the SDK cannot get the device permission.
+   *
+   * When the SDK fails to get the device permission, the SDK triggers this callback to report which
+   * device permission cannot be got.
+   *
+   * @note This method is for Android and iOS only.
+
+  @param permissionType The type of the device permission. See #PERMISSION_TYPE.
+  */
+  virtual void onPermissionError(PERMISSION_TYPE permissionType) {
+    (void)permissionType;
+  }
+
+  /** Occurs when the local user registers a user account.
+   *
+   * After the local user successfully calls `registerLocalUserAccount` to register the user account
+   * or calls `joinChannelWithUserAccount` to join a channel, the SDK triggers the callback and
+   * informs the local user's UID and User Account.
+
+    @param uid The ID of the local user.
+    @param userAccount The user account of the local user.
+   */
+  virtual void onLocalUserRegistered(uid_t uid, const char* userAccount) {
+    (void)uid;
+    (void)userAccount;
+  }
+
+  /** Occurs when the SDK gets the user ID and user account of the remote user.
+
+    After a remote user joins the channel, the SDK gets the UID and user account of the remote user,
+    caches them in a mapping table object (`userInfo`), and triggers this callback on the local client.
+
+    @param uid The ID of the remote user.
+    @param info The `UserInfo` object that contains the user ID and user account of the remote user.
+    */
+  virtual void onUserInfoUpdated(uid_t uid, const UserInfo& info) {
+    (void)uid;
+    (void)info;
+  }
+
+  /**
+   * Occurs when the user account is updated.
+   *
+   * @param uid The user ID.
+   * @param userAccount The user account.
+   */
+  virtual void onUserAccountUpdated(uid_t uid, const char* userAccount){
+    (void)uid;
+    (void)userAccount;
+  }
+
+  /**
+   * Reports the tracing result of video rendering event of the user.
+   * 
+   * @param uid The user ID.
+   * @param currentEvent The current event of the tracing result: #MEDIA_TRACE_EVENT.
+   * @param tracingInfo The tracing result: #VideoRenderingTracingInfo.
+   */
+  virtual void onVideoRenderingTracingResult(uid_t uid, MEDIA_TRACE_EVENT currentEvent, VideoRenderingTracingInfo tracingInfo) {
+    (void)uid;
+    (void)currentEvent;
+    (void)tracingInfo;
+  }
+
+  /**
+   * Occurs when local video transcoder stream has an error.
+   *
+   * @param stream Stream type of TranscodingVideoStream.
+   * @param error Error code of VIDEO_TRANSCODER_ERROR.
+   */
+  virtual void onLocalVideoTranscoderError(const TranscodingVideoStream& stream, VIDEO_TRANSCODER_ERROR error){
+    (void)stream;
+    (void)error;
+  }
+
+  /**
+   * Reports the user log upload result
+   * @param requestId RequestId of the upload
+   * @param success Is upload success
+   * @param reason Reason of the upload, 0: OK, 1 Network Error, 2 Server Error.
+   */
+  virtual void onUploadLogResult(const char* requestId, bool success, UPLOAD_ERROR_REASON reason) {
+    (void)requestId;
+    (void)success;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the audio subscribing state changes.
+   *
+   * @param channel The name of the channel.
+   * @param uid The ID of the remote user.
+   * @param oldState The previous subscribing status: #STREAM_SUBSCRIBE_STATE.
+   * @param newState The current subscribing status: #STREAM_SUBSCRIBE_STATE.
+   * @param elapseSinceLastState The time elapsed (ms) from the previous state to the current state.
+   */
+  virtual void onAudioSubscribeStateChanged(const char* channel, uid_t uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState) {
+    (void)channel;
+    (void)uid;
+    (void)oldState;
+    (void)newState;
+    (void)elapseSinceLastState;
+  }
+
+  /**
+   * Occurs when the video subscribing state changes.
+   *
+   * @param channel The name of the channel.
+   * @param uid The ID of the remote user.
+   * @param oldState The previous subscribing status: #STREAM_SUBSCRIBE_STATE.
+   * @param newState The current subscribing status: #STREAM_SUBSCRIBE_STATE.
+   * @param elapseSinceLastState The time elapsed (ms) from the previous state to the current state.
+   */
+  virtual void onVideoSubscribeStateChanged(const char* channel, uid_t uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState) {
+    (void)channel;
+    (void)uid;
+    (void)oldState;
+    (void)newState;
+    (void)elapseSinceLastState;
+  }
+
+  /**
+   * Occurs when the audio publishing state changes.
+   *
+   * @param channel The name of the channel.
+   * @param oldState The previous publishing state: #STREAM_PUBLISH_STATE.
+   * @param newState The current publishing state: #STREAM_PUBLISH_STATE.
+   * @param elapseSinceLastState The time elapsed (ms) from the previous state to the current state.
+   */
+  virtual void onAudioPublishStateChanged(const char* channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState) {
+    (void)channel;
+    (void)oldState;
+    (void)newState;
+    (void)elapseSinceLastState;
+  }
+
+  /**
+   * Occurs when the video publishing state changes.
+   *
+   * @param source The video source type.
+   * @param channel The name of the channel.
+   * @param oldState The previous publishing state: #STREAM_PUBLISH_STATE.
+   * @param newState The current publishing state: #STREAM_PUBLISH_STATE.
+   * @param elapseSinceLastState The time elapsed (ms) from the previous state to the current state.
+   */
+  virtual void onVideoPublishStateChanged(VIDEO_SOURCE_TYPE source, const char* channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState) {
+    (void)source;
+    (void)channel;
+    (void)oldState;
+    (void)newState;
+    (void)elapseSinceLastState;
+  }
+
+  /**
+   * Occurs when receive a video transcoder stream which has video layout info.
+   *
+   * @param uid user id of the transcoded stream.
+   * @param width width of the transcoded stream.
+   * @param height height of the transcoded stream.
+   * @param layoutCount count of layout info in the transcoded stream.
+   * @param layoutlist video layout info list of the transcoded stream.
+   */
+  virtual void onTranscodedStreamLayoutInfo(uid_t uid, int width, int height, int layoutCount,const VideoLayout* layoutlist) {
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)layoutCount;
+    (void)layoutlist;
+  }
+
+  /**
+   * Occurs when the SDK receives audio metadata.
+   * @since v4.3.1
+   * @param uid ID of the remote user.
+   * @param metadata The pointer of metadata
+   * @param length Size of metadata
+   * @technical preview 
+   */
+  virtual void onAudioMetadataReceived(uid_t uid, const char* metadata, size_t length) {
+    (void)uid;
+    (void)metadata;
+    (void)length;
+  }
+
+  /**
+   * The event callback of the extension.
+   *
+   * To listen for events while the extension is running, you need to register this callback.
+   *
+   * @param provider The name of the extension provider.
+   * @param extension The name of the extension.
+   * @param key The key of the extension.
+   * @param value The value of the extension key.
+   */
+  virtual void onExtensionEvent(const char* provider, const char* extension, const char* key, const char* value) {
+    (void)provider;
+    (void)extension;
+    (void)key;
+    (void)value;
+  }
+
+  /**
+   * Occurs when the extension is enabled.
+   *
+   * After a successful call of `enableExtension(true)`, the extension triggers this callback.
+   *
+   * @param provider The name of the extension provider.
+   * @param extension The name of the extension.
+   */
+  virtual void onExtensionStarted(const char* provider, const char* extension) {
+    (void)provider;
+    (void)extension;
+  }
+
+  /**
+   * Occurs when the extension is disabled.
+   *
+   * After a successful call of `enableExtension(false)`, the extension triggers this callback.
+   *
+   * @param provider The name of the extension provider.
+   * @param extension The name of the extension.
+   */
+  virtual void onExtensionStopped(const char* provider, const char* extension) {
+    (void)provider;
+    (void)extension;
+  }
+
+  /**
+   * Occurs when the extension runs incorrectly.
+   *
+   * When calling `enableExtension(true)` fails or the extension runs in error, the extension triggers
+   * this callback and reports the error code and reason.
+   *
+   * @param provider The name of the extension provider.
+   * @param extension The name of the extension.
+   * @param error The error code. For details, see the extension documentation provided by the extension provider.
+   * @param message The error message. For details, see the extension documentation provided by the extension provider.
+   */
+  virtual void onExtensionError(const char* provider, const char* extension, int error, const char* message) {
+    (void)provider;
+    (void)extension;
+    (void)error;
+    (void)message;
+  }
+
+  /**
+   * Occurs when the SDK receives RTM setting change response.
+   *
+   * @technical preview
+   * @param code The error code.
+   */
+  virtual void onSetRtmFlagResult(int code) {
+    (void)code;
+  }
+};
+
+/**
+ * The IVideoDeviceCollection class. You can get information related to video devices through this interface.
+ */
+class IVideoDeviceCollection {
+ public:
+  virtual ~IVideoDeviceCollection() {}
+
+  /**
+   * Gets the total number of the indexed video capture devices in the system.
+   *
+   * @return The total number of the indexed video capture devices.
+   */
+  virtual int getCount() = 0;
+
+  /**
+   * Specifies a device with the device ID.
+   *
+   * @param deviceIdUTF8 The device ID. The maximum length is #MAX_DEVICE_ID_LENGTH_TYPE. Plugging or
+   * unplugging the audio device does not change the value of deviceId.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDevice(const char deviceIdUTF8[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets a specified piece of information about an indexed video device.
+   *
+   * @param index The index value of the video device. The value of this parameter must be less than the value returned in `getCount`.
+   * @param deviceNameUTF8 The name of the device. The maximum length is #MAX_DEVICE_ID_LENGTH.
+   * @param deviceIdUTF8 The device ID of the video device. The maximum length is #MAX_DEVICE_ID_LENGTH.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDevice(int index, char deviceNameUTF8[MAX_DEVICE_ID_LENGTH],
+                        char deviceIdUTF8[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Releases all the resources occupied by the IVideoDeviceCollection object.
+   */
+  virtual void release() = 0;
+};
+
+/**
+ * The IVideoDeviceManager class.
+ */
+class IVideoDeviceManager {
+ public:
+  virtual ~IVideoDeviceManager() {}
+  /**
+   * Enumerates the video devices.
+   *
+   * This method returns an `IVideoDeviceCollection` object including all video devices in the system.
+   * With the `IVideoDeviceCollection` object, the application can enumerate video devices. The
+   * application must call the release method to release the returned object after using it.
+   *
+   * @return
+   * - Success: An `IVideoDeviceCollection` object including all video devices in the system.
+   * - Failure: NULL.
+   */
+  virtual IVideoDeviceCollection* enumerateVideoDevices() = 0;
+
+  /**
+   * Specifies the video capture device with the device ID.
+   *
+   * @param deviceIdUTF8 he device ID. You can get the device ID by calling `enumerateVideoDevices`.
+   * The maximum length is #MAX_DEVICE_ID_LENGTH.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDevice(const char deviceIdUTF8[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Retrieves the current video capture device.
+   * @param deviceIdUTF8 Output parameter. The device ID. The maximum length is #MAX_DEVICE_ID_LENGTH_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDevice(char deviceIdUTF8[MAX_DEVICE_ID_LENGTH]) = 0;
+
+#if defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) || \
+    (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE)
+  /**
+   * Gets the number of video formats supported by the specified video capture device.
+   *
+   * Video capture devices may support multiple video formats, and each format supports different
+   * combinations of video frame width, video frame height, and frame rate.
+   *
+   * You can call this method to get how many video formats the specified video capture device can
+   * support, and then call `getCapability` to get the specific video frame information in the
+   * specified video format.
+   *
+   * @param deviceIdUTF8 The ID of the video capture device.
+   *
+   * @return
+   * - 0: Success. Returns the number of video formats supported by this device. For example: If the
+   * specified camera supports 10 different video formats, the return value is 10.
+   * - < 0: Failure.
+   */
+  virtual int numberOfCapabilities(const char* deviceIdUTF8) = 0;
+
+  /**
+   * Gets the detailed video frame information of the video capture device in the specified video format.
+   *
+   * After calling `numberOfCapabilities` to get the number of video formats supported by the video capture
+   * device, you can call this method to get the specific video frame information supported by the
+   * specified index number.
+   *
+   * @param deviceIdUTF8 ID of the video capture device.
+   * @param deviceCapabilityNumber The index number of the video format. If the return value of `numberOfCapabilities`
+   * is i, the value range of this parameter is [0,i).
+   * @param capability Output parameter. Indicates the specific information of the specified video format,
+   * including width (px), height (px), and frame rate (fps). See VideoFormat.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getCapability(const char* deviceIdUTF8, const uint32_t deviceCapabilityNumber, VideoFormat& capability) = 0;
+#endif
+  /**
+   * Starts the video capture device test.
+   *
+   * This method tests whether the video capture device works properly.
+   * Before calling this method, ensure that you have already called
+   * \ref IRtcEngine::enableVideo "enableVideo", and the HWND window handle of
+   * the incoming parameter is valid.
+   *
+   * @param hwnd An Output parameter that specifies the window handle to display the video.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startDeviceTest(view_t hwnd) = 0;
+
+  /**
+   * Stops the video capture device test.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopDeviceTest() = 0;
+
+  /**
+   * Releases all the resources occupied by the `IVideoDeviceManager` object.
+   */
+  virtual void release() = 0;
+};
+
+/**
+ * The context of IRtcEngine.
+ */
+struct RtcEngineContext {
+  /**
+   * The event handler for IRtcEngine.
+   */
+  IRtcEngineEventHandler* eventHandler;
+  /**
+   * The App ID issued by Agora for your project. Only users in apps with the same App ID can join the
+   * same channel and communicate with each other. An App ID can only be used to create one `IRtcEngine`
+   * instance. To change your App ID, call release to destroy the current IRtcEngine instance, and then
+   * create a new one.
+   */
+  const char* appId;
+  /**
+   * - For Android, it is the context of Activity or Application.
+   * - For Windows, it is the window handle of app. Once set, this parameter enables you to plug
+   * or unplug the video devices while they are powered.
+   */
+  void* context;
+  /**
+   * The channel profile. See #CHANNEL_PROFILE_TYPE.
+   */
+  CHANNEL_PROFILE_TYPE channelProfile;
+
+  /**
+   * The license used for verification when connectting channel. Charge according to the license
+   */
+  const char* license;
+
+  /**
+   * The audio application scenario. See #AUDIO_SCENARIO_TYPE.
+   *
+   * @note Agora recommends the following scenarios:
+   * - `AUDIO_SCENARIO_DEFAULT(0)`
+   * - `AUDIO_SCENARIO_GAME_STREAMING(3)`
+   */
+  AUDIO_SCENARIO_TYPE audioScenario;
+  /**
+   * The region for connection. This is an advanced feature and applies to scenarios that have regional restrictions.
+   *
+   * For the regions that Agora supports, see #AREA_CODE. The area codes support bitwise operation.
+   *
+   * After specifying the region, the app integrated with the Agora SDK connects to the Agora servers
+   * within that region.
+   */
+  unsigned int areaCode;
+
+  /**
+   * The log files that the SDK outputs. See LogConfig.
+   *
+   * By default, the SDK generates five SDK log files and five API call log files with the following rules:
+   * - The SDK log files are: `agorasdk.log`, `agorasdk.1.log`, `agorasdk.2.log`, `agorasdk.3.log`, and `agorasdk.4.log`.
+   * - The API call log files are: `agoraapi.log`, `agoraapi.1.log`, `agoraapi.2.log`, `agoraapi.3.log`, and `agoraapi.4.log`.
+   * - The default size for each SDK log file is 1,024 KB; the default size for each API call log file is 2,048 KB. These log files are encoded in UTF-8.
+   * - The SDK writes the latest logs in `agorasdk.log` or `agoraapi.log`.
+   * - When `agorasdk.log` is full, the SDK processes the log files in the following order:
+   *   - Delete the `agorasdk.4.log` file (if any).
+   *   - Rename `agorasdk.3.log` to `agorasdk.4.log`.
+   *   - Rename `agorasdk.2.log` to `agorasdk.3.log`.
+   *   - Rename `agorasdk.1.log` to `agorasdk.2.log`.
+   *   - Create a new `agorasdk.log` file.
+   */
+  commons::LogConfig logConfig;
+
+  /**
+   * Thread priority for SDK common threads
+   */
+  Optional<THREAD_PRIORITY_TYPE> threadPriority;
+
+  /**
+   * Whether to use egl context in the current thread as sdk‘s root egl context,
+   * which is shared by all egl related modules. eg. camera capture, video renderer.
+   *
+   * @note
+   * This property applies to Android only.
+   */
+  bool useExternalEglContext;
+
+  /**
+   * Determines whether to enable domain limit
+   * -true: only connect to servers which already parsed by DNS
+   * -false: (Default) connect to servers with no limit
+   */
+  bool domainLimit;
+
+  /**
+   * Whether to automatically register Agora extensions when initializing RtcEngine.
+   * -true: (Default) Automatically register Agora extensions.
+   * -false: Do not automatically register Agora extensions. The user calls EnableExtension to manually register an Agora extension.
+   */
+  bool autoRegisterAgoraExtensions;
+
+  RtcEngineContext()
+      : eventHandler(NULL), appId(NULL), context(NULL), channelProfile(CHANNEL_PROFILE_LIVE_BROADCASTING),
+        license(NULL), audioScenario(AUDIO_SCENARIO_DEFAULT), areaCode(AREA_CODE_GLOB),
+        logConfig(), useExternalEglContext(false), domainLimit(false), autoRegisterAgoraExtensions(true) {}
+};
+
+/** Definition of IMetadataObserver
+*/
+class IMetadataObserver {
+public:
+    virtual ~IMetadataObserver() {}
+
+    /** The metadata type.
+     *
+     * @note We only support video metadata for now.
+     */
+    enum METADATA_TYPE
+    {
+        /** -1: (Not supported) Unknown.
+         */
+        UNKNOWN_METADATA = -1,
+        /** 0: (Supported) Video metadata.
+         */
+        VIDEO_METADATA = 0,
+    };
+    /**
+      * The maximum metadata size.
+      */
+    enum MAX_METADATA_SIZE_TYPE
+    {
+        INVALID_METADATA_SIZE_IN_BYTE = -1,
+        DEFAULT_METADATA_SIZE_IN_BYTE = 512,
+        MAX_METADATA_SIZE_IN_BYTE = 1024
+    };
+
+    /** Metadata.
+     */
+    struct Metadata
+    {
+      /** The User ID that sent the metadata.
+        * - For the receiver: The user ID of the user who sent the `metadata`.
+        * - For the sender: Ignore this value.
+        */
+      unsigned int uid;
+      /** The buffer size of the sent or received `metadata`.
+        */
+      unsigned int size;
+      /** The buffer address of the sent or received `metadata`.
+        */
+      unsigned char* buffer;
+      /** The timestamp (ms) of the `metadata`.
+        *
+        */
+      long long timeStampMs;
+
+      Metadata() : uid(0), size(0), buffer(NULL), timeStampMs(0) {}
+    };
+
+   /** Occurs when the SDK requests the maximum size of the metadata.
+    *
+    *
+    * After successfully complete the registration by calling `registerMediaMetadataObserver`, the SDK
+    * triggers this callback once every video frame is sent. You need to specify the maximum size of
+    * the metadata in the return value of this callback.
+    *
+    * @return The maximum size of the buffer of the metadata that you want to use. The highest value is
+    * 1024 bytes. Ensure that you set the return value.
+    */
+    virtual int getMaxMetadataSize() { return DEFAULT_METADATA_SIZE_IN_BYTE; }
+
+    /** Occurs when the local user receives the metadata.
+
+     @note Ensure that the size of the metadata does not exceed the value set in the `getMaxMetadataSize` callback.
+
+     @param metadata The metadata that the user wants to send. For details, see Metadata.
+     @param source_type The video data type: #VIDEO_SOURCE_TYPE.
+     @return
+     - true:  Send.
+     - false: Do not send.
+     */
+    virtual bool onReadyToSendMetadata(Metadata &metadata, VIDEO_SOURCE_TYPE source_type) = 0;
+
+    /** Occurs when the local user receives the metadata.
+     *
+     * @param metadata The metadata received. See Metadata.
+     *
+     * @note If the receiver is audience, the receiver cannot get the NTP timestamp (ms)
+     * that the metadata sends.
+     */
+    virtual void onMetadataReceived(const Metadata& metadata) = 0;
+};
+
+// The reason codes for media streaming
+// GENERATED_JAVA_ENUM_PACKAGE: io.agora.streaming
+enum DIRECT_CDN_STREAMING_REASON {
+  // No error occurs.
+  DIRECT_CDN_STREAMING_REASON_OK = 0,
+  // A general error occurs (no specified reason).
+  DIRECT_CDN_STREAMING_REASON_FAILED = 1,
+  // Audio publication error.
+  DIRECT_CDN_STREAMING_REASON_AUDIO_PUBLICATION = 2,
+  // Video publication error.
+  DIRECT_CDN_STREAMING_REASON_VIDEO_PUBLICATION = 3,
+
+  DIRECT_CDN_STREAMING_REASON_NET_CONNECT = 4,
+  // Already exist stream name.
+  DIRECT_CDN_STREAMING_REASON_BAD_NAME = 5,
+};
+
+// The connection state of media streaming
+// GENERATED_JAVA_ENUM_PACKAGE: io.agora.streaming
+enum DIRECT_CDN_STREAMING_STATE {
+
+  DIRECT_CDN_STREAMING_STATE_IDLE = 0,
+
+  DIRECT_CDN_STREAMING_STATE_RUNNING = 1,
+
+  DIRECT_CDN_STREAMING_STATE_STOPPED = 2,
+
+  DIRECT_CDN_STREAMING_STATE_FAILED = 3,
+
+  DIRECT_CDN_STREAMING_STATE_RECOVERING = 4,
+};
+
+/**
+ * The statistics of the Direct Cdn Streams.
+ */
+struct DirectCdnStreamingStats {
+    /**
+     * Width of the video pushed by rtmp.
+     */
+    int videoWidth;
+
+    /**
+     * Height of the video pushed by rtmp.
+     */
+    int videoHeight;
+
+    /**
+     * The frame rate of the video pushed by rtmp.
+     */
+    int fps;
+
+    /**
+     * Real-time bit rate of the video streamed by rtmp.
+     */
+    int videoBitrate;
+
+    /**
+     * Real-time bit rate of the audio pushed by rtmp.
+     */
+    int audioBitrate;
+};
+
+/**
+ * The event handler for direct cdn streaming
+ *
+ */
+class IDirectCdnStreamingEventHandler {
+ public:
+  virtual ~IDirectCdnStreamingEventHandler() {}
+
+  /**
+   * Event callback of direct cdn streaming
+   * @param state Current status
+   * @param reason Reason Code
+   * @param message Message
+   */
+  virtual void onDirectCdnStreamingStateChanged(DIRECT_CDN_STREAMING_STATE state, DIRECT_CDN_STREAMING_REASON reason, const char* message)  {
+    (void)state;
+    (void)reason;
+    (void)message;
+  };
+
+  virtual void onDirectCdnStreamingStats(const DirectCdnStreamingStats& stats)  {
+    (void)stats;
+  };
+};
+
+/**
+ * The channel media options.
+ */
+struct DirectCdnStreamingMediaOptions {
+  /**
+   * Determines whether to publish the video of the camera track.
+   * - true: Publish the video track of the camera capturer.
+   * - false: (Default) Do not publish the video track of the camera capturer.
+   */
+  Optional<bool> publishCameraTrack;
+  /**
+   * Determines whether to publish the recorded audio.
+   * - true: Publish the recorded audio.
+   * - false: (Default) Do not publish the recorded audio.
+   */
+  Optional<bool> publishMicrophoneTrack;
+  /**
+   * Determines whether to publish the audio of the custom audio track.
+   * - true: Publish the audio of the custom audio track.
+   * - false: (Default) Do not publish the audio of the custom audio track.
+   */
+  Optional<bool> publishCustomAudioTrack;
+  /**
+   * Determines whether to publish the video of the custom video track.
+   * - true: Publish the video of the custom video track.
+   * - false: (Default) Do not publish the video of the custom video track.
+   */
+  Optional<bool> publishCustomVideoTrack;
+  /**
+  * Determines whether to publish the audio track of media player source.
+  * - true: Publish the audio track of media player source.
+  * - false: (Default) Do not publish the audio track of media player source.
+  */
+  Optional<bool> publishMediaPlayerAudioTrack;
+  /**
+  * Determines which media player source should be published.
+  * You can get the MediaPlayerId after calling getMediaPlayerId() of AgoraMediaPlayer.
+  */
+  Optional<int> publishMediaPlayerId;
+  /**
+   * The custom video track id which will used to publish.
+   * You can get the VideoTrackId after calling createCustomVideoTrack() of IRtcEngine.
+   */
+  Optional<video_track_id_t> customVideoTrackId;
+
+  DirectCdnStreamingMediaOptions() {}
+  ~DirectCdnStreamingMediaOptions() {}
+
+  void SetAll(const DirectCdnStreamingMediaOptions& change) {
+#define SET_FROM(X) SetFrom(&X, change.X)
+      SET_FROM(publishCameraTrack);
+      SET_FROM(publishMicrophoneTrack);
+      SET_FROM(publishCustomAudioTrack);
+      SET_FROM(publishCustomVideoTrack);
+      SET_FROM(publishMediaPlayerAudioTrack);
+      SET_FROM(publishMediaPlayerId);
+      SET_FROM(customVideoTrackId);
+#undef SET_FROM
+  }
+
+  bool operator==(const DirectCdnStreamingMediaOptions& o) const {
+#define BEGIN_COMPARE() bool b = true
+#define ADD_COMPARE(X) b = (b && (X == o.X))
+#define END_COMPARE()
+
+      BEGIN_COMPARE();
+      ADD_COMPARE(publishCameraTrack);
+      ADD_COMPARE(publishMicrophoneTrack);
+      ADD_COMPARE(publishCustomAudioTrack);
+      ADD_COMPARE(publishCustomVideoTrack);
+      ADD_COMPARE(publishMediaPlayerAudioTrack);
+      ADD_COMPARE(customVideoTrackId);
+      ADD_COMPARE(publishMediaPlayerId);
+      END_COMPARE();
+
+#undef BEGIN_COMPARE
+#undef ADD_COMPARE
+#undef END_COMPARE
+      return b;
+  }
+
+  DirectCdnStreamingMediaOptions& operator=(const DirectCdnStreamingMediaOptions& replace) {
+    if (this != &replace) {
+#define REPLACE_BY(X) ReplaceBy(&X, replace.X)
+
+        REPLACE_BY(publishCameraTrack);
+        REPLACE_BY(publishMicrophoneTrack);
+        REPLACE_BY(publishCustomAudioTrack);
+        REPLACE_BY(publishCustomVideoTrack);
+        REPLACE_BY(publishMediaPlayerAudioTrack);
+        REPLACE_BY(customVideoTrackId);
+        REPLACE_BY(publishMediaPlayerId);
+#undef REPLACE_BY
+    }
+    return *this;
+  }
+};
+
+/**
+ * The information for extension.
+ */
+struct ExtensionInfo {
+  /**
+   * The type of media device.
+   */
+  agora::media::MEDIA_SOURCE_TYPE mediaSourceType;
+
+  /**
+   * The id of the remote user on which the extension works.
+   *
+   * @note remoteUid = 0 means that the extension works on all remote streams.
+   */
+  uid_t remoteUid;
+
+  /**
+   *  The unique channel name for the AgoraRTC session in the string format. The string
+   * length must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+",
+   * "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   */
+  const char* channelId;
+
+  /**
+   * User ID: A 32-bit unsigned integer ranging from 1 to (2^32-1). It must be unique.
+   */
+  uid_t localUid;
+
+  ExtensionInfo() : mediaSourceType(agora::media::UNKNOWN_MEDIA_SOURCE), remoteUid(0), channelId(NULL), localUid(0) {}
+};
+
+class IMediaPlayer;
+class IMediaRecorder;
+
+/**
+ * The IRtcEngine class, which is the basic interface of the Agora SDK that implements the core functions of real-time communication.
+ *
+ * `IRtcEngine` provides the main methods that your app can call.
+ *
+ */
+class IRtcEngine : public agora::base::IEngineBase {
+ public:
+  /**
+   * Releases the IRtcEngine object.
+   *
+   * This method releases all resources used by the Agora SDK. Use this method for apps in which users
+   * occasionally make voice or video calls. When users do not make calls, you can free up resources for
+   * other operations.
+   *
+   * After a successful method call, you can no longer use any method or callback in the SDK anymore.
+   * If you want to use the real-time communication functions again, you must call `createAgoraRtcEngine`
+   * and `initialize` to create a new `IRtcEngine` instance.
+   *
+   * @note If you want to create a new `IRtcEngine` instance after destroying the current one, ensure
+   * that you wait till the `release` method execution to complete.
+   *
+   * @param sync Determines whether this method is a synchronous call.
+   * - `true`: This method is a synchronous call, which means that the result of this method call
+   * returns after the IRtcEngine object resources are released. Do not call this method
+   * in any callback generated by the SDK, or it may result in a deadlock.
+   * - `false`: This method is an asynchronous call. The result returns immediately even when the
+   * IRtcEngine object resources are not released.
+   *
+   */
+  AGORA_CPP_API static void release(bool sync = false);
+
+  /**
+   * Initializes `IRtcEngine`.
+   *
+   * All called methods provided by the `IRtcEngine` class are executed asynchronously. Agora
+   * recommends calling these methods in the same thread.
+   *
+   * @note
+   * - Before calling other APIs, you must call `createAgoraRtcEngine` and `initialize `to create and
+   * initialize the `IRtcEngine` object.
+   * - The SDK supports creating only one `IRtcEngine` instance for an app.
+   *
+   * @param context The RtcEngineContext object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int initialize(const RtcEngineContext& context) = 0;
+
+  /**
+   * Gets the pointer to the specified interface.
+   *
+   * @param iid The ID of the interface. See #INTERFACE_ID_TYPE for details.
+   * @param inter Output parameter. The pointer to the specified interface.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int queryInterface(INTERFACE_ID_TYPE iid, void** inter) = 0;
+
+
+  /**
+   * Gets the SDK version.
+   * @param build The build number.
+   * @return The version of the current SDK in the string format.
+   */
+  virtual const char* getVersion(int* build) = 0;
+
+  /**
+   * Gets the warning or error description.
+   * @param code The error code or warning code reported by the SDK.
+   * @return The specific error or warning description.
+   */
+  virtual const char* getErrorDescription(int code) = 0;
+
+  /**
+   * Queries the capacity of the current device codec.
+   *
+   * @param codec_info An array of the codec cap information: CodecCapInfo.
+   * @param size The array size.
+   * @return 
+   * 0: Success.
+   * < 0: Failure.
+   */
+  virtual int queryCodecCapability(CodecCapInfo* codecInfo, int& size) = 0;
+
+    /**
+   * Queries the score of the current device.
+   *
+   * @return 
+   * > 0: If the value is greater than 0, it means that the device score has been retrieved and represents the score value.
+   * Most devices score between 60-100, with higher scores indicating better performance.
+   * 
+   * < 0: Failure.
+   */
+  virtual int queryDeviceScore() = 0;
+
+  /**
+   * Preload a channel.
+   *
+   * This method enables users to preload a channel.
+   *
+   * A successful call of this method will reduce the time of joining the same channel.
+   *
+   * Note:
+   *  1. The SDK supports preloading up to 20 channels. Once the preloaded channels exceed the limit, the SDK will keep the latest 20 available.
+   *  2. Renew the token of the preloaded channel by calling this method with the same 'channelId' and 'uid'.
+   *
+   * @param token The token generated on your server for authentication.
+   * @param channelId The channel name. This parameter signifies the channel in which users engage in
+   * real-time audio and video interaction. Under the premise of the same App ID, users who fill in
+   * the same channel ID enter the same channel for audio and video interaction. The string length
+   * must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param uid The user ID. This parameter is used to identify the user in the channel for real-time
+   * audio and video interaction. You need to set and manage user IDs yourself, and ensure that each
+   * user ID in the same channel is unique. This parameter is a 32-bit unsigned integer. The value
+   * range is 1 to 2<h>32</h>-1. If the user ID is not assigned (or set to 0), the SDK assigns a random user
+   * ID and returns it in the onJoinChannelSuccess callback. Your application must record and maintain
+   * the returned user ID, because the SDK does not do so.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+   * object before calling this method.
+   *   - -102: The channel name is invalid. You need to pass in a valid channel name in channelId to
+   * preload the channel again.
+   */
+  virtual int preloadChannel(const char* token, const char* channelId, uid_t uid) = 0;
+
+  /**
+   * Preload a channel.
+   *
+   * This method enables users to preload a channel.
+   *
+   * A successful call of this method will reduce the time of joining the same channel.
+   *
+   * Note:
+   *  1. The SDK supports preloading up to 20 channels. Once the preloaded channels exceed the limit, the SDK will keep the latest 20 available.
+   *  2. Renew the token of the preloaded channel by calling this method with the same 'channelId' and 'userAccount'.
+   *
+   * @param token The token generated on your server for authentication.
+   * @param channelId The channel name. This parameter signifies the channel in which users engage in
+   * real-time audio and video interaction. Under the premise of the same App ID, users who fill in
+   * the same channel ID enter the same channel for audio and video interaction. The string length
+   * must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *   - -2: The parameter is invalid. For example, the userAccount parameter is empty.
+   * You need to pass in a valid parameter and preload the channel again.
+   *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+   * object before calling this method.
+   *   - -102: The channel name is invalid. You need to pass in a valid channel name in channelId to
+   * preload the channel again.
+   */
+  virtual int preloadChannelWithUserAccount(const char* token, const char* channelId, const char* userAccount) = 0;
+
+  /**
+   * Update token of the preloaded channels.
+   *
+   * An easy way to update all preloaded channels' tokens, if all preloaded channels use the same token.
+   *
+   * If preloaded channels use different tokens, we need to call the 'preloadChannel' method with the same 'channelId'
+   * and 'uid' or 'userAccount' to update the corresponding token.
+   *
+   * @param token The token generated on your server for authentication.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *   - -2: The token is invalid. You need to pass in a valid token and update the token again.
+   *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+   * object before calling this method.
+   */
+  virtual int updatePreloadChannelToken(const char* token) = 0;
+
+  /**
+   * Joins a channel.
+   *
+   * This method enables users to join a channel. Users in the same channel can talk to each other,
+   * and multiple users in the same channel can start a group chat. Users with different App IDs
+   * cannot call each other.
+   *
+   * A successful call of this method triggers the following callbacks:
+   * - The local client: The `onJoinChannelSuccess` and `onConnectionStateChanged` callbacks.
+   * - The remote client: `onUserJoined`, if the user joining the channel is in the Communication
+   * profile or is a host in the Live-broadcasting profile.
+   *
+   * When the connection between the client and Agora's server is interrupted due to poor network
+   * conditions, the SDK tries reconnecting to the server. When the local client successfully rejoins
+   * the channel, the SDK triggers the `onRejoinChannelSuccess` callback on the local client.
+   *
+   * @note Once a user joins the channel, the user subscribes to the audio and video streams of all
+   * the other users in the channel by default, giving rise to usage and billing calculation. To
+   * stop subscribing to a specified stream or all remote streams, call the corresponding `mute` methods.
+   *
+   * @param token The token generated on your server for authentication.
+   * @param channelId The channel name. This parameter signifies the channel in which users engage in
+   * real-time audio and video interaction. Under the premise of the same App ID, users who fill in
+   * the same channel ID enter the same channel for audio and video interaction. The string length
+   * must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param info (Optional) Reserved for future use.
+   * @param uid The user ID. This parameter is used to identify the user in the channel for real-time
+   * audio and video interaction. You need to set and manage user IDs yourself, and ensure that each
+   * user ID in the same channel is unique. This parameter is a 32-bit unsigned integer. The value
+   * range is 1 to 2<h>32</h>-1. If the user ID is not assigned (or set to 0), the SDK assigns a random user
+   * ID and returns it in the onJoinChannelSuccess callback. Your application must record and maintain
+   * the returned user ID, because the SDK does not do so.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *   - -2: The parameter is invalid. For example, the token is invalid, the uid parameter is not set
+   * to an integer, or the value of a member in the `ChannelMediaOptions` structure is invalid. You need
+   * to pass in a valid parameter and join the channel again.
+   *   - -3: Failes to initialize the `IRtcEngine` object. You need to reinitialize the IRtcEngine object.
+   *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+   * object before calling this method.
+   *   - -8: The internal state of the IRtcEngine object is wrong. The typical cause is that you call
+   * this method to join the channel without calling `stopEchoTest` to stop the test after calling
+   * `startEchoTest` to start a call loop test. You need to call `stopEchoTest` before calling this method.
+   *   - -17: The request to join the channel is rejected. The typical cause is that the user is in the
+   * channel. Agora recommends using the `onConnectionStateChanged` callback to get whether the user is
+   * in the channel. Do not call this method to join the channel unless you receive the
+   * `CONNECTION_STATE_DISCONNECTED(1)` state.
+   *   - -102: The channel name is invalid. You need to pass in a valid channel name in channelId to
+   * rejoin the channel.
+   *   - -121: The user ID is invalid. You need to pass in a valid user ID in uid to rejoin the channel.
+   */
+  virtual int joinChannel(const char* token, const char* channelId, const char* info, uid_t uid) = 0;
+
+  /**
+   * Joins a channel with media options.
+   *
+   * This method enables users to join a channel. Users in the same channel can talk to each other,
+   * and multiple users in the same channel can start a group chat. Users with different App IDs
+   * cannot call each other.
+   *
+   * A successful call of this method triggers the following callbacks:
+   * - The local client: The `onJoinChannelSuccess` and `onConnectionStateChanged` callbacks.
+   * - The remote client: `onUserJoined`, if the user joining the channel is in the Communication
+   * profile or is a host in the Live-broadcasting profile.
+   *
+   * When the connection between the client and Agora's server is interrupted due to poor network
+   * conditions, the SDK tries reconnecting to the server. When the local client successfully rejoins
+   * the channel, the SDK triggers the `onRejoinChannelSuccess` callback on the local client.
+   *
+   * Compared to `joinChannel`, this method adds the options parameter to configure whether to
+   * automatically subscribe to all remote audio and video streams in the channel when the user
+   * joins the channel. By default, the user subscribes to the audio and video streams of all
+   * the other users in the channel, giving rise to usage and billings. To unsubscribe, set the
+   * `options` parameter or call the `mute` methods accordingly.
+   *
+   * @note
+   * - This method allows users to join only one channel at a time.
+   * - Ensure that the app ID you use to generate the token is the same app ID that you pass in the
+   * `initialize` method; otherwise, you may fail to join the channel by token.
+   *
+   * @param token The token generated on your server for authentication.
+   *
+   * @param channelId The channel name. This parameter signifies the channel in which users engage in
+   * real-time audio and video interaction. Under the premise of the same App ID, users who fill in
+   * the same channel ID enter the same channel for audio and video interaction. The string length
+   * must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param uid The user ID. This parameter is used to identify the user in the channel for real-time
+   * audio and video interaction. You need to set and manage user IDs yourself, and ensure that each
+   * user ID in the same channel is unique. This parameter is a 32-bit unsigned integer. The value
+   * range is 1 to 2<h>32</h>-1. If the user ID is not assigned (or set to 0), the SDK assigns a random user
+   * ID and returns it in the `onJoinChannelSuccess` callback. Your application must record and maintain
+   * the returned user ID, because the SDK does not do so.
+   * @param options The channel media options: ChannelMediaOptions.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *   - -2: The parameter is invalid. For example, the token is invalid, the uid parameter is not set
+   * to an integer, or the value of a member in the `ChannelMediaOptions` structure is invalid. You need
+   * to pass in a valid parameter and join the channel again.
+   *   - -3: Failes to initialize the `IRtcEngine` object. You need to reinitialize the IRtcEngine object.
+   *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+   * object before calling this method.
+   *   - -8: The internal state of the IRtcEngine object is wrong. The typical cause is that you call
+   * this method to join the channel without calling `stopEchoTest` to stop the test after calling
+   * `startEchoTest` to start a call loop test. You need to call `stopEchoTest` before calling this method.
+   *   - -17: The request to join the channel is rejected. The typical cause is that the user is in the
+   * channel. Agora recommends using the `onConnectionStateChanged` callback to get whether the user is
+   * in the channel. Do not call this method to join the channel unless you receive the
+   * `CONNECTION_STATE_DISCONNECTED(1)` state.
+   *   - -102: The channel name is invalid. You need to pass in a valid channel name in channelId to
+   * rejoin the channel.
+   *   - -121: The user ID is invalid. You need to pass in a valid user ID in uid to rejoin the channel.
+   */
+  virtual int joinChannel(const char* token, const char* channelId, uid_t uid, const ChannelMediaOptions& options) = 0;
+
+  /**
+   *  Updates the channel media options after joining the channel.
+   *
+   * @param options The channel media options: ChannelMediaOptions.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int updateChannelMediaOptions(const ChannelMediaOptions& options) = 0;
+
+  /**
+   * Leaves the channel.
+   *
+   * This method allows a user to leave the channel, for example, by hanging up or exiting a call.
+   *
+   * This method is an asynchronous call, which means that the result of this method returns even before
+   * the user has not actually left the channel. Once the user successfully leaves the channel, the
+   * SDK triggers the \ref IRtcEngineEventHandler::onLeaveChannel "onLeaveChannel" callback.
+   *
+   * @note
+   * If you call \ref release "release" immediately after this method, the leaveChannel process will be
+   * interrupted, and the SDK will not trigger the `onLeaveChannel` callback.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int leaveChannel() = 0;
+
+  /**
+   * Leaves the channel.
+   *
+   * @param options The leave channel options.
+   *
+   * This method allows a user to leave the channel, for example, by hanging up or exiting a call.
+   *
+   * This method is an asynchronous call, which means that the result of this method returns even before
+   * the user has not actually left the channel. Once the user successfully leaves the channel, the
+   * SDK triggers the \ref IRtcEngineEventHandler::onLeaveChannel "onLeaveChannel" callback.
+   *
+   * @note
+   * If you call \ref release "release" immediately after this method, the leaveChannel process will be
+   * interrupted, and the SDK will not trigger the `onLeaveChannel` callback.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int leaveChannel(const LeaveChannelOptions& options) = 0;
+
+  /**
+   * Renews the token.
+   *
+   * Once a token is enabled and used, it expires after a certain period of time.
+   *
+   * Under the following circumstances, generate a new token on your server, and then call this method to
+   * renew it. Failure to do so results in the SDK disconnecting from the server.
+   * - The \ref IRtcEngineEventHandler onTokenPrivilegeWillExpire "onTokenPrivilegeWillExpire" callback is triggered;
+   * - The \ref IRtcEngineEventHandler::onRequestToken "onRequestToken" callback is triggered;
+   * - The `ERR_TOKEN_EXPIRED(-109)` error is reported.
+   *
+   * @param token The new token.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int renewToken(const char* token) = 0;
+
+  /**
+   * Sets the channel profile.
+   *
+   * The IRtcEngine differentiates channel profiles and applies different optimization algorithms accordingly.
+   * For example, it prioritizes smoothness and low latency for a video call, and prioritizes video quality
+   * for a video broadcast.
+   *
+   * @note
+   * - To ensure the quality of real-time communication, we recommend that all users in a channel use the
+   * same channel profile.
+   * - Call this method before calling `joinChannel`. You cannot set the channel profile
+   * once you have joined the channel.
+   *
+   * @param profile The channel profile: #CHANNEL_PROFILE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setChannelProfile(CHANNEL_PROFILE_TYPE profile) = 0;
+
+  /**
+   * Sets the role of a user.
+   *
+   * This method sets the user role as either BROADCASTER or AUDIENCE (default).
+   * - The broadcaster sends and receives streams.
+   * - The audience receives streams only.
+   *
+   * By default, all users are audience regardless of the channel profile.
+   * Call this method to change the user role to BROADCASTER so that the user can
+   * send a stream.
+   *
+   * @note
+   * After calling the setClientRole() method to CLIENT_ROLE_AUDIENCE, the SDK stops audio recording.
+   * However, CLIENT_ROLE_AUDIENCE will keep audio recording with AUDIO_SCENARIO_CHATROOM(5).
+   * Normally, app developer can also use mute api to achieve the same result, and we implement
+   * this 'non-orthogonal' behavior only to make API backward compatible.
+   *
+   * @param role The role of the client: #CLIENT_ROLE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setClientRole(CLIENT_ROLE_TYPE role) = 0;
+
+  /** Sets the role of the user, such as a host or an audience (default), before joining a channel in the live interactive streaming.
+    *
+    * This method can be used to switch the user role in the live interactive streaming after the user joins a channel.
+    *
+    * In the `LIVE_BROADCASTING` profile, when a user switches user roles after joining a channel, a successful \ref agora::rtc::IRtcEngine::setClientRole "setClientRole" method call triggers the following callbacks:
+    * - The local client: \ref agora::rtc::IRtcEngineEventHandler::onClientRoleChanged "onClientRoleChanged"
+    * - The remote client: \ref agora::rtc::IRtcEngineEventHandler::onUserJoined "onUserJoined" or \ref agora::rtc::IRtcEngineEventHandler::onUserOffline "onUserOffline" (BECOME_AUDIENCE)
+    *
+    * @note
+    * This method applies only to the `LIVE_BROADCASTING` profile.
+    *
+    * @param role Sets the role of the user. See #CLIENT_ROLE_TYPE.
+    * @param options Sets the audience latency level of the user. See #ClientRoleOptions.
+    *
+    * @return
+    * - 0(ERR_OK): Success.
+    * - < 0: Failure.
+    *  - -1(ERR_FAILED): A general error occurs (no specified reason).
+    *  - -2(ERR_INALID_ARGUMENT): The parameter is invalid.
+    *  - -7(ERR_NOT_INITIALIZED): The SDK is not initialized.
+    */
+  virtual int setClientRole(CLIENT_ROLE_TYPE role, const ClientRoleOptions& options) = 0;
+
+  /** Starts an audio call test.
+
+  This method launches an audio call test to determine whether the audio devices
+  (for example, headset and speaker) and the network connection are working
+  properly.
+
+  In the test, the user first speaks, and the recording is played back
+  in 10 seconds. If the user can hear the recording in 10 seconds, it indicates
+  that the audio devices and network connection work properly.
+
+  @note
+  After calling the startEchoTest() method, always call stopEchoTest() to end
+  the test. Otherwise, the app cannot run the next echo test, nor can
+  it call the joinChannel() method.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startEchoTest() = 0;
+
+  /** Starts an audio call test.
+
+  This method starts an audio call test to determine whether the audio devices (for example, headset and speaker) and the network connection are working properly.
+
+  In the audio call test, you record your voice. If the recording plays back within the set time interval, the audio devices and the network connection are working properly.
+
+  @note
+  - Call this method before joining a channel.
+  - After calling this method, call the \ref IRtcEngine::stopEchoTest "stopEchoTest" method to end the test. Otherwise, the app cannot run the next echo test, or call the \ref IRtcEngine::joinChannel "joinChannel" method.
+  - In the `LIVE_BROADCASTING` profile, only a host can call this method.
+  @param intervalInSeconds The time interval (s) between when you speak and when the recording plays back.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startEchoTest(int intervalInSeconds) = 0;
+
+  /** Starts a video call test.
+   *
+   * @param config: configuration for video call test.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startEchoTest(const EchoTestConfiguration& config) = 0;
+
+  /** Stops the audio call test.
+  @return int
+
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int stopEchoTest() = 0;
+
+#if defined(__APPLE__) && TARGET_OS_IOS
+  /** Enables the SDK use AVCaptureMultiCamSession or AVCaptureSession. Applies to iOS 13.0+ only.
+   * @param enabled Whether to enable multi-camera when capturing video:
+   * - true: Enable multi-camera, and the SDK uses AVCaptureMultiCamSession.
+   * - false: Disable multi-camera, and the SDK uses AVCaptureSession.
+   * @param config The config for secondary camera capture session. See #CameraCapturerConfiguration.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableMultiCamera(bool enabled, const CameraCapturerConfiguration& config) = 0;
+#endif
+  /**
+   * Enables the video.
+   *
+   * You can call this method either before joining a channel or during a call.
+   * If you call this method before entering a channel, the service starts the video; if you call it
+   * during a call, the audio call switches to a video call.
+   *
+   * @note
+   * This method controls the underlying states of the Engine. It is still
+   * valid after one leaves the channel.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableVideo() = 0;
+
+  /**
+   * Disables the video.
+   *
+   * This method stops capturing the local video and receiving any remote video.
+   * To enable the local preview function, call \ref enableLocalVideo "enableLocalVideo" (true).
+   * @return int
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int disableVideo() = 0;
+
+  /**
+   * Starts the local video preview before joining a channel.
+   *
+   * Once you call this method to start the local video preview, if you leave
+   * the channel by calling \ref leaveChannel "leaveChannel", the local video preview remains until
+   * you call \ref stopPreview "stopPreview" to disable it.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startPreview() = 0;
+
+  /**
+   * Starts the local video preview for specific source type.
+   * @param sourceType - The video source type.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startPreview(VIDEO_SOURCE_TYPE sourceType) = 0;
+
+  /**
+   * Stops the local video preview and the video.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopPreview() = 0;
+
+  /**
+   * Stops the local video preview for specific source type.
+   * @param sourceType - The video source type.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopPreview(VIDEO_SOURCE_TYPE sourceType) = 0;
+
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  virtual bool isPipSupported() = 0;
+  virtual int setupPip(const PipOptions& options) = 0;
+  virtual int startPip() = 0;
+#if defined(__APPLE__) && TARGET_OS_IOS
+  virtual int stopPip() = 0;
+#endif
+#endif
+
+  /** Starts the last-mile network probe test.
+
+  This method starts the last-mile network probe test before joining a channel
+  to get the uplink and downlink last-mile network statistics, including the
+  bandwidth, packet loss, jitter, and round-trip time (RTT).
+
+  Call this method to check the uplink network quality before users join a
+  channel or before an audience switches to a host. Once this method is
+  enabled, the SDK returns the following callbacks:
+  - \ref IRtcEngineEventHandler::onLastmileQuality "onLastmileQuality": the
+  SDK triggers this callback depending on the network
+  conditions. This callback rates the network conditions and is more closely
+  linked to the user experience.
+  - \ref IRtcEngineEventHandler::onLastmileProbeResult "onLastmileProbeResult":
+  the SDK triggers this callback within 30 seconds depending on the network
+  conditions. This callback returns the real-time statistics of the network
+  conditions and is more objective.
+
+  @note
+  - Do not call other methods before receiving the
+  \ref IRtcEngineEventHandler::onLastmileQuality "onLastmileQuality" and
+  \ref IRtcEngineEventHandler::onLastmileProbeResult "onLastmileProbeResult"
+  callbacks. Otherwise, the callbacks may be interrupted.
+  - In the Live-broadcast profile, a host should not call this method after
+  joining a channel.
+
+  @param config Sets the configurations of the last-mile network probe test. See
+  LastmileProbeConfig.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int startLastmileProbeTest(const LastmileProbeConfig& config) = 0;
+
+  /** Stops the last-mile network probe test. */
+  virtual int stopLastmileProbeTest() = 0;
+
+  /**
+   * Sets the video encoder configuration.
+   *
+   * Each configuration profile corresponds to a set of video parameters, including
+   * the resolution, frame rate, and bitrate.
+   *
+   * The parameters specified in this method are the maximum values under ideal network conditions.
+   * If the video engine cannot render the video using the specified parameters due
+   * to poor network conditions, the parameters further down the list are considered
+   * until a successful configuration is found.
+   *
+   * @param config The local video encoder configuration: VideoEncoderConfiguration.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVideoEncoderConfiguration(const VideoEncoderConfiguration& config) = 0;
+
+  /** Enables/Disables image enhancement and sets the options.
+   *
+   * @note Call this method after calling the \ref IRtcEngine::enableVideo "enableVideo" method.
+   *
+   * @param enabled Sets whether or not to enable image enhancement:
+   * - true: enables image enhancement.
+   * - false: disables image enhancement.
+   * @param options Sets the image enhancement option. See BeautyOptions.
+   */
+  virtual int setBeautyEffectOptions(bool enabled, const BeautyOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0;
+  /**
+   * Sets low-light enhancement.
+   *
+   * @since v4.0.0
+   *
+   * The low-light enhancement feature can adaptively adjust the brightness value of the video captured in situations with low or uneven lighting, such as backlit, cloudy, or dark scenes. It restores or highlights the image details and improves the overall visual effect of the video.
+   *
+   * You can call this method to enable the low-light enhancement feature and set the options of the low-light enhancement effect.
+   *
+   * @note
+   * - Before calling this method, ensure that you have integrated the following dynamic library into your project:
+   *  - Android: `libagora_segmentation_extension.so`
+   *  - iOS/macOS: `AgoraVideoSegmentationExtension.xcframework`
+   *  - Windows: `libagora_segmentation_extension.dll`
+   * - Call this method after \ref IRtcEngine::enableVideo "enableVideo".
+   * - The low-light enhancement feature has certain performance requirements on devices. If your device overheats after you enable low-light enhancement, Agora recommends modifying the low-light enhancement options to a less performance-consuming level or disabling low-light enhancement entirely.
+   *
+   * @param enabled Sets whether to enable low-light enhancement:
+   * - `true`: Enable.
+   * - `false`: (Default) Disable.
+   * @param options The low-light enhancement options. See LowlightEnhanceOptions.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLowlightEnhanceOptions(bool enabled, const LowlightEnhanceOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0;
+  /**
+   * Sets video noise reduction.
+   *
+   * @since v4.0.0
+   *
+   * Underlit environments and low-end video capture devices can cause video images to contain significant noise, which affects video quality. In real-time interactive scenarios, video noise also consumes bitstream resources and reduces encoding efficiency during encoding.
+   *
+   * You can call this method to enable the video noise reduction feature and set the options of the video noise reduction effect.
+   *
+   * @note
+   * - Before calling this method, ensure that you have integrated the following dynamic library into your project:
+   *  - Android: `libagora_segmentation_extension.so`
+   *  - iOS/macOS: `AgoraVideoSegmentationExtension.xcframework`
+   *  - Windows: `libagora_segmentation_extension.dll`
+   * - Call this method after \ref IRtcEngine::enableVideo "enableVideo".
+   * - The video noise reduction feature has certain performance requirements on devices. If your device overheats after you enable video noise reduction, Agora recommends modifying the video noise reduction options to a less performance-consuming level or disabling video noise reduction entirely.
+   *
+   * @param enabled Sets whether to enable video noise reduction:
+   * - `true`: Enable.
+   * - `false`: (Default) Disable.
+   * @param options The video noise reduction options. See VideoDenoiserOptions.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVideoDenoiserOptions(bool enabled, const VideoDenoiserOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0;
+  /**
+   * Sets color enhancement.
+   *
+   * @since v4.0.0
+   *
+   * The video images captured by the camera can have color distortion. The color enhancement feature intelligently adjusts video characteristics such as saturation and contrast to enhance the video color richness and color reproduction, making the video more vivid.
+   *
+   * You can call this method to enable the color enhancement feature and set the options of the color enhancement effect.
+   *
+   * @note
+   * - Before calling this method, ensure that you have integrated the following dynamic library into your project:
+   *  - Android: `libagora_segmentation_extension.so`
+   *  - iOS/macOS: `AgoraVideoSegmentationExtension.xcframework`
+   *  - Windows: `libagora_segmentation_extension.dll`
+   * - Call this method after \ref IRtcEngine::enableVideo "enableVideo".
+   * - The color enhancement feature has certain performance requirements on devices. If your device overheats after you enable color enhancement, Agora recommends modifying the color enhancement options to a less performance-consuming level or disabling color enhancement entirely.
+   *
+   * @param enabled Sets whether to enable color enhancement:
+   * - `true`: Enable.
+   * - `false`: (Default) Disable.
+   * @param options The color enhancement options. See ColorEnhanceOptions.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setColorEnhanceOptions(bool enabled, const ColorEnhanceOptions& options, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0;
+
+  /**
+   * Enables/Disables the virtual background. (beta function)
+   *
+   * @since v3.7.200
+   *
+   * After enabling the virtual background function, you can replace the original background image of the local user
+   * with a custom background image. After the replacement, all users in the channel can see the custom background
+   * image.
+   *
+   * @note
+   * - Before calling this method, ensure that you have integrated the
+   * `libagora_segmentation_extension.dll` (Windows)/`AgoraVideoSegmentationExtension.framework` (macOS) dynamic
+   * library into the project folder.
+   * - Call this method after \ref IRtcEngine::enableVideo "enableVideo".
+   * - This function requires a high-performance device. Agora recommends that you use this function on devices with
+   * an i5 CPU and better.
+   * - Agora recommends that you use this function in scenarios that meet the following conditions:
+   *  - A high-definition camera device is used, and the environment is uniformly lit.
+   *  - The captured video image is uncluttered, the user's portrait is half-length and largely unobstructed, and the
+   * background is a single color that differs from the color of the user's clothing.
+   *
+   * @param enabled Sets whether to enable the virtual background:
+   * - true: Enable.
+   * - false: Disable.
+   * @param backgroundSource The custom background image. See VirtualBackgroundSource. **Note**: To adapt the
+   * resolution of the custom background image to the resolution of the SDK capturing video, the SDK scales and crops
+   * the custom background image while ensuring that the content of the custom background image is not distorted.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableVirtualBackground(bool enabled, VirtualBackgroundSource backgroundSource, SegmentationProperty segproperty, agora::media::MEDIA_SOURCE_TYPE type = agora::media::PRIMARY_CAMERA_SOURCE) = 0;
+
+  /**
+   * Initializes the video view of a remote user.
+   *
+   * This method initializes the video view of a remote stream on the local device. It affects only the
+   * video view that the local user sees.
+   *
+   * Usually the app should specify the `uid` of the remote video in the method call before the
+   * remote user joins the channel. If the remote `uid` is unknown to the app, set it later when the
+   * app receives the \ref IRtcEngineEventHandler::onUserJoined "onUserJoined" callback.
+   *
+   * To unbind the remote user from the view, set `view` in VideoCanvas as `null`.
+   *
+   * @note
+   * Ensure that you call this method in the UI thread.
+   *
+   * @param canvas The remote video view settings: VideoCanvas.
+   * @return int
+   *  VIRTUAL_BACKGROUND_SOURCE_STATE_REASON_SUCCESS = 0,
+   *  VIRTUAL_BACKGROUND_SOURCE_STATE_REASON_IMAGE_NOT_EXIST = -1,
+   *  VIRTUAL_BACKGROUND_SOURCE_STATE_REASON_COLOR_FORMAT_NOT_SUPPORTED = -2,
+   *  VIRTUAL_BACKGROUND_SOURCE_STATE_REASON_DEVICE_NOT_SUPPORTED = -3,
+   */
+  virtual int setupRemoteVideo(const VideoCanvas& canvas) = 0;
+
+  /**
+   * Initializes the local video view.
+   *
+   * This method initializes the video view of the local stream on the local device. It affects only
+   * the video view that the local user sees, not the published local video stream.
+   *
+   * To unbind the local video from the view, set `view` in VideoCanvas as `null`.
+   *
+   * @note
+   * Call this method before joining a channel.
+   *
+   * @param canvas The local video view setting: VideoCanvas.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setupLocalVideo(const VideoCanvas& canvas) = 0;
+
+  /**
+   * Sets the Video application scenario.
+   *
+   * @since v4.2.0
+   *
+   * You can call this method to set the expected video scenario.
+   * The SDK will optimize the video experience for each scenario you set.
+   *
+   *
+   * @param scenarioType The video application scenario. See #ApplicationScenarioType.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_SUPPORTED (4): Unable to set video application scenario.
+   * - ERR_NOT_INITIALIZED (7): The SDK is not initialized.
+   */
+  virtual int setVideoScenario(VIDEO_APPLICATION_SCENARIO_TYPE scenarioType) = 0;
+
+    /**
+   * Sets the video qoe preference.
+   *
+   * @since v4.2.1
+   *
+   * You can call this method to set the expected QoE Preference.
+   * The SDK will optimize the video experience for each preference you set.
+   *
+   *
+   * @param qoePreference The qoe preference type. See #VIDEO_QOE_PREFERENCE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_SUPPORTED (4): Unable to set video application scenario.
+   * - ERR_NOT_INITIALIZED (7): The SDK is not initialized.
+   */
+  virtual int setVideoQoEPreference(VIDEO_QOE_PREFERENCE_TYPE qoePreference) = 0;
+
+  /**
+   * Enables the audio.
+   *
+   * The audio is enabled by default.
+   *
+   * @note
+   * This method controls the underlying states of the Engine. It is still
+   * valid after one leaves channel.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAudio() = 0;
+
+  /**
+   * Disables the audio.
+   *
+   * @note
+   * This method controls the underlying states of the Engine. It is still
+   * valid after one leaves channel.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int disableAudio() = 0;
+  
+  /**
+   * Sets the audio parameters and application scenarios.
+   *
+   * @deprecated This method is deprecated. You can use the
+   * \ref IRtcEngine::setAudioProfile(AUDIO_PROFILE_TYPE) "setAudioProfile"
+   * method instead. To set the audio scenario, call the \ref IRtcEngine::initialize "initialize"
+   * method and pass value in the `audioScenario` member in the RtcEngineContext struct.
+   *
+   * @note
+   * - Call this method before calling the `joinChannel` method.
+   * - In scenarios requiring high-quality audio, we recommend setting `profile` as `MUSIC_HIGH_QUALITY`(4)
+   * and `scenario` as `AUDIO_SCENARIO_GAME_STREAMING`(3).
+   *
+   * @param profile Sets the sample rate, bitrate, encoding mode, and the number of channels:
+   * #AUDIO_PROFILE_TYPE.
+   * @param scenario Sets the audio application scenarios: #AUDIO_SCENARIO_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioProfile(AUDIO_PROFILE_TYPE profile, AUDIO_SCENARIO_TYPE scenario) __deprecated = 0;
+
+  /**
+   * Sets the audio profile.
+   *
+   * @note
+   * - Call this method before calling the `joinChannel` method.
+   * - In scenarios requiring high-quality audio, Agora recommends setting `profile` as `MUSIC_HIGH_QUALITY`(4).
+   * - To set the audio scenario, call the \ref IRtcEngine::initialize "initialize"
+   * method and pass value in the `audioScenario` member in the RtcEngineContext struct.
+   *
+   * @param profile The audio profile, such as the sample rate, bitrate and codec type: #AUDIO_PROFILE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioProfile(AUDIO_PROFILE_TYPE profile) = 0;
+  /**
+   * Set the audio scenario.
+   *
+   * @param scenario The audio scenario: #AUDIO_SCENARIO_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioScenario(AUDIO_SCENARIO_TYPE scenario) = 0;
+  /**
+   * Enables or disables the local audio capture.
+   *
+   * The audio function is enabled by default. This method disables or re-enables the
+   * local audio function, that is, to stop or restart local audio capture and
+   * processing.
+   *
+   * This method does not affect receiving or playing the remote audio streams,
+   * and `enableLocalAudio` (false) is applicable to scenarios where the user wants
+   * to receive remote audio streams without sending any audio stream to other users
+   * in the channel.
+   *
+   * @param enabled Determines whether to disable or re-enable the local audio function:
+   * - true: (Default) Re-enable the local audio function, that is, to start local
+   * audio capture and processing.
+   * - false: Disable the local audio function, that is, to stop local audio
+   * capture and processing.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableLocalAudio(bool enabled) = 0;
+
+  /**
+   Stops or resumes sending the local audio stream.
+
+   After calling this method successfully, the SDK triggers the
+   \ref IRtcEngineEventHandler::onRemoteAudioStateChanged "onRemoteAudioStateChanged"
+   callback with the following parameters:
+   - REMOTE_AUDIO_STATE_STOPPED(0) and REMOTE_AUDIO_REASON_REMOTE_MUTED(5).
+   - REMOTE_AUDIO_STATE_DECODING(2) and REMOTE_AUDIO_REASON_REMOTE_UNMUTED(6).
+
+   @note
+   - When `mute` is set as `true`, this method does not disable the
+   microphone, which does not affect any ongoing recording.
+   - If you call \ref IRtcEngine::setChannelProfile "setChannelProfile" after
+   this method, the SDK resets whether or not to mute the local audio
+   according to the channel profile and user role. Therefore, we recommend
+   calling this method after the `setChannelProfile` method.
+
+   @param mute Determines whether to send or stop sending the local audio stream:
+   - true: Stop sending the local audio stream.
+   - false: (Default) Send the local audio stream.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int muteLocalAudioStream(bool mute) = 0;
+
+  /**
+   Stops or resumes receiving all remote audio stream.
+
+   This method works for all remote users that join or will join a channel
+   using the `joinChannel` method. It is
+   equivalent to the `autoSubscribeAudio` member in the ChannelMediaOptions
+   class.
+   - Ensure that you call this method after joining a channel.
+   - If you call muteAllRemoteAudioStreams(true) after joining a channel, the
+   local use stops receiving any audio stream from any user in the channel,
+   including any user who joins the channel after you call this method.
+   - If you call muteAllRemoteAudioStreams(true) after leaving a channel, the
+   local user does not receive any audio stream the next time the user joins a
+   channel.
+
+   After you successfully call muteAllRemoteAudioStreams(true), you can take
+   the following actions:
+   - To resume receiving all remote audio streams, call
+   muteAllRemoteAudioStreams(false).
+   - To resume receiving the audio stream of a specified user, call
+   muteRemoteAudioStream(uid, false), where uid is the ID of the user whose
+   audio stream you want to resume receiving.
+
+   @note
+   - The result of calling this method is affected by calling
+   \ref IRtcEngine::enableAudio "enableAudio" and
+   \ref IRtcEngine::disableAudio "disableAudio". Settings in
+   muteAllRemoteAudioStreams stop taking effect if either of the following occurs:
+     - Calling `enableAudio` after muteAllRemoteAudioStreams(true).
+     - Calling `disableAudio` after muteAllRemoteAudioStreams(false).
+   - This method only works for the channel created by calling
+   `joinChannel`. To set whether to receive remote
+   audio streams for a specific channel, Agora recommends using
+   `autoSubscribeAudio` in the ChannelMediaOptions class.
+   @param mute Whether to stop receiving remote audio streams:
+   - true: Stop receiving any remote audio stream.
+   - false: (Default) Resume receiving all remote audio streams.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int muteAllRemoteAudioStreams(bool mute) = 0;
+
+  /**
+   * Determines whether to receive all remote audio streams by default.
+   *
+   * @deprecated This method is deprecated. To set whether to receive remote
+   * audio streams by default, call
+   * \ref IRtcEngine::muteAllRemoteAudioStreams "muteAllRemoteAudioStreams"
+   * before calling `joinChannel`
+   *
+   * Use this method to set whether to receive audio streams of subsequent peer
+   * users. Agora recommends calling it before joining a channel.
+   *
+   * A successful call of setDefaultMuteAllRemoteAudioStreams(true) results in
+   * that the local user not receiving any audio stream after joining a channel.
+   * @param mute Whether to receive remote audio streams by default:
+   * - true: Do not receive any remote audio stream by default.
+   * - false: (Default) Receive remote audio streams by default.
+   *
+   * @return int
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDefaultMuteAllRemoteAudioStreams(bool mute) __deprecated = 0;
+
+  /**
+   * Stops or resumes receiving the audio stream of a specified user.
+   *
+   * @note
+   * You can call this method before or after joining a channel. If a user
+   * leaves a channel, the settings in this method become invalid.
+   *
+   * @param uid The ID of the specified user.
+   * @param mute Whether to stop receiving the audio stream of the specified user:
+   * - true: Stop receiving the audio stream of the specified user.
+   * - false: (Default) Resume receiving the audio stream of the specified user.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int muteRemoteAudioStream(uid_t uid, bool mute) = 0;
+
+  /**
+   * Stops or resumes sending the local video stream.
+   *
+   * @param mute Determines whether to send or stop sending the local video stream:
+   * - true: Stop sending the local video stream.
+   * - false: (Default) Send the local video stream.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int muteLocalVideoStream(bool mute) = 0;
+
+  /**
+   * Disables or re-enables the local video capture.
+   *
+   * Once you enable the video using \ref enableVideo "enableVideo", the local video is enabled
+   * by default. This method disables or re-enables the local video capture.
+   *
+   * `enableLocalVideo(false)` applies to scenarios when the user wants to watch the remote video
+   * without sending any video stream to the other user.
+   *
+   * @note
+   * Call this method after `enableVideo`. Otherwise, this method may not work properly.
+   *
+   * @param enabled Determines whether to disable or re-enable the local video, including
+   * the capturer, renderer, and sender:
+   * - true:  (Default) Re-enable the local video.
+   * - false: Disable the local video. Once the local video is disabled, the remote
+   * users can no longer receive the video stream of this user, while this user
+   * can still receive the video streams of other remote users. When you set
+   * `enabled` as `false`, this method does not require a local camera.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableLocalVideo(bool enabled) = 0;
+
+  /** Stops or resumes receiving all remote video streams.
+
+   This method works for all remote users that join or will join a channel
+   using the `joinChannel` method. It is
+   equivalent to the `autoSubscribeVideo` member in the ChannelMediaOptions
+   class.
+   - Ensure that you call this method after joining a channel.
+   - If you call muteAllRemoteVideoStreams(true) after joining a channel, the
+   local use stops receiving any video stream from any user in the channel,
+   including any user who joins the channel after you call this method.
+   - If you call muteAllRemoteVideoStreams(true) after leaving a channel,
+   the local user does not receive any video stream the next time the user
+   joins a channel.
+
+   After you successfully call muteAllRemoteVideoStreams(true), you can take
+   the following actions:
+   - To resume receiving all remote video streams, call
+   muteAllRemoteVideoStreams(false).
+   - To resume receiving the video stream of a specified user, call
+   muteRemoteVideoStream(uid, false), where uid is the ID of the user whose
+   video stream you want to resume receiving.
+
+   @note
+   - The result of calling this method is affected by calling
+   \ref IRtcEngine::enableVideo "enableVideo" and
+   \ref IRtcEngine::disableVideo "disableVideo". Settings in
+   muteAllRemoteVideoStreams stop taking effect if either of the following occurs:
+     - Calling `enableVideo` after muteAllRemoteVideoStreams(true).
+     - Calling `disableVideo` after muteAllRemoteVideoStreams(false).
+   - This method only works for the channel created by calling `joinChannel`.
+   To set whether to receive remote audio streams for a specific channel, Agora
+   recommends using `autoSubscribeVideo` in the ChannelMediaOptions class.
+   @param mute Whether to stop receiving remote video streams:
+   - true: Stop receiving any remote video stream.
+   - false: (Default) Resume receiving all remote video streams.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int muteAllRemoteVideoStreams(bool mute) = 0;
+
+  /**
+   Determines whether to receive all remote video streams by default.
+
+   @deprecated This method is deprecated. To set whether to receive remote
+   video streams by default, call
+   \ref IRtcEngine::muteAllRemoteVideoStreams "muteAllRemoteVideoStreams"
+   before calling `joinChannel`.
+
+   Use this method to set whether to receive video streams of subsequent peer
+   users. Agora recommends calling it before joining a channel.
+
+   A successful call of setDefaultMuteAllRemoteVideoStreams(true) results in
+   that the local user not receiving any video stream after joining a channel.
+
+   @param mute Whether to receive remote video streams by default:
+   - true: Do not receive any remote video stream by default.
+   - false: (Default) Receive remote video streams by default.
+   @return int
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setDefaultMuteAllRemoteVideoStreams(bool mute) __deprecated = 0;
+
+  /**
+   * Sets the default stream type of the remote video if the remote user has enabled dual-stream.
+   *
+   * @param streamType Sets the default video stream type: #VIDEO_STREAM_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRemoteDefaultVideoStreamType(VIDEO_STREAM_TYPE streamType) = 0;
+
+  /**
+   * Stops or resumes receiving the video stream of a specified user.
+   *
+   * @note
+   * You can call this method before or after joining a channel. If a user
+   * leaves a channel, the settings in this method become invalid.
+   *
+   * @param uid The ID of the specified user.
+   * @param mute Whether to stop receiving the video stream of the specified user:
+   * - true: Stop receiving the video stream of the specified user.
+   * - false: (Default) Resume receiving the video stream of the specified user.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int muteRemoteVideoStream(uid_t uid, bool mute) = 0;
+
+  /**
+   * Sets the remote video stream type.
+   *
+   * If the remote user has enabled the dual-stream mode, by default the SDK receives the high-stream video by
+   * Call this method to switch to the low-stream video.
+   *
+   * @note
+   * This method applies to scenarios where the remote user has enabled the dual-stream mode using
+   * \ref enableDualStreamMode "enableDualStreamMode"(true) before joining the channel.
+   *
+   * @param uid ID of the remote user sending the video stream.
+   * @param streamType Sets the video stream type: #VIDEO_STREAM_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRemoteVideoStreamType(uid_t uid, VIDEO_STREAM_TYPE streamType) = 0;
+
+  /**
+   * Sets the remote video subscription options
+   *
+   *
+   * @param uid ID of the remote user sending the video stream.
+   * @param options Sets the video subscription options.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRemoteVideoSubscriptionOptions(uid_t uid, const VideoSubscriptionOptions &options) = 0;
+
+  /**
+   * Sets the blocklist of subscribe remote stream audio.
+   *
+   * @param uidList The id list of users whose audio you do not want to subscribe to.
+   * @param uidNumber The number of uid in uidList.
+   *
+   * @note
+   * If uid is in uidList, the remote user's audio will not be subscribed,
+   * even if muteRemoteAudioStream(uid, false) and muteAllRemoteAudioStreams(false) are operated.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSubscribeAudioBlocklist(uid_t* uidList, int uidNumber) = 0;
+
+  /**
+   * Sets the allowlist of subscribe remote stream audio.
+   *
+   * @param uidList The id list of users whose audio you want to subscribe to.
+   * @param uidNumber The number of uid in uidList.
+   *
+   * @note
+   * If uid is in uidList, the remote user's audio will be subscribed,
+   * even if muteRemoteAudioStream(uid, true) and muteAllRemoteAudioStreams(true) are operated.
+   *
+   * If a user is in the blocklist and allowlist at the same time, only the blocklist takes effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSubscribeAudioAllowlist(uid_t* uidList, int uidNumber) = 0;
+
+  /**
+   * Sets the blocklist of subscribe remote stream video.
+   *
+   * @param uidList The id list of users whose video you do not want to subscribe to.
+   * @param uidNumber The number of uid in uidList.
+   *
+   * @note
+   * If uid is in uidList, the remote user's video will not be subscribed,
+   * even if muteRemoteVideoStream(uid, false) and muteAllRemoteVideoStreams(false) are operated.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSubscribeVideoBlocklist(uid_t* uidList, int uidNumber) = 0;
+
+  /**
+   * Sets the allowlist of subscribe remote stream video.
+   *
+   * @param uidList The id list of users whose video you want to subscribe to.
+   * @param uidNumber The number of uid in uidList.
+   *
+   * @note
+   * If uid is in uidList, the remote user's video will be subscribed,
+   * even if muteRemoteVideoStream(uid, true) and muteAllRemoteVideoStreams(true) are operated.
+   *
+   * If a user is in the blocklist and allowlist at the same time, only the blocklist takes effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setSubscribeVideoAllowlist(uid_t* uidList, int uidNumber) = 0;
+
+  /**
+   * Enables the `onAudioVolumeIndication` callback to report on which users are speaking
+   * and the speakers' volume.
+   *
+   * Once the \ref IRtcEngineEventHandler::onAudioVolumeIndication "onAudioVolumeIndication"
+   * callback is enabled, the SDK returns the volume indication in the at the time interval set
+   * in `enableAudioVolumeIndication`, regardless of whether any user is speaking in the channel.
+   *
+   * @param interval Sets the time interval between two consecutive volume indications:
+   * - <= 0: Disables the volume indication.
+   * - > 0: Time interval (ms) between two consecutive volume indications,
+   * and should be integral multiple of 200 (less than 200 will be set to 200).
+   * @param smooth The smoothing factor that sets the sensitivity of the audio volume
+   * indicator. The value range is [0, 10]. The greater the value, the more sensitive the
+   * indicator. The recommended value is 3.
+   * @param reportVad
+   * - `true`: Enable the voice activity detection of the local user. Once it is enabled, the `vad` parameter of the
+   * `onAudioVolumeIndication` callback reports the voice activity status of the local user.
+   * - `false`: (Default) Disable the voice activity detection of the local user. Once it is disabled, the `vad` parameter
+   * of the `onAudioVolumeIndication` callback does not report the voice activity status of the local user, except for
+   * the scenario where the engine automatically detects the voice activity of the local user.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAudioVolumeIndication(int interval, int smooth, bool reportVad) = 0;
+
+  /** Starts an audio recording.
+
+  The SDK allows recording during a call, which supports either one of the
+  following two formats:
+
+  - .wav: Large file size with high sound fidelity
+  - .aac: Small file size with low sound fidelity
+
+  Ensure that the directory to save the recording file exists and is writable.
+  This method is usually called after the joinChannel() method.
+  The recording automatically stops when the leaveChannel() method is
+  called.
+
+  @param filePath Full file path of the recording file. The string of the file
+  name is in UTF-8 code.
+  @param quality Sets the audio recording quality: #AUDIO_RECORDING_QUALITY_TYPE.
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startAudioRecording(const char* filePath,
+                                  AUDIO_RECORDING_QUALITY_TYPE quality) = 0;
+  /** Starts an audio recording.
+
+  The SDK allows recording during a call, which supports either one of the
+  following two formats:
+
+  - .wav: Large file size with high sound fidelity
+  - .aac: Small file size with low sound fidelity
+
+  Ensure that the directory to save the recording file exists and is writable.
+  This method is usually called after the joinChannel() method.
+  The recording automatically stops when the leaveChannel() method is
+  called.
+
+  @param filePath Full file path of the recording file. The string of the file
+  name is in UTF-8 code.
+  @param sampleRate Sample rate, value should be 16000, 32000, 44100, or 48000.
+  @param quality Sets the audio recording quality: #AUDIO_RECORDING_QUALITY_TYPE.
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startAudioRecording(const char* filePath,
+                                  int sampleRate,
+                                  AUDIO_RECORDING_QUALITY_TYPE quality) = 0;
+
+  /** Starts an audio recording.
+
+  The SDK allows recording during a call, which supports either one of the
+  following two formats:
+
+  - .wav: Large file size with high sound fidelity
+  - .aac: Small file size with low sound fidelity
+
+  Ensure that the directory to save the recording file exists and is writable.
+  This method is usually called after the joinChannel() method.
+  The recording automatically stops when the leaveChannel() method is
+  called.
+
+  @param config Audio recording config.
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startAudioRecording(const AudioRecordingConfiguration& config) = 0;
+
+  /** register encoded audio frame observer
+   @return
+  - 0: Success.
+  - < 0: Failure.
+   */
+  virtual int registerAudioEncodedFrameObserver(const AudioEncodedFrameObserverConfig& config,  IAudioEncodedFrameObserver *observer) = 0;
+
+  /** Stops the audio recording on the client.
+
+  The recording automatically stops when the leaveChannel() method is called.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int stopAudioRecording() = 0;
+
+    /**
+   * Creates a media player source object and return its pointer. If full featured
+   * media player source is supported, it will create it, or it will create a simple
+   * media player.
+   *
+   * @return
+   * - The pointer to \ref rtc::IMediaPlayerSource "IMediaPlayerSource",
+   *   if the method call succeeds.
+   * - The empty pointer NULL, if the method call fails.
+   */
+  virtual agora_refptr<IMediaPlayer> createMediaPlayer() = 0;
+
+  /**
+   * Destroy a media player source instance.
+   * If a media player source instance is destroyed, the video and audio of it cannot
+   * be published.
+   *
+   * @param media_player The pointer to \ref rtc::IMediaPlayerSource.
+   *
+   * @return
+   * - >0: The id of media player source instance.
+   * - < 0: Failure.
+   */
+  virtual int destroyMediaPlayer(agora_refptr<IMediaPlayer> media_player) = 0;
+
+  /**
+   * Creates a media recorder object and return its pointer.
+   *
+   * @param info The RecorderStreamInfo object. It contains the user ID and the channel name.
+   * 
+   * @return
+   * - The pointer to \ref rtc::IMediaRecorder "IMediaRecorder",
+   *   if the method call succeeds.
+   * - The empty pointer NULL, if the method call fails.
+   */
+  virtual agora_refptr<IMediaRecorder> createMediaRecorder(const RecorderStreamInfo& info) = 0;
+
+  /**
+   * Destroy a media recorder object.
+   *
+   * @param mediaRecorder The pointer to \ref rtc::IMediaRecorder.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int destroyMediaRecorder(agora_refptr<IMediaRecorder> mediaRecorder) = 0;
+
+  /** Starts playing and mixing the music file.
+
+  This method mixes the specified local audio file with the audio stream from
+  the microphone. You can choose whether the other user can hear the local
+  audio playback and specify the number of playback loops. This method also
+  supports online music playback.
+
+  After calling this method successfully, the SDK triggers the
+
+  \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (PLAY)
+  callback on the local client.
+  When the audio mixing file playback finishes after calling this method, the
+  SDK triggers the
+  \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (STOPPED)
+  callback on the local client.
+
+  @note
+  - Call this method after joining a channel, otherwise issues may occur.
+  - If the local audio mixing file does not exist, or if the SDK does not
+  support the file format or cannot access the music file URL, the SDK returns
+  #WARN_AUDIO_MIXING_OPEN_ERROR (701).
+  - If you want to play an online music file, ensure that the time interval
+  between calling this method is more than 100 ms, or the
+  `AUDIO_MIXING_ERROR_TOO_FREQUENT_CALL(702)` error code occurs.
+
+  @param filePath Pointer to the absolute path (including the suffixes of the
+  filename) of the local or online audio file to mix, for example, c:/music/au
+  dio.mp4. Supported audio formats: 3GP, ASF, ADTS, AVI, MP3, MP4, MPEG-4,
+  SAMI, and WAVE.
+  @param loopback Sets which user can hear the audio mixing:
+  - true: Only the local user can hear the audio mixing.
+  - false: Both users can hear the audio mixing.
+
+  @param cycle Sets the number of playback loops:
+  - Positive integer: Number of playback loops.
+  - `-1`: Infinite playback loops.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startAudioMixing(const char* filePath, bool loopback, int cycle) = 0;
+
+  /** Starts playing and mixing the music file.
+
+  This method mixes the specified local audio file with the audio stream from
+  the microphone. You can choose whether the other user can hear the local
+  audio playback and specify the number of playback loops. This method also
+  supports online music playback.
+
+  After calling this method successfully, the SDK triggers the
+
+  \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (PLAY)
+  callback on the local client.
+  When the audio mixing file playback finishes after calling this method, the
+  SDK triggers the
+  \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (STOPPED)
+  callback on the local client.
+
+  @note
+  - Call this method after joining a channel, otherwise issues may occur.
+  - If the local audio mixing file does not exist, or if the SDK does not
+  support the file format or cannot access the music file URL, the SDK returns
+  #WARN_AUDIO_MIXING_OPEN_ERROR (701).
+  - If you want to play an online music file, ensure that the time interval
+  between calling this method is more than 100 ms, or the
+  `AUDIO_MIXING_ERROR_TOO_FREQUENT_CALL(702)` error code occurs.
+
+  @param filePath Pointer to the absolute path (including the suffixes of the
+  filename) of the local or online audio file to mix, for example, c:/music/au
+  dio.mp4. Supported audio formats: 3GP, ASF, ADTS, AVI, MP3, MP4, MPEG-4,
+  SAMI, and WAVE.
+  @param loopback Sets which user can hear the audio mixing:
+  - true: Only the local user can hear the audio mixing.
+  - false: Both users can hear the audio mixing.
+
+  @param cycle Sets the number of playback loops:
+  - Positive integer: Number of playback loops.
+  - `-1`: Infinite playback loops.
+
+  @param startPos The playback position (ms) of the music file.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int startAudioMixing(const char* filePath, bool loopback, int cycle, int startPos) = 0;
+
+  /** Stops playing and mixing the music file.
+
+  Call this method when you are in a channel.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int stopAudioMixing() = 0;
+
+  /** Pauses playing and mixing the music file.
+
+  Call this method when you are in a channel.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int pauseAudioMixing() = 0;
+
+  /** Resumes playing and mixing the music file.
+
+  Call this method when you are in a channel.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int resumeAudioMixing() = 0;
+
+  /** Select audio track for the music file.
+
+  Call this method when you are in a channel.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int selectAudioTrack(int index) = 0;
+  /** Get audio track count of the music file.
+
+   Call this method when you are in a channel.
+
+   @return
+   - &ge; 0: Audio track count of the music file, if the method call is successful.
+   - < 0: Failure.
+   */
+  virtual int getAudioTrackCount() = 0;
+
+  /** Adjusts the volume during audio mixing.
+
+  Call this method when you are in a channel.
+
+  @note This method does not affect the volume of audio effect file playback
+  invoked by the \ref IRtcEngine::playEffect "playEffect" method.
+
+  @param volume The audio mixing volume. The value ranges between 0 and 100
+  (default).
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int adjustAudioMixingVolume(int volume) = 0;
+
+  /** Adjusts the audio mixing volume for publishing (for remote users).
+   @note Call this method when you are in a channel.
+   @param volume Audio mixing volume for publishing. The value ranges between 0 and 100 (default).
+   @return
+    - 0: Success.
+    - < 0: Failure.
+   */
+  virtual int adjustAudioMixingPublishVolume(int volume) = 0;
+
+  /** Retrieves the audio mixing volume for publishing.
+   This method helps troubleshoot audio volume related issues.
+   @note Call this method when you are in a channel.
+   @return
+    - &ge; 0: The audio mixing volume for publishing, if this method call succeeds. The value range is [0,100].
+    - < 0: Failure.
+   */
+  virtual int getAudioMixingPublishVolume() = 0;
+
+  /** Adjusts the audio mixing volume for local playback.
+   @note Call this method when you are in a channel.
+   @param volume Audio mixing volume for local playback. The value ranges between 0 and 100 (default).
+   @return
+    - 0: Success.
+    - < 0: Failure.
+   */
+  virtual int adjustAudioMixingPlayoutVolume(int volume) = 0;
+
+  /** Retrieves the audio mixing volume for local playback.
+   This method helps troubleshoot audio volume related issues.
+   @note Call this method when you are in a channel.
+   @return
+    - &ge; 0: The audio mixing volume, if this method call succeeds. The value range is [0,100].
+    - < 0: Failure.
+   */
+  virtual int getAudioMixingPlayoutVolume() = 0;
+
+  /** Gets the duration (ms) of the music file.
+
+   Call this API when you are in a channel.
+
+   @return
+   - Returns the audio mixing duration, if the method call is successful.
+   - < 0: Failure.
+   */
+  virtual int getAudioMixingDuration() = 0;
+
+  /** Gets the playback position (ms) of the music file.
+
+   Call this method when you are in a channel.
+
+   @return
+   - &ge; 0: The current playback position of the audio mixing, if this method
+   call succeeds.
+   - < 0: Failure.
+   */
+  virtual int getAudioMixingCurrentPosition() = 0;
+
+  /** Sets the playback position of the music file to a different starting
+   position (the default plays from the beginning).
+
+   @param pos The playback starting position (ms) of the audio mixing file.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setAudioMixingPosition(int pos /*in ms*/) = 0;
+
+  /** In dual-channel music files, different audio data can be stored on the left and right channels.
+   * According to actual needs, you can set the channel mode as the original mode,
+   * the left channel mode, the right channel mode or the mixed mode
+
+   @param mode The mode of channel mode
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setAudioMixingDualMonoMode(media::AUDIO_MIXING_DUAL_MONO_MODE mode) = 0;
+
+  /** Sets the pitch of the local music file.
+   *
+   * When a local music file is mixed with a local human voice, call this method to set the pitch of the local music file only.
+   *
+   * @note Call this method after calling \ref IRtcEngine::startAudioMixing "startAudioMixing" and
+   * receiving the \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (AUDIO_MIXING_STATE_PLAYING) callback.
+   *
+   * @param pitch Sets the pitch of the local music file by chromatic scale. The default value is 0,
+   * which means keeping the original pitch. The value ranges from -12 to 12, and the pitch value between
+   * consecutive values is a chromatic value. The greater the absolute value of this parameter, the
+   * higher or lower the pitch of the local music file.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioMixingPitch(int pitch) = 0;
+
+  /**
+   * Sets the playback speed of the current music file.
+   *
+   * @note Call this method after calling \ref IRtcEngine::startAudioMixing(const char*,bool,bool,int,int) "startAudioMixing" [2/2]
+   * and receiving the \ref IRtcEngineEventHandler::onAudioMixingStateChanged "onAudioMixingStateChanged" (AUDIO_MIXING_STATE_PLAYING) callback.
+   *
+   * @param speed The playback speed. Agora recommends that you limit this value to between 50 and 400, defined as follows:
+   * - 50: Half the original speed.
+   * - 100: The original speed.
+   * - 400: 4 times the original speed.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioMixingPlaybackSpeed(int speed) = 0;
+  
+  /**
+   * Gets the volume of audio effects.
+   *
+   * @return
+   * - &ge; 0: The volume of audio effects. The value ranges between 0 and 100 (original volume).
+   * - < 0: Failure.
+   */
+  virtual int getEffectsVolume() = 0;
+  /** Sets the volume of audio effects.
+   *
+   * @param volume The volume of audio effects. The value ranges between 0
+   * and 100 (original volume).
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setEffectsVolume(int volume) = 0;
+  /** Preloads a specified audio effect.
+   *
+   * This method preloads only one specified audio effect into the memory each time
+   * it is called. To preload multiple audio effects, call this method multiple times.
+   *
+   * After preloading, you can call \ref IRtcEngine::playEffect "playEffect"
+   * to play the preloaded audio effect or call
+   * \ref IRtcEngine::playAllEffects "playAllEffects" to play all the preloaded
+   * audio effects.
+   *
+   * @note
+   * - To ensure smooth communication, limit the size of the audio effect file.
+   * - Agora recommends calling this method before joining the channel.
+   *
+   * @param soundId The ID of the audio effect.
+   * @param filePath The absolute path of the local audio effect file or the URL
+   * of the online audio effect file. Supported audio formats: mp3, mp4, m4a, aac,
+   * 3gp, mkv, and wav.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int preloadEffect(int soundId, const char* filePath, int startPos = 0) = 0;
+  /** Plays a specified audio effect.
+   *
+   * After calling \ref IRtcEngine::preloadEffect "preloadEffect", you can call
+   * this method to play the specified audio effect for all users in
+   * the channel.
+   *
+   * This method plays only one specified audio effect each time it is called.
+   * To play multiple audio effects, call this method multiple times.
+   *
+   * @note
+   * - Agora recommends playing no more than three audio effects at the same time.
+   * - The ID and file path of the audio effect in this method must be the same
+   * as that in the \ref IRtcEngine::preloadEffect "preloadEffect" method.
+   *
+   * @param soundId The ID of the audio effect.
+   * @param filePath The absolute path of the local audio effect file or the URL
+   * of the online audio effect file. Supported audio formats: mp3, mp4, m4a, aac,
+   * 3gp, mkv, and wav.
+   * @param loopCount The number of times the audio effect loops:
+   * - `-1`: Play the audio effect in an indefinite loop until
+   * \ref IRtcEngine::stopEffect "stopEffect" or
+   * \ref IRtcEngine::stopAllEffects "stopAllEffects"
+   * - `0`: Play the audio effect once.
+   * - `1`: Play the audio effect twice.
+   * @param pitch The pitch of the audio effect. The value ranges between 0.5 and 2.0.
+   * The default value is `1.0` (original pitch). The lower the value, the lower the pitch.
+   * @param pan The spatial position of the audio effect. The value ranges between -1.0 and 1.0:
+   * - `-1.0`: The audio effect displays to the left.
+   * - `0.0`: The audio effect displays ahead.
+   * - `1.0`: The audio effect displays to the right.
+   * @param gain The volume of the audio effect. The value ranges between 0 and 100.
+   * The default value is `100` (original volume). The lower the value, the lower
+   * the volume of the audio effect.
+   * @param publish Sets whether to publish the audio effect to the remote:
+   * - true: Publish the audio effect to the remote.
+   * - false: (Default) Do not publish the audio effect to the remote.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int playEffect(int soundId, const char* filePath, int loopCount, double pitch, double pan, int gain, bool publish = false, int startPos = 0) = 0;
+  /** Plays all audio effects.
+   *
+   * After calling \ref IRtcEngine::preloadEffect "preloadEffect" multiple times
+   * to preload multiple audio effects into the memory, you can call this
+   * method to play all the specified audio effects for all users in
+   * the channel.
+   *
+   * @param loopCount The number of times the audio effect loops:
+   * - `-1`: Play the audio effect in an indefinite loop until
+   * \ref IRtcEngine::stopEffect "stopEffect" or
+   * \ref IRtcEngine::stopAllEffects "stopAllEffects"
+   * - `0`: Play the audio effect once.
+   * - `1`: Play the audio effect twice.
+   * @param pitch The pitch of the audio effect. The value ranges between 0.5 and 2.0.
+   * The default value is `1.0` (original pitch). The lower the value, the lower the pitch.
+   * @param pan The spatial position of the audio effect. The value ranges between -1.0 and 1.0:
+   * - `-1.0`: The audio effect displays to the left.
+   * - `0.0`: The audio effect displays ahead.
+   * - `1.0`: The audio effect displays to the right.
+   * @param gain The volume of the audio effect. The value ranges between 0 and 100.
+   * The default value is `100` (original volume). The lower the value, the lower
+   * the volume of the audio effect.
+   * @param publish Sets whether to publish the audio effect to the remote:
+   * - true: Publish the audio effect to the remote.
+   * - false: (Default) Do not publish the audio effect to the remote.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int playAllEffects(int loopCount, double pitch, double pan, int gain, bool publish = false) = 0;
+
+  /** Gets the volume of the specified audio effect.
+   *
+   * @param soundId The ID of the audio effect.
+   *
+   * @return
+   * - &ge; 0: The volume of the specified audio effect. The value ranges
+   * between 0 and 100 (original volume).
+   * - < 0: Failure.
+   */
+  virtual int getVolumeOfEffect(int soundId) = 0;
+
+  /** Sets the volume of the specified audio effect.
+   *
+   * @param soundId The ID of the audio effect.
+   * @param volume The volume of the specified audio effect. The value ranges
+   * between 0 and 100 (original volume).
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVolumeOfEffect(int soundId, int volume) = 0;
+  /** Pauses playing the specified audio effect.
+   *
+   * @param soundId The ID of the audio effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pauseEffect(int soundId) = 0;
+  /** Pauses playing audio effects.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int pauseAllEffects() = 0;
+  /** Resumes playing the specified audio effect.
+   *
+   * @param soundId The ID of the audio effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int resumeEffect(int soundId) = 0;
+  /** Resumes playing audio effects.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int resumeAllEffects() = 0;
+  /** Stops playing the specified audio effect.
+   *
+   * @param soundId The ID of the audio effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopEffect(int soundId) = 0;
+  /** Stops playing audio effects.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopAllEffects() = 0;
+  /** Releases the specified preloaded audio effect from the memory.
+   *
+   * @param soundId The ID of the audio effect.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unloadEffect(int soundId) = 0;
+  /** Releases preloaded audio effects from the memory.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unloadAllEffects() = 0;
+  /**
+   * Gets the duration of the audio effect file.
+   * @note
+   * - Call this method after joining a channel.
+   * - For the audio file formats supported by this method, see [What formats of audio files does the Agora RTC SDK support](https://docs.agora.io/en/faq/audio_format).
+   *
+   * @param filePath The absolute path or URL address (including the filename extensions)
+   * of the music file. For example: `C:\music\audio.mp4`.
+   * When you access a local file on Android, Agora recommends passing a URI address or the path starts
+   * with `/assets/` in this parameter.
+   *
+   * @return
+   * - &ge; 0: A successful method call. Returns the total duration (ms) of
+   * the specified audio effect file.
+   * - < 0: Failure.
+   *  - `-22(ERR_RESOURCE_LIMITED)`: Cannot find the audio effect file. Please
+   * set a correct `filePath`.
+   */
+  virtual int getEffectDuration(const char* filePath) = 0;
+  /**
+   * Sets the playback position of an audio effect file.
+   * After a successful setting, the local audio effect file starts playing at the specified position.
+   *
+   * @note Call this method after \ref IRtcEngine::playEffect(int,const char*,int,double,double,int,bool,int) "playEffect" .
+   *
+   * @param soundId Audio effect ID. Ensure that this parameter is set to the
+   * same value as in \ref IRtcEngine::playEffect(int,const char*,int,double,double,int,bool,int) "playEffect" .
+   * @param pos The playback position (ms) of the audio effect file.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *  - `-22(ERR_RESOURCE_LIMITED)`: Cannot find the audio effect file. Please
+   * set a correct `soundId`.
+   */
+  virtual int setEffectPosition(int soundId, int pos) = 0;
+  /**
+   * Gets the playback position of the audio effect file.
+   * @note Call this method after \ref IRtcEngine::playEffect(int,const char*,int,double,double,int,bool,int) "playEffect" .
+   *
+   * @param soundId Audio effect ID. Ensure that this parameter is set to the
+   * same value as in \ref IRtcEngine::playEffect(int,const char*,int,double,double,int,bool,int) "playEffect" .
+   *
+   * @return
+   * - &ge; 0: A successful method call. Returns the playback position (ms) of
+   * the specified audio effect file.
+   * - < 0: Failure.
+   *  - `-22(ERR_RESOURCE_LIMITED)`: Cannot find the audio effect file. Please
+   * set a correct `soundId`.
+   */
+  virtual int getEffectCurrentPosition(int soundId) = 0;
+  /** Enables/Disables stereo panning for remote users.
+
+   Ensure that you call this method before joinChannel to enable stereo panning for remote users so that the local user can track the position of a remote user by calling \ref agora::rtc::IRtcEngine::setRemoteVoicePosition "setRemoteVoicePosition".
+
+   @param enabled Sets whether or not to enable stereo panning for remote users:
+   - true: enables stereo panning.
+   - false: disables stereo panning.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int enableSoundPositionIndication(bool enabled) = 0;
+
+  /** Sets the sound position and gain of a remote user.
+
+   When the local user calls this method to set the sound position of a remote user, the sound difference between the left and right channels allows the local user to track the real-time position of the remote user, creating a real sense of space. This method applies to massively multiplayer online games, such as Battle Royale games.
+
+   @note
+   - For this method to work, enable stereo panning for remote users by calling the \ref agora::rtc::IRtcEngine::enableSoundPositionIndication "enableSoundPositionIndication" method before joining a channel.
+   - This method requires hardware support. For the best sound positioning, we recommend using a wired headset.
+   - Ensure that you call this method after joining a channel.
+
+   @param uid The ID of the remote user.
+   @param pan The sound position of the remote user. The value ranges from -1.0 to 1.0:
+   - 0.0: the remote sound comes from the front.
+   - -1.0: the remote sound comes from the left.
+   - 1.0: the remote sound comes from the right.
+   @param gain Gain of the remote user. The value ranges from 0.0 to 100.0. The default value is 100.0 (the original gain of the remote user). The smaller the value, the less the gain.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setRemoteVoicePosition(uid_t uid, double pan, double gain) = 0;
+
+  /** enable spatial audio
+
+   @param enabled enable/disable spatial audio:
+   - true: enable spatial audio.
+   - false: disable spatial audio.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int enableSpatialAudio(bool enabled) = 0;
+
+  /** Sets remote user parameters for spatial audio
+
+   @param uid The ID of the remote user.
+   @param param spatial audio parameters: SpatialAudioParams.
+
+   @return int
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setRemoteUserSpatialAudioParams(uid_t uid, const agora::SpatialAudioParams& params) = 0;
+
+  /** Sets an SDK preset voice beautifier effect.
+   *
+   * Call this method to set an SDK preset voice beautifier effect for the local user who sends an
+   * audio stream. After setting a voice beautifier effect, all users in the channel can hear the
+   * effect.
+   *
+   * You can set different voice beautifier effects for different scenarios. See *Set the Voice
+   * Beautifier and Audio Effects*.
+   *
+   * To achieve better audio effect quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `scenario` parameter to
+   * `AUDIO_SCENARIO_GAME_STREAMING(3)` and the `profile` parameter to
+   * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before
+   * calling this method.
+   *
+   * @note
+   * - You can call this method either before or after joining a channel.
+   * - Do not set the `profile` parameter of \ref IRtcEngine::setAudioProfile "setAudioProfile" to
+   * `AUDIO_PROFILE_SPEECH_STANDARD(1)` or `AUDIO_PROFILE_IOT(6)`; otherwise, this method call
+   * fails.
+   * - This method works best with the human voice. Agora does not recommend using this method for
+   * audio containing music.
+   * - After calling this method, Agora recommends not calling the following methods, because they
+   * can override \ref IRtcEngine::setAudioEffectParameters "setAudioEffectParameters":
+   *  - \ref IRtcEngine::setAudioEffectPreset "setAudioEffectPreset"
+   *  - \ref IRtcEngine::setVoiceBeautifierPreset "setVoiceBeautifierPreset"
+   *  - \ref IRtcEngine::setLocalVoicePitch "setLocalVoicePitch"
+   *  - \ref IRtcEngine::setLocalVoiceEqualization "setLocalVoiceEqualization"
+   *  - \ref IRtcEngine::setLocalVoiceReverb "setLocalVoiceReverb"
+   *  - \ref IRtcEngine::setVoiceBeautifierParameters "setVoiceBeautifierParameters"
+   *
+   * @param preset The options for SDK preset voice beautifier effects: #VOICE_BEAUTIFIER_PRESET.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVoiceBeautifierPreset(VOICE_BEAUTIFIER_PRESET preset) = 0;
+
+  /** Sets an SDK preset audio effect.
+   *
+   * Call this method to set an SDK preset audio effect for the local user who sends an audio
+   * stream. This audio effect does not change the gender characteristics of the original voice.
+   * After setting an audio effect, all users in the channel can hear the effect.
+   *
+   * You can set different audio effects for different scenarios. See *Set the Voice Beautifier and
+   * Audio Effects*.
+   *
+   * To achieve better audio effect quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `scenario` parameter to
+   * `AUDIO_SCENARIO_GAME_STREAMING(3)` before calling this method.
+   *
+   * @note
+   * - You can call this method either before or after joining a channel.
+   * - Do not set the profile `parameter` of `setAudioProfile` to `AUDIO_PROFILE_SPEECH_STANDARD(1)`
+   * or `AUDIO_PROFILE_IOT(6)`; otherwise, this method call fails.
+   * - This method works best with the human voice. Agora does not recommend using this method for
+   * audio containing music.
+   * - If you call this method and set the `preset` parameter to enumerators except
+   * `ROOM_ACOUSTICS_3D_VOICE` or `PITCH_CORRECTION`, do not call \ref
+   * IRtcEngine::setAudioEffectParameters "setAudioEffectParameters"; otherwise,
+   * `setAudioEffectParameters` overrides this method.
+   * - After calling this method, Agora recommends not calling the following methods, because they
+   * can override `setAudioEffectPreset`:
+   *  - \ref IRtcEngine::setVoiceBeautifierPreset "setVoiceBeautifierPreset"
+   *  - \ref IRtcEngine::setLocalVoicePitch "setLocalVoicePitch"
+   *  - \ref IRtcEngine::setLocalVoiceEqualization "setLocalVoiceEqualization"
+   *  - \ref IRtcEngine::setLocalVoiceReverb "setLocalVoiceReverb"
+   *  - \ref IRtcEngine::setVoiceBeautifierParameters "setVoiceBeautifierParameters"
+   *
+   * @param preset The options for SDK preset audio effects. See #AUDIO_EFFECT_PRESET.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioEffectPreset(AUDIO_EFFECT_PRESET preset) = 0;
+
+  /** Sets an SDK preset voice conversion.
+   *
+   * Call this method to set an SDK preset voice conversion for the local user who sends an audio
+   * stream. After setting an voice conversion, all users in the channel can hear the effect.
+   *
+   * You can set different voice conversion for different scenarios. See *Set the Voice Beautifier and
+   * Audio Effects*.
+   *
+   * To achieve better voice conversion quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `scenario` parameter to
+   * `AUDIO_SCENARIO_GAME_STREAMING(3)` before calling this method.
+   *
+   * @note
+   * - You can call this method either before or after joining a channel.
+   * - Do not set the profile `parameter` of `setAudioProfile` to `AUDIO_PROFILE_SPEECH_STANDARD(1)`
+   * or `AUDIO_PROFILE_IOT(6)`; otherwise, this method call fails.
+   * - This method works best with the human voice. Agora does not recommend using this method for
+   * audio containing music.
+   * - If you call this method and set the `preset` parameter to enumerators,
+   * - After calling this method, Agora recommends not calling the following methods, because they
+   * can override `setVoiceConversionPreset`:
+   *  - \ref IRtcEngine::setVoiceBeautifierPreset "setVoiceBeautifierPreset"
+   *  - \ref IRtcEngine::setAudioEffectPreset "setAudioEffectPreset"
+   *  - \ref IRtcEngine::setLocalVoicePitch "setLocalVoicePitch"
+   *  - \ref IRtcEngine::setLocalVoiceFormant "setLocalVoiceFormant"
+   *  - \ref IRtcEngine::setLocalVoiceEqualization "setLocalVoiceEqualization"
+   *  - \ref IRtcEngine::setLocalVoiceReverb "setLocalVoiceReverb"
+   *  - \ref IRtcEngine::setVoiceBeautifierParameters "setVoiceBeautifierParameters"
+   *  - \ref IRtcEngine::setAudioEffectParameters "setAudioEffectParameters"
+   *
+   * @param preset The options for SDK preset voice conversion. See #VOICE_CONVERSION_PRESET.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVoiceConversionPreset(VOICE_CONVERSION_PRESET preset) = 0;
+
+  /** Sets parameters for SDK preset audio effects.
+   *
+   * Call this method to set the following parameters for the local user who send an audio stream:
+   * - 3D voice effect: Sets the cycle period of the 3D voice effect.
+   * - Pitch correction effect: Sets the basic mode and tonic pitch of the pitch correction effect.
+   * Different songs have different modes and tonic pitches. Agora recommends bounding this method
+   * with interface elements to enable users to adjust the pitch correction interactively.
+   *
+   * After setting parameters, all users in the channel can hear the relevant effect.
+   *
+   * You can call this method directly or after \ref IRtcEngine::setAudioEffectPreset
+   * "setAudioEffectPreset". If you call this method after \ref IRtcEngine::setAudioEffectPreset
+   * "setAudioEffectPreset", ensure that you set the preset parameter of `setAudioEffectPreset` to
+   * `ROOM_ACOUSTICS_3D_VOICE` or `PITCH_CORRECTION` and then call this method to set the same
+   * enumerator; otherwise, this method overrides `setAudioEffectPreset`.
+   *
+   * @note
+   * - You can call this method either before or after joining a channel.
+   * - To achieve better audio effect quality, Agora recommends calling \ref
+   * IRtcEngine::setAudioProfile "setAudioProfile" and setting the `scenario` parameter to
+   * `AUDIO_SCENARIO_GAME_STREAMING(3)` before calling this method.
+   * - Do not set the `profile` parameter of \ref IRtcEngine::setAudioProfile "setAudioProfile" to
+   * `AUDIO_PROFILE_SPEECH_STANDARD(1)` or `AUDIO_PROFILE_IOT(6)`; otherwise, this method call
+   * fails.
+   * - This method works best with the human voice. Agora does not recommend using this method for
+   * audio containing music.
+   * - After calling this method, Agora recommends not calling the following methods, because they
+   * can override `setAudioEffectParameters`:
+   *  - \ref IRtcEngine::setAudioEffectPreset "setAudioEffectPreset"
+   *  - \ref IRtcEngine::setVoiceBeautifierPreset "setVoiceBeautifierPreset"
+   *  - \ref IRtcEngine::setLocalVoicePitch "setLocalVoicePitch"
+   *  - \ref IRtcEngine::setLocalVoiceEqualization "setLocalVoiceEqualization"
+   *  - \ref IRtcEngine::setLocalVoiceReverb "setLocalVoiceReverb"
+   *  - \ref IRtcEngine::setVoiceBeautifierParameters "setVoiceBeautifierParameters"
+   * @param preset The options for SDK preset audio effects:
+   * - 3D voice effect: `ROOM_ACOUSTICS_3D_VOICE`.
+   *  - Call \ref IRtcEngine::setAudioProfile "setAudioProfile" and set the `profile` parameter to
+   * `AUDIO_PROFILE_MUSIC_STANDARD_STEREO(3)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before
+   * setting this enumerator; otherwise, the enumerator setting does not take effect.
+   *  - If the 3D voice effect is enabled, users need to use stereo audio playback devices to hear
+   * the anticipated voice effect.
+   * - Pitch correction effect: `PITCH_CORRECTION`. To achieve better audio effect quality, Agora
+   * recommends calling \ref IRtcEngine::setAudioProfile "setAudioProfile" and setting the `profile`
+   * parameter to `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or
+   * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)` before setting this enumerator.
+   * @param param1
+   * - If you set `preset` to `ROOM_ACOUSTICS_3D_VOICE`, the `param1` sets the cycle period of the
+   * 3D voice effect. The value range is [1,60] and the unit is a second. The default value is 10
+   * seconds, indicating that the voice moves around you every 10 seconds.
+   * - If you set `preset` to `PITCH_CORRECTION`, `param1` sets the basic mode of the pitch
+   * correction effect:
+   *  - `1`: (Default) Natural major scale.
+   *  - `2`: Natural minor scale.
+   *  - `3`: Japanese pentatonic scale.
+   * @param param2
+   * - If you set `preset` to `ROOM_ACOUSTICS_3D_VOICE`, you need to set `param2` to `0`.
+   * - If you set `preset` to `PITCH_CORRECTION`, `param2` sets the tonic pitch of the pitch
+   * correction effect:
+   *  - `1`: A
+   *  - `2`: A#
+   *  - `3`: B
+   *  - `4`: (Default) C
+   *  - `5`: C#
+   *  - `6`: D
+   *  - `7`: D#
+   *  - `8`: E
+   *  - `9`: F
+   *  - `10`: F#
+   *  - `11`: G
+   *  - `12`: G#
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioEffectParameters(AUDIO_EFFECT_PRESET preset, int param1, int param2) = 0;
+
+  /** Sets parameters for SDK preset voice beautifier effects.
+   *
+   * Call this method to set a gender characteristic and a reverberation effect for the singing
+   * beautifier effect. This method sets parameters for the local user who sends an audio stream.
+   *
+   * After you call this method successfully, all users in the channel can hear the relevant effect.
+   *
+   * To achieve better audio effect quality, before you call this method, Agora recommends calling
+   * \ref IRtcEngine::setAudioProfile "setAudioProfile", and setting the `scenario` parameter as
+   * `AUDIO_SCENARIO_GAME_STREAMING(3)` and the `profile` parameter as
+   * `AUDIO_PROFILE_MUSIC_HIGH_QUALITY(4)` or `AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO(5)`.
+   *
+   * @note
+   * - You can call this method either before or after joining a channel.
+   * - Do not set the `profile` parameter of \ref IRtcEngine::setAudioProfile "setAudioProfile" as
+   * `AUDIO_PROFILE_SPEECH_STANDARD(1)` or `AUDIO_PROFILE_IOT(6)`; otherwise, this method call does
+   * not take effect.
+   * - This method works best with the human voice. Agora does not recommend using this method for
+   * audio containing music.
+   * - After you call this method, Agora recommends not calling the following methods, because they
+   * can override `setVoiceBeautifierParameters`:
+   *    - \ref IRtcEngine::setAudioEffectPreset "setAudioEffectPreset"
+   *    - \ref IRtcEngine::setAudioEffectParameters "setAudioEffectParameters"
+   *    - \ref IRtcEngine::setVoiceBeautifierPreset "setVoiceBeautifierPreset"
+   *    - \ref IRtcEngine::setLocalVoicePitch "setLocalVoicePitch"
+   *    - \ref IRtcEngine::setLocalVoiceEqualization "setLocalVoiceEqualization"
+   *    - \ref IRtcEngine::setLocalVoiceReverb "setLocalVoiceReverb"
+   *
+   * @param preset The options for SDK preset voice beautifier effects:
+   * - `SINGING_BEAUTIFIER`: Singing beautifier effect.
+   * @param param1 The gender characteristics options for the singing voice:
+   * - `1`: A male-sounding voice.
+   * - `2`: A female-sounding voice.
+   * @param param2 The reverberation effects options:
+   * - `1`: The reverberation effect sounds like singing in a small room.
+   * - `2`: The reverberation effect sounds like singing in a large room.
+   * - `3`: The reverberation effect sounds like singing in a hall.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVoiceBeautifierParameters(VOICE_BEAUTIFIER_PRESET preset,
+                                            int param1, int param2) = 0;
+
+  /** Set parameters for SDK preset voice conversion.
+   *
+   * @note
+   * - reserved interface
+   *
+   * @param preset The options for SDK preset audio effects. See #VOICE_CONVERSION_PRESET.
+   * @param param1 reserved.
+   * @param param2 reserved.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setVoiceConversionParameters(VOICE_CONVERSION_PRESET preset,
+                                            int param1, int param2) = 0;
+
+  /** Changes the voice pitch of the local speaker.
+
+  @param pitch The voice pitch. The value ranges between 0.5 and 2.0. The lower
+  the value, the lower the voice pitch. The default value is 1.0 (no change to
+  the local voice pitch).
+
+  @return
+  - 0: Success.
+  - -1: Failure.
+  */
+  virtual int setLocalVoicePitch(double pitch) = 0;
+
+  /** Changes the voice formant ratio for local speaker.
+
+  @param formantRatio The voice formant ratio. The value ranges between -1.0 and 1.0. 
+  The lower the value, the deeper the sound, and the higher the value, the more it 
+  sounds like a child. The default value is 0.0 (the local user's voice will not be changed).
+
+  @return
+  - 0: Success.
+  - -1: Failure.
+  */
+  virtual int setLocalVoiceFormant(double formantRatio) = 0;
+
+  /** Sets the local voice equalization effect.
+
+  @param bandFrequency The band frequency ranging from 0 to 9, representing the
+  respective 10-band center frequencies of the voice effects, including 31, 62,
+  125, 500, 1k, 2k, 4k, 8k, and 16k Hz.
+  @param bandGain  Gain of each band in dB. The value ranges from -15 to 15. The
+  default value is 0.
+  @return
+  - 0: Success.
+  - -1: Failure.
+  */
+  virtual int setLocalVoiceEqualization(AUDIO_EQUALIZATION_BAND_FREQUENCY bandFrequency, int bandGain) = 0;
+
+  /** Sets the local voice reverberation.
+
+  @param reverbKey The reverberation key: #AUDIO_REVERB_TYPE.
+  @param value The value of the reverberation key: #AUDIO_REVERB_TYPE.
+  @return
+  - 0: Success.
+  - -1: Failure.
+  */
+  virtual int setLocalVoiceReverb(AUDIO_REVERB_TYPE reverbKey, int value) = 0;
+  /** Sets preset audio playback effect for remote headphones after remote audio is mixed.
+
+  @param preset The preset key: #HEADPHONE_EQUALIZER_PRESET.
+  - HEADPHONE_EQUALIZER_OFF = 0x00000000 : Turn off the eualizer effect for headphones.
+  - HEADPHONE_EQUALIZER_OVEREAR = 0x04000001 : For over-ear headphones only.
+  - HEADPHONE_EQUALIZER_INEAR = 0x04000002 : For in-ear headphones only.
+  @return
+  - 0: Success.
+  - < 0: Failure.
+    - -1(ERR_FAILED): A general error occurs (no specified reason).
+  */
+  virtual int setHeadphoneEQPreset(HEADPHONE_EQUALIZER_PRESET preset) = 0;
+
+  /** Sets the parameters of audio playback effect for remote headphones after remote audio is mixed.
+
+  @param lowGain The higher the parameter value, the deeper the sound. The value range is [-10,10].
+  @param highGain The higher the parameter value, the sharper the sound. The value range is [-10,10].
+  @return
+  - 0: Success.
+  - < 0: Failure.
+    - -1(ERR_FAILED): A general error occurs (no specified reason).
+  */
+  virtual int setHeadphoneEQParameters(int lowGain, int highGain) = 0;
+
+  /** **DEPRECATED** Specifies an SDK output log file.
+   *
+   * The log file records all log data for the SDK’s operation. Ensure that the
+   * directory for the log file exists and is writable.
+   *
+   * @note
+   * Ensure that you call this method immediately after \ref initialize "initialize",
+   * or the output log may not be complete.
+   *
+   * @param filePath File path of the log file. The string of the log file is in UTF-8.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLogFile(const char* filePath) = 0;
+
+  /**
+   * Sets the output log filter level of the SDK.
+   *
+   * You can use one or a combination of the filters. The log filter level follows the
+   * sequence of `OFF`, `CRITICAL`, `ERROR`, `WARNING`, `INFO`, and `DEBUG`. Choose a filter level
+   * and you will see logs preceding that filter level. For example, if you set the log filter level to
+   * `WARNING`, you see the logs within levels `CRITICAL`, `ERROR`, and `WARNING`.
+   *
+   * @param filter The log filter level:
+   * - `LOG_FILTER_DEBUG(0x80f)`: Output all API logs. Set your log filter as DEBUG
+   * if you want to get the most complete log file.
+   * - `LOG_FILTER_INFO(0x0f)`: Output logs of the CRITICAL, ERROR, WARNING, and INFO
+   * level. We recommend setting your log filter as this level.
+   * - `LOG_FILTER_WARNING(0x0e)`: Output logs of the CRITICAL, ERROR, and WARNING level.
+   * - `LOG_FILTER_ERROR(0x0c)`: Output logs of the CRITICAL and ERROR level.
+   * - `LOG_FILTER_CRITICAL(0x08)`: Output logs of the CRITICAL level.
+   * - `LOG_FILTER_OFF(0)`: Do not output any log.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLogFilter(unsigned int filter) = 0;
+
+  /**
+   * Sets the output log level of the SDK.
+   *
+   * You can set the SDK to ouput the log files of the specified level.
+   *
+   * @param level The log level:
+   * - `LOG_LEVEL_NONE (0x0000)`: Do not output any log file.
+   * - `LOG_LEVEL_INFO (0x0001)`: (Recommended) Output log files of the INFO level.
+   * - `LOG_LEVEL_WARN (0x0002)`: Output log files of the WARN level.
+   * - `LOG_LEVEL_ERROR (0x0004)`: Output log files of the ERROR level.
+   * - `LOG_LEVEL_FATAL (0x0008)`: Output log files of the FATAL level.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLogLevel(commons::LOG_LEVEL level) = 0;
+
+  /**
+   * Sets the log file size (KB).
+   *
+   * The SDK has two log files, each with a default size of 512 KB. If you set
+   * `fileSizeInBytes` as 1024 KB, the SDK outputs log files with a total
+   * maximum size of 2 MB.
+   * If the total size of the log files exceed the set value,
+   * the new output log files overwrite the old output log files.
+   *
+   * @param fileSizeInKBytes The SDK log file size (KB).
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLogFileSize(unsigned int fileSizeInKBytes) = 0;
+
+  /** Upload current log file immediately to server.
+   *  only use this when an error occurs
+   *  block before log file upload success or timeout.
+   *
+   *  @return
+   *  - 0: Success.
+   *  - < 0: Failure.
+   */
+  virtual int uploadLogFile(agora::util::AString& requestId) = 0;
+
+   /** * Write the log to SDK . @technical preview
+   *
+   * You can Write the log to SDK log files of the specified level.
+   *
+   * @param level The log level:
+   * - `LOG_LEVEL_NONE (0x0000)`: Do not output any log file.
+   * - `LOG_LEVEL_INFO (0x0001)`: (Recommended) Output log files of the INFO level.
+   * - `LOG_LEVEL_WARN (0x0002)`: Output log files of the WARN level.
+   * - `LOG_LEVEL_ERROR (0x0004)`: Output log files of the ERROR level.
+   * - `LOG_LEVEL_FATAL (0x0008)`: Output log files of the FATAL level.
+   *
+   *  @return
+   *  - 0: Success.
+   *  - < 0: Failure.
+   */
+  virtual int writeLog(commons::LOG_LEVEL level, const char* fmt, ...) = 0;
+
+  /**
+   * Updates the display mode of the local video view.
+   *
+   * After initializing the local video view, you can call this method to  update its rendering mode.
+   * It affects only the video view that the local user sees, not the published local video stream.
+   *
+   * @note
+   * - Ensure that you have called \ref setupLocalVideo "setupLocalVideo" to initialize the local video
+   * view before this method.
+   * - During a call, you can call this method as many times as necessary to update the local video view.
+   *
+   * @param renderMode Sets the local display mode. See #RENDER_MODE_TYPE.
+   * @param mirrorMode Sets the local mirror mode. See #VIDEO_MIRROR_MODE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode, VIDEO_MIRROR_MODE_TYPE mirrorMode) = 0;
+
+  /**
+   * Updates the display mode of the video view of a remote user.
+   *
+   * After initializing the video view of a remote user, you can call this method to update its
+   * rendering and mirror modes. This method affects only the video view that the local user sees.
+   *
+   * @note
+   * - Ensure that you have called \ref setupRemoteVideo "setupRemoteVideo" to initialize the remote video
+   * view before calling this method.
+   * - During a call, you can call this method as many times as necessary to update the display mode
+   * of the video view of a remote user.
+   *
+   * @param uid ID of the remote user.
+   * @param renderMode Sets the remote display mode. See #RENDER_MODE_TYPE.
+   * @param mirrorMode Sets the mirror type. See #VIDEO_MIRROR_MODE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRemoteRenderMode(uid_t uid, media::base::RENDER_MODE_TYPE renderMode,
+                                  VIDEO_MIRROR_MODE_TYPE mirrorMode) = 0;
+
+  // The following APIs are either deprecated and going to deleted.
+
+  /**
+   * Updates the display mode of the local video view.
+   *
+   * After initializing the local video view, you can call this method to  update its rendering mode.
+   * It affects only the video view that the local user sees, not the published local video stream.
+   *
+   * @note
+   * - Ensure that you have called \ref setupLocalVideo "setupLocalVideo" to initialize the local video
+   * view before this method.
+   * - During a call, you can call this method as many times as necessary to update the local video view.
+   *
+   * @param renderMode Sets the local display mode. See #RENDER_MODE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode) __deprecated = 0;
+
+  /**
+   * Sets the local video mirror mode.
+   *
+   * Use this method before calling the \ref startPreview "startPreview" method, or the mirror mode
+   * does not take effect until you call the `startPreview` method again.
+   * @param mirrorMode Sets the local video mirror mode. See #VIDEO_MIRROR_MODE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLocalVideoMirrorMode(VIDEO_MIRROR_MODE_TYPE mirrorMode) __deprecated = 0;
+
+  /**
+   * Enables or disables the dual video stream mode.
+   *
+   * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+   * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video stream)
+   * video using \ref setRemoteVideoStreamType "setRemoteVideoStreamType".
+   *
+   * @param enabled
+   * - true: Enable the dual-stream mode.
+   * - false: (default) Disable the dual-stream mode.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableDualStreamMode(bool enabled) __deprecated = 0;
+
+  /**
+   * Enables or disables the dual video stream mode.
+   *
+   * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+   * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video stream)
+   * video using \ref setRemoteVideoStreamType "setRemoteVideoStreamType".
+   *
+   * @param enabled
+   * - true: Enable the dual-stream mode.
+   * - false: (default) Disable the dual-stream mode.
+   * @param streamConfig
+   * - The minor stream config
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableDualStreamMode(bool enabled, const SimulcastStreamConfig& streamConfig) __deprecated = 0;
+
+
+  /**
+   * Enables, disables or auto enable the dual video stream mode.
+   *
+   * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+   * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video stream)
+   * video using \ref setRemoteVideoStreamType "setRemoteVideoStreamType".
+   *
+   * @param mode
+   * - The dual stream mode
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDualStreamMode(SIMULCAST_STREAM_MODE mode) = 0;
+
+  /**
+   * Sets the multi-layer video stream configuration.
+   *
+   * If multi-layer is configured, the subscriber can choose to receive the coresponding layer
+   * of video stream using {@link setRemoteVideoStreamType setRemoteVideoStreamType}.
+   *
+   * @param simulcastConfig
+   * - The configuration for multi-layer video stream. It includes seven layers, ranging from
+   *   STREAM_LAYER_1 to STREAM_LOW. A maximum of 3 layers can be enabled simultaneously.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   * @technical preview
+   */
+  virtual int setSimulcastConfig(const SimulcastConfig& simulcastConfig) = 0;
+
+  /**
+   * Enables, disables or auto enable the dual video stream mode.
+   *
+   * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+   * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video stream)
+   * video using \ref setRemoteVideoStreamType "setRemoteVideoStreamType".
+   *
+   * @param mode Dual stream mode: #SIMULCAST_STREAM_MODE.
+   * @param streamConfig Configurations of the low stream: SimulcastStreamConfig.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDualStreamMode(SIMULCAST_STREAM_MODE mode, const SimulcastStreamConfig& streamConfig) = 0;
+
+  /**
+   * Sets the external audio track.
+   *
+   * @note
+   * Ensure that you call this method before joining the channel.
+   *
+   * @param trackId custom audio track id.
+   * @param enabled Determines whether to local playback the external audio track:
+   * - true: Local playback the external audio track.
+   * - false: Local don`t playback the external audio track.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableCustomAudioLocalPlayback(track_id_t trackId, bool enabled) = 0;
+
+  /**
+   * Sets the audio recording format for the
+   * \ref agora::media::IAudioFrameObserver::onRecordAudioFrame "onRecordAudioFrame" callback.
+   *
+   * @param sampleRate The sample rate (Hz) of the audio data returned in the `onRecordAudioFrame` callback, which can set be
+   * as 8000, 16000, 32000, 44100, or 48000.
+   * @param channel The number of audio channels of the audio data returned in the `onRecordAudioFrame` callback, which can
+   * be set as 1 or 2:
+   * - 1: Mono.
+   * - 2: Stereo.
+   * @param mode This mode is deprecated.
+   * @param samplesPerCall not support. Sampling points in the called data returned in
+   * onRecordAudioFrame(). For example, it is usually set as 1024 for stream
+   * pushing.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRecordingAudioFrameParameters(int sampleRate, int channel,
+                                               RAW_AUDIO_FRAME_OP_MODE_TYPE mode,
+                                               int samplesPerCall) = 0;
+
+  /**
+   * Sets the audio playback format for the
+   * \ref agora::media::IAudioFrameObserver::onPlaybackAudioFrame "onPlaybackAudioFrame" callback.
+   *
+   * @param sampleRate Sets the sample rate (Hz) of the audio data returned in the `onPlaybackAudioFrame` callback,
+   * which can set be as 8000, 16000, 32000, 44100, or 48000.
+   * @param channel The number of channels of the audio data returned in the `onPlaybackAudioFrame` callback, which
+   * can be set as 1 or 2:
+   * - 1: Mono
+   * - 2: Stereo
+   * @param mode Deprecated. The use mode of the onPlaybackAudioFrame() callback:
+   * agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE.
+   * @param samplesPerCall not support. Sampling points in the called data returned in
+   * onPlaybackAudioFrame(). For example, it is usually set as 1024 for stream
+   * pushing.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackAudioFrameParameters(int sampleRate, int channel,
+                                              RAW_AUDIO_FRAME_OP_MODE_TYPE mode,
+                                              int samplesPerCall) = 0;
+
+  /**
+   * Sets the mixed audio format for the
+   * \ref agora::media::IAudioFrameObserver::onMixedAudioFrame "onMixedAudioFrame" callback.
+   *
+  * @param sampleRate The sample rate (Hz) of the audio data returned in the `onMixedAudioFrame` callback, which can set
+  * be as 8000, 16000, 32000, 44100, or 48000.
+  * @param channel The number of channels of the audio data in `onMixedAudioFrame` callback, which can be set as 1 or 2:
+  * - 1: Mono
+  * - 2: Stereo
+  * @param samplesPerCall not support. Sampling points in the called data returned in
+  * `onMixedAudioFrame`. For example, it is usually set as 1024 for stream pushing.
+  * @return
+  * - 0: Success.
+  * - < 0: Failure.
+  */
+  virtual int setMixedAudioFrameParameters(int sampleRate, int channel, int samplesPerCall) = 0;
+
+  /**
+   * Sets the audio ear monitoring format for the
+   * \ref agora::media::IAudioFrameObserver::onEarMonitoringAudioFrame "onEarMonitoringAudioFrame" callback.
+   *
+   * @param sampleRate Sets the sample rate (Hz) of the audio data returned in the `onEarMonitoringAudioFrame` callback,
+   * which can set be as 8000, 16000, 32000, 44100, or 48000.
+   * @param channel The number of channels of the audio data returned in the `onEarMonitoringAudioFrame` callback, which
+   * can be set as 1 or 2:
+   * - 1: Mono
+   * - 2: Stereo
+   * @param mode Deprecated. The use mode of the onEarMonitoringAudioFrame() callback:
+   * agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE.
+   * @param samplesPerCall not support. Sampling points in the called data returned in
+   * onEarMonitoringAudioFrame(). For example, it is usually set as 1024 for stream
+   * pushing.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setEarMonitoringAudioFrameParameters(int sampleRate, int channel,
+                                                   RAW_AUDIO_FRAME_OP_MODE_TYPE mode,
+                                                   int samplesPerCall) = 0;
+
+  /**
+   * Sets the audio playback format before mixing in the
+   * \ref agora::media::IAudioFrameObserver::onPlaybackAudioFrameBeforeMixing "onPlaybackAudioFrameBeforeMixing"
+   * callback.
+   *
+   * @param sampleRate The sample rate (Hz) of the audio data returned in
+   * `onPlaybackAudioFrameBeforeMixing`, which can set be as 8000, 16000, 32000, 44100, or 48000.
+   * @param channel Number of channels of the audio data returned in `onPlaybackAudioFrameBeforeMixing`,
+   * which can be set as 1 or 2:
+   * - 1: Mono
+   * - 2: Stereo
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackAudioFrameBeforeMixingParameters(int sampleRate, int channel) = 0;
+
+  /**
+   * Enable the audio spectrum monitor.
+   *
+   * @param intervalInMS Sets the time interval(ms) between two consecutive audio spectrum callback.
+   * The default value is 100. This param should be larger than 10.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableAudioSpectrumMonitor(int intervalInMS = 100) = 0;
+  /**
+   * Disalbe the audio spectrum monitor.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int disableAudioSpectrumMonitor() = 0;
+
+  /**
+   * Registers an audio spectrum observer.
+   *
+   * You need to implement the `IAudioSpectrumObserver` class in this method, and register the following callbacks
+   * according to your scenario:
+   * - \ref agora::media::IAudioSpectrumObserver::onAudioSpectrumComputed "onAudioSpectrumComputed": Occurs when
+   * the SDK receives the audio data and at set intervals.
+   *
+   * @param observer A pointer to the audio spectrum observer: \ref agora::media::IAudioSpectrumObserver
+   * "IAudioSpectrumObserver".
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerAudioSpectrumObserver(agora::media::IAudioSpectrumObserver * observer) = 0;
+  /**
+   * Releases the audio spectrum observer.
+   *
+   * @param observer The pointer to the audio spectrum observer: \ref agora::media::IAudioSpectrumObserver
+   * "IAudioSpectrumObserver".
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int unregisterAudioSpectrumObserver(agora::media::IAudioSpectrumObserver * observer) = 0;
+
+  /** Adjusts the recording volume.
+
+  @param volume The recording volume, which ranges from 0 to 400:
+
+  - 0: Mute the recording volume.
+  - 100: The Original volume.
+  - 400: (Maximum) Four times the original volume with signal clipping
+  protection.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int adjustRecordingSignalVolume(int volume) = 0;
+
+  /**
+   * Mute or resume recording signal volume.
+   *
+   * @param mute Determines whether to mute or resume the recording signal volume.
+   * - true: Mute the recording signal volume.
+   * - false: (Default) Resume the recording signal volume.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int muteRecordingSignal(bool mute) = 0;
+
+  /** Adjusts the playback volume.
+
+  @param volume The playback volume, which ranges from 0 to 400:
+
+  - 0: Mute the recoridng volume.
+  - 100: The Original volume.
+  - 400: (Maximum) Four times the original volume with signal clipping
+  protection.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int adjustPlaybackSignalVolume(int volume) = 0;
+
+  /*
+   * Adjust the playback volume of the user specified by uid.
+   *
+   * You can call this method to adjust the playback volume of the user specified by uid
+   * in call. If you want to adjust playback volume of the multi user, you can call this
+   * this method multi times.
+   *
+   * @note
+   * Please call this method after join channel.
+   * This method adjust the playback volume of specified user.
+   *
+   * @param uid Remote user ID.
+   * @param volume The playback volume of the specified remote user. The value ranges between 0 and 400, including the following:
+   * 0: Mute.
+   * 100: (Default) Original volume.
+   * 400: Four times the original volume with signal-clipping protection.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int adjustUserPlaybackSignalVolume(uid_t uid, int volume) = 0;
+
+  /** Sets the fallback option for the published video stream based on the network conditions.
+
+   If `option` is set as #STREAM_FALLBACK_OPTION_AUDIO_ONLY (2), the SDK will:
+
+   - Disable the upstream video but enable audio only when the network conditions deteriorate and cannot support both video and audio.
+   - Re-enable the video when the network conditions improve.
+
+   When the published video stream falls back to audio only or when the audio-only stream switches back to the video, the SDK triggers the \ref agora::rtc::IRtcEngineEventHandler::onLocalPublishFallbackToAudioOnly "onLocalPublishFallbackToAudioOnly" callback.
+
+   @note
+   - Agora does not recommend using this method for CDN live streaming, because the remote CDN live user will have a noticeable lag when the published video stream falls back to audio only.
+   - Ensure that you call this method before joining a channel.
+
+   @param option Sets the fallback option for the published video stream:
+   - #STREAM_FALLBACK_OPTION_DISABLED (0): (Default) No fallback behavior for the published video stream when the uplink network condition is poor. The stream quality is not guaranteed.
+   - #STREAM_FALLBACK_OPTION_AUDIO_ONLY (2): The published video stream falls back to audio only when the uplink network condition is poor.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setLocalPublishFallbackOption(STREAM_FALLBACK_OPTIONS option) = 0;
+
+  /** Sets the fallback option for the remotely subscribed video stream based on the network conditions.
+
+   The default setting for `option` is #STREAM_FALLBACK_OPTION_VIDEO_STREAM_LOW (1), where the remotely subscribed video stream falls back to the low-stream video (low resolution and low bitrate) under poor downlink network conditions.
+
+   If `option` is set as #STREAM_FALLBACK_OPTION_AUDIO_ONLY (2), the SDK automatically switches the video from a high-stream to a low-stream, or disables the video when the downlink network conditions cannot support both audio and video to guarantee the quality of the audio. The SDK monitors the network quality and restores the video stream when the network conditions improve.
+
+   When the remotely subscribed video stream falls back to audio only or when the audio-only stream switches back to the video stream, the SDK triggers the \ref agora::rtc::IRtcEngineEventHandler::onRemoteSubscribeFallbackToAudioOnly "onRemoteSubscribeFallbackToAudioOnly" callback.
+
+   @note Ensure that you call this method before joining a channel.
+
+   @param  option  Sets the fallback option for the remotely subscribed video stream. See #STREAM_FALLBACK_OPTIONS.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setRemoteSubscribeFallbackOption(STREAM_FALLBACK_OPTIONS option) = 0;
+
+  /** Sets the high priority user list and their fallback level in weak network condition.
+   * @note
+   * - This method can be called before and after joining a channel.
+   * - If a subscriber is set to high priority, this stream only fallback to lower stream after all normal priority users fallback to their fallback level on weak network condition if needed.
+   *
+   * @param uidList The high priority user list.
+   * @param uidNum The size of uidList.
+   * @param option The fallback level of high priority users.
+   *
+   * @return int
+   * - 0 : Success.
+   * - <0 : Failure.
+   */
+  virtual int setHighPriorityUserList(uid_t* uidList, int uidNum, STREAM_FALLBACK_OPTIONS option) = 0;
+
+  /**
+   * Enable/Disable an extension.
+   * By calling this function, you can dynamically enable/disable the extension without changing the pipeline.
+   * For example, enabling/disabling Extension_A means the data will be adapted/bypassed by Extension_A.
+   *
+   * NOTE: For compatibility reasons, if you haven't call registerExtension,
+   * enableExtension will automatically register the specified extension.
+   * We suggest you call registerExtension explicitly.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param extensionInfo The information for extension.
+   * @param enable Whether to enable the extension:
+   * - true: (Default) Enable the extension.
+   * - false: Disable the extension.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableExtension(const char* provider, const char* extension, const ExtensionInfo& extensionInfo, bool enable = true) = 0;
+
+  /**
+   * Sets the properties of an extension.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param extensionInfo The information for extension.
+   * @param key The key of the extension.
+   * @param value The JSON formatted value of the extension key.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExtensionProperty(const char* provider, const char* extension, const ExtensionInfo& extensionInfo, const char* key, const char* value) = 0;
+
+  /**
+   * Gets the properties of an extension.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param extensionInfo The information for extension.
+   * @param key The key of the extension.
+   * @param value The value of the extension key.
+   * @param buf_len Maximum length of the JSON string indicating the extension property.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getExtensionProperty(const char* provider, const char* extension, const ExtensionInfo& extensionInfo, const char* key, char* value, int buf_len)  = 0;
+
+  /** Enables loopback recording.
+   *
+   * If you enable loopback recording, the output of the default sound card is mixed into
+   * the audio stream sent to the other end.
+   *
+   * @note This method is for Windows only.
+   *
+   * @param enabled Sets whether to enable/disable loopback recording.
+   * - true: Enable loopback recording.
+   * - false: (Default) Disable loopback recording.
+   * @param deviceName Pointer to the device name of the sound card. The default value is NULL (the default sound card).
+   * - This method is for macOS and Windows only.
+   * - macOS does not support loopback capturing of the default sound card. If you need to use this method,
+   * please use a virtual sound card and pass its name to the deviceName parameter. Agora has tested and recommends using soundflower.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableLoopbackRecording(bool enabled, const char* deviceName = NULL) = 0;
+
+
+  /** Adjusts the loopback recording volume.
+
+  @param volume The loopback volume, which ranges from 0 to 100:
+
+  - 0: Mute the recoridng volume.
+  - 100: The Original volume.
+  protection.
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int adjustLoopbackSignalVolume(int volume) = 0;
+
+  /** Retrieves the audio volume for recording loopback.
+  @note Call this method when you are in a channel.
+  @return
+  - &ge; 0: The audio volume for loopback, if this method call succeeds. The value range is [0,100].
+  - < 0: Failure.
+  */
+  virtual int getLoopbackRecordingVolume() = 0;
+
+  /**
+   * Enables in-ear monitoring.
+   *
+   * @param enabled Determines whether to enable in-ear monitoring.
+   * - true: Enable.
+   * - false: (Default) Disable.
+   * @param includeAudioFilters The type of the ear monitoring: #EAR_MONITORING_FILTER_TYPE
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableInEarMonitoring(bool enabled, int includeAudioFilters) = 0;
+
+  /**
+   * Sets the volume of the in-ear monitor.
+   *
+   * @param volume Sets the volume of the in-ear monitor. The value ranges
+   * between 0 and 100 (default).
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setInEarMonitoringVolume(int volume) = 0;
+
+#if defined (_WIN32) || defined(__linux__) || defined(__ANDROID__)
+  virtual int loadExtensionProvider(const char* path, bool unload_after_use = false) = 0;
+#endif
+
+  /**
+   * Sets the provider property of an extension.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param key The key of the extension.
+   * @param value The JSON formatted value of the extension key.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExtensionProviderProperty(const char* provider, const char* key, const char* value) = 0;
+
+  /**
+   * Registers an extension. Normally you should call this function immediately after engine initialization.
+   * Once an extension is registered, the SDK will automatically create and add it to the pipeline.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param type The source type of the extension, e.g. PRIMARY_CAMERA_SOURCE. The default is UNKNOWN_MEDIA_SOURCE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerExtension(const char* provider, const char* extension, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * Enable/Disable an extension.
+   * By calling this function, you can dynamically enable/disable the extension without changing the pipeline.
+   * For example, enabling/disabling Extension_A means the data will be adapted/bypassed by Extension_A.
+   *
+   * NOTE: For compatibility reasons, if you haven't call registerExtension,
+   * enableExtension will automatically register the specified extension.
+   * We suggest you call registerExtension explicitly.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param enable Whether to enable the extension:
+   * - true: (Default) Enable the extension.
+   * - false: Disable the extension.
+   * @param type The source type of the extension, e.g. PRIMARY_CAMERA_SOURCE. The default is UNKNOWN_MEDIA_SOURCE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableExtension(const char* provider, const char* extension, bool enable=true, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * Sets the properties of an extension.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param key The key of the extension.
+   * @param value The JSON formatted value of the extension key.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExtensionProperty(
+      const char* provider, const char* extension,
+      const char* key, const char* value, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /**
+   * Gets the properties of an extension.
+   *
+   * @param provider The name of the extension provider, e.g. agora.io.
+   * @param extension The name of the extension, e.g. agora.beauty.
+   * @param key The key of the extension.
+   * @param value The value of the extension key.
+   * @param buf_len Maximum length of the JSON string indicating the extension property.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getExtensionProperty(
+      const char* provider, const char* extension,
+      const char* key, char* value, int buf_len, agora::media::MEDIA_SOURCE_TYPE type = agora::media::UNKNOWN_MEDIA_SOURCE) = 0;
+
+  /** Sets the camera capture configuration.
+   * @note Call this method before enabling the local camera.
+   * That said, you can call this method before calling \ref IRtcEngine::joinChannel "joinChannel",
+   * \ref IRtcEngine::enableVideo "enableVideo", or \ref IRtcEngine::enableLocalVideo "enableLocalVideo",
+   * depending on which method you use to turn on your local camera.
+   *
+   * @param config Sets the camera capturer configuration. See CameraCapturerConfiguration.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setCameraCapturerConfiguration(const CameraCapturerConfiguration& config) = 0;
+
+  /**
+   * Get an custom video track id created by internal,which could used to publish or preview
+   *
+   * @return
+   * - > 0: the useable video track id.
+   * - < 0: Failure.
+   */
+  virtual video_track_id_t createCustomVideoTrack() = 0;
+
+  /**
+   * Get an custom encoded video track id created by internal,which could used to publish or preview
+   *
+   * @return
+   * - > 0: the useable video track id.
+   * - < 0: Failure.
+   */
+  virtual video_track_id_t createCustomEncodedVideoTrack(const SenderOptions& sender_option) = 0;
+
+  /**
+   * destroy a created custom video track id
+   *
+   * @param video_track_id The video track id which was created by createCustomVideoTrack
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int destroyCustomVideoTrack(video_track_id_t video_track_id) = 0;
+
+  /**
+   * destroy a created custom encoded video track id
+   *
+   * @param video_track_id The video track id which was created by createCustomEncodedVideoTrack
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int destroyCustomEncodedVideoTrack(video_track_id_t video_track_id) = 0;
+
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  /**
+   * Switches between front and rear cameras.
+   *
+   * @note This method applies to Android and iOS only.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int switchCamera() = 0;
+
+  /**
+   * Checks whether the camera zoom function is supported.
+   *
+   * @return
+   * - true: The camera zoom function is supported.
+   * - false: The camera zoom function is not supported.
+   */
+  virtual bool isCameraZoomSupported() = 0;
+
+  /**
+   * Checks whether the camera face detect is supported.
+   *
+   * @return
+   * - true: The camera face detect is supported.
+   * - false: The camera face detect is not supported.
+   */
+  virtual bool isCameraFaceDetectSupported() = 0;
+
+  /**
+   * Checks whether the camera flash function is supported.
+   *
+   * @return
+   * - true: The camera flash function is supported.
+   * - false: The camera flash function is not supported.
+   */
+  virtual bool isCameraTorchSupported() = 0;
+
+  /**
+   * Checks whether the camera manual focus function is supported.
+   *
+   * @return
+   * - true: The camera manual focus function is supported.
+   * - false: The camera manual focus function is not supported.
+   */
+  virtual bool isCameraFocusSupported() = 0;
+
+  /**
+   * Checks whether the camera auto focus function is supported.
+   *
+   * @return
+   * - true: The camera auto focus function is supported.
+   * - false: The camera auto focus function is not supported.
+   */
+  virtual bool isCameraAutoFocusFaceModeSupported() = 0;
+
+  /**
+   * Sets the camera zoom ratio.
+   *
+   * @param factor The camera zoom factor. It ranges from 1.0 to the maximum zoom
+   * supported by the camera.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setCameraZoomFactor(float factor) = 0;
+
+  /**
+   * Sets the camera face detection.
+   *
+   * @param enabled The camera face detection enabled.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableFaceDetection(bool enabled) = 0;
+
+  /**
+   * Gets the maximum zoom ratio supported by the camera.
+   * @return The maximum zoom ratio supported by the camera.
+   */
+  virtual float getCameraMaxZoomFactor() = 0;
+
+  /**
+   * Sets the manual focus position.
+   *
+   * @param positionX The horizontal coordinate of the touch point in the view.
+   * @param positionY The vertical coordinate of the touch point in the view.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+  */
+  virtual int setCameraFocusPositionInPreview(float positionX, float positionY) = 0;
+
+  /**
+   * Enables the camera flash.
+   *
+   * @param isOn Determines whether to enable the camera flash.
+   * - true: Enable the flash.
+   * - false: Do not enable the flash.
+   */
+  virtual int setCameraTorchOn(bool isOn) = 0;
+
+  /**
+   * Enables the camera auto focus face function.
+   *
+   * @param enabled Determines whether to enable the camera auto focus face mode.
+   * - true: Enable the auto focus face function.
+   * - false: Do not enable the auto focus face function.
+   */
+  virtual int setCameraAutoFocusFaceModeEnabled(bool enabled) = 0;
+
+  /** Checks whether the camera exposure function is supported.
+   *
+   * Ensure that you call this method after the camera starts, for example, by calling `startPreview` or `joinChannel`.
+   *
+   * @since v2.3.2.
+   * @return
+   * <ul>
+   *     <li>true: The device supports the camera exposure function.</li>
+   *     <li>false: The device does not support the camera exposure function.</li>
+   * </ul>
+   */
+  virtual bool isCameraExposurePositionSupported() = 0;
+
+  /** Sets the camera exposure position.
+   *
+   * Ensure that you call this method after the camera starts, for example, by calling `startPreview` or `joinChannel`.
+   *
+   * A successful setCameraExposurePosition method call triggers the {@link IRtcEngineEventHandler#onCameraExposureAreaChanged onCameraExposureAreaChanged} callback on the local client.
+   * @since v2.3.2.
+   * @param positionXinView The horizontal coordinate of the touch point in the view.
+   * @param positionYinView The vertical coordinate of the touch point in the view.
+   *
+   * @return
+   * <ul>
+   *     <li>0: Success.</li>
+   *     <li>< 0: Failure.</li>
+   * </ul>
+   */
+  virtual int setCameraExposurePosition(float positionXinView, float positionYinView) = 0;
+
+  /**
+  * Returns whether exposure value adjusting is supported by the current device.
+  * Exposure compensation is in auto exposure mode.
+  * @since v4.2.2
+  * @note
+  * This method only supports Android and iOS.
+  * This interface returns valid values only after the device is initialized.
+  *
+  * @return
+  * - true: exposure value adjusting is supported.
+  * - false: exposure value adjusting is not supported or device is not initialized.
+  */
+  virtual bool isCameraExposureSupported() = 0;
+
+  /**
+   * Sets the camera exposure ratio.
+   * @since v4.2.2
+   * @param factor The camera zoom factor. The recommended camera exposure factor ranging from -8.0 to 8.0 for iOS,
+   *        and -20.0 to 20.0 for Android.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setCameraExposureFactor(float factor) = 0;
+
+#if defined(__APPLE__)
+  /**
+   * Checks whether the camera auto exposure function is supported.
+   *
+   * @return
+   * - true: The camera auto exposure function is supported.
+   * - false: The camera auto exposure function is not supported.
+   */
+  virtual bool isCameraAutoExposureFaceModeSupported() = 0;
+
+
+  /**
+   * Enables the camera auto exposure face function.
+   *
+   * @param enabled Determines whether to enable the camera auto exposure face mode.
+   * - true: Enable the auto exposure face function.
+   * - false: Do not enable the auto exposure face function.
+   */
+  virtual int setCameraAutoExposureFaceModeEnabled(bool enabled) = 0;
+
+  /**
+   * set camera stabilization mode.If open stabilization mode, fov will be smaller and capture latency will be longer.
+   *
+   * @param mode specifies the camera stabilization mode.
+   */
+  virtual int setCameraStabilizationMode(CAMERA_STABILIZATION_MODE mode) = 0;
+#endif
+
+  /** Sets the default audio route (for Android and iOS only).
+
+   Most mobile phones have two audio routes: an earpiece at the top, and a
+   speakerphone at the bottom. The earpiece plays at a lower volume, and the
+   speakerphone at a higher volume.
+
+   When setting the default audio route, you determine whether audio playback
+   comes through the earpiece or speakerphone when no external audio device is
+   connected.
+
+   Depending on the scenario, Agora uses different default audio routes:
+   - Voice call: Earpiece
+   - Audio broadcast: Speakerphone
+   - Video call: Speakerphone
+   - Video broadcast: Speakerphone
+
+   Call this method before, during, or after a call, to change the default
+   audio route. When the audio route changes, the SDK triggers the
+   \ref IRtcEngineEventHandler::onAudioRoutingChanged "onAudioRoutingChanged"
+   callback.
+
+   @note The system audio route changes when an external audio device, such as
+   a headphone or a Bluetooth audio device, is connected. See *Principles for changing the audio route*.
+
+   @param defaultToSpeaker Whether to set the speakerphone as the default audio
+   route:
+   - true: Set the speakerphone as the default audio route.
+   - false: Do not set the speakerphone as the default audio route.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setDefaultAudioRouteToSpeakerphone(bool defaultToSpeaker) = 0;
+
+  /** Enables/Disables the speakerphone temporarily (for Android and iOS only).
+
+   When the audio route changes, the SDK triggers the
+   \ref IRtcEngineEventHandler::onAudioRoutingChanged "onAudioRoutingChanged"
+   callback.
+
+   You can call this method before, during, or after a call. However, Agora
+   recommends calling this method only when you are in a channel to change
+   the audio route temporarily.
+
+   @note This method sets the audio route temporarily. Plugging in or
+   unplugging a headphone, or the SDK re-enabling the audio device module
+   (ADM) to adjust the media volume in some scenarios relating to audio, leads
+   to a change in the audio route. See *Principles for changing the audio
+   route*.
+
+   @param speakerOn Whether to set the speakerphone as the temporary audio
+   route:
+   - true: Set the speakerphone as the audio route temporarily. (For iOS only:
+   calling setEnableSpeakerphone(true) does not change the audio route to the
+   speakerphone if a headphone or a Bluetooth audio device is connected.)
+   - false: Do not set the speakerphone as the audio route.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setEnableSpeakerphone(bool speakerOn) = 0;
+
+  /** Checks whether the speakerphone is enabled (for Android and iOS only).
+
+   @return
+   - true: The speakerphone is enabled, and the audio plays from the speakerphone.
+   - false: The speakerphone is not enabled, and the audio plays from devices
+   other than the speakerphone. For example, the headset or earpiece.
+   */
+  virtual bool isSpeakerphoneEnabled() = 0;
+
+    /** Select preferred route for android communication mode
+
+   @param route The preferred route. For example, when a Bluetooth headset is connected,
+   you can use this API to switch the route to a wired headset.
+   @return meanless, route switch result is pass through CallbackOnRoutingChanged
+   */
+  virtual int setRouteInCommunicationMode(int route) = 0;
+
+#endif  // __ANDROID__ || (__APPLE__ && TARGET_OS_IOS)
+
+#if defined(__APPLE__)
+  /**
+   * Checks whether the center stage is supported. Use this method after starting the camera.
+   *
+   * @return
+   * - true: The center stage is supported.
+   * - false: The center stage is not supported.
+   */
+  virtual bool isCameraCenterStageSupported() = 0;
+
+  /** Enables the camera Center Stage.
+   * @param enabled enable Center Stage:
+   * - true: Enable Center Stage.
+   * - false: Disable Center Stage.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableCameraCenterStage(bool enabled) = 0;
+#endif
+
+#if defined(_WIN32) || (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE)
+  /** Get \ref ScreenCaptureSourceInfo list including available windows and screens.
+    *
+    * @param thumbSize Set expected size for thumb, image will be scaled accordingly. For windows, SIZE is defined in windef.h.
+    * @param iconSize Set expected size for icon, image will be scaled accordingly. For windows, SIZE is defined in windef.h.
+    * @param includeScreen Determines whether to include screens info.
+    * - true: sources will have screens info
+    * - false: source will only have windows info
+    * @return
+    * - IScreenCaptureSourceList* a pointer to an instance of IScreenCaptureSourceList
+    */
+  virtual IScreenCaptureSourceList* getScreenCaptureSources(const SIZE& thumbSize, const SIZE& iconSize, const bool includeScreen) = 0;
+#endif // _WIN32 || (__APPLE__ && !TARGET_OS_IPHONE && TARGET_OS_MAC)
+#if (defined(__APPLE__) && TARGET_OS_IOS)
+  /** Sets the operational permission of the SDK on the audio session.
+   *
+   * The SDK and the app can both configure the audio session by default. If
+   * you need to only use the app to configure the audio session, this method
+   * restricts the operational permission of the SDK on the audio session.
+   *
+   * You can call this method either before or after joining a channel. Once
+   * you call this method to restrict the operational permission of the SDK
+   * on the audio session, the restriction takes effect when the SDK needs to
+   * change the audio session.
+   *
+   * @note
+   * - This method is for iOS only.
+   * - This method does not restrict the operational permission of the app on
+   * the audio session.
+   *
+   * @param restriction The operational permission of the SDK on the audio session.
+   * See #AUDIO_SESSION_OPERATION_RESTRICTION. This parameter is in bit mask
+   * format, and each bit corresponds to a permission.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAudioSessionOperationRestriction(AUDIO_SESSION_OPERATION_RESTRICTION restriction) = 0;
+#endif // __APPLE__ && TARGET_OS_IOS
+
+#if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IPHONE && TARGET_OS_MAC)
+
+  /** Shares the whole or part of a screen by specifying the display ID.
+
+  @note This method applies to macOS only.
+
+  @param  displayId The display ID of the screen to be shared. This parameter
+  specifies which screen you want to share. For information on how to get the
+  displayId, see the advanced guide: Share the Screen.
+  @param regionRect (Optional) Sets the relative location of the region to the
+  screen. NIL means sharing the whole screen. See Rectangle.
+  If the specified region overruns the screen, the SDK shares only the region
+  within it; if you set width or height as 0, the SDK shares the whole screen.
+  @param captureParams Sets the screen sharing encoding parameters. See
+  ScreenCaptureParameters.
+
+  @return
+  - 0: Success.
+  - < 0: Failure:
+  - ERR_INVALID_ARGUMENT (2): The argument is invalid.
+  - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when try to start screen capture.
+  */
+  virtual int startScreenCaptureByDisplayId(uint32_t displayId, const Rectangle& regionRect,
+                                            const ScreenCaptureParameters& captureParams) = 0;
+
+#endif  // __APPLE__ && TARGET_OS_MAC && !TARGET_OS_IPHONE
+
+#if defined(_WIN32)
+  /**
+   * Shares the whole or part of a screen by specifying the screen rect.
+   *
+   * @deprecated This method is deprecated, use \ref IRtcEngine::startScreenCaptureByDisplayId "startScreenCaptureByDisplayId" instead. Agora strongly recommends using `startScreenCaptureByDisplayId` if you need to start screen sharing on a device connected to another display.
+   *
+   * @note This method applies to Windows only.
+   *
+   * @param screenRect Sets the relative location of the screen to the virtual
+   * screen. For information on how to get screenRect, see the advanced guide:
+   * Share the Screen.
+   * @param regionRect (Optional) Sets the relative location of the region to the
+   * screen. NULL means sharing the whole screen. See Rectangle.
+   * If the specified region overruns the screen, the SDK shares only the region
+   * within it; if you set width or height as 0, the SDK shares the whole screen.
+   * @param captureParams Sets the screen sharing encoding parameters. See
+   * ScreenCaptureParameters.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure:
+   * - ERR_INVALID_ARGUMENT (2): The argument is invalid.
+   * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when try to start screen capture.
+  */
+  virtual int startScreenCaptureByScreenRect(const Rectangle& screenRect,
+                                             const Rectangle& regionRect,
+                                             const ScreenCaptureParameters& captureParams) __deprecated = 0;
+#endif
+
+#if defined(__ANDROID__)
+  /**
+   * Gets the the Audio device Info
+   * @return
+   * - 0: Success.
+   * - < 0: Failure..
+   */
+  virtual int getAudioDeviceInfo(DeviceInfo& deviceInfo) = 0;
+#endif  // __ANDROID__
+
+#if defined(_WIN32) || (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE)
+
+ /** Shares the whole or part of a window by specifying the window ID.
+  *
+  * @param windowId The ID of the window to be shared. For information on how to
+  * get the windowId, see the advanced guide *Share Screen*.
+  * @param regionRect (Optional) The relative location of the region to the
+  * window. NULL means sharing the whole window. See Rectangle. If the
+  * specified region overruns the window, the SDK shares only the region within
+  * it; if you set width or height as 0, the SDK shares the whole window.
+  * @param captureParams The window sharing encoding parameters. See
+  * ScreenCaptureParameters.
+  *
+  * @return
+  * - 0: Success.
+  * - < 0: Failure:
+  * - ERR_INVALID_ARGUMENT (2): The argument is invalid.
+  * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when try to start screen capture.
+  */
+  virtual int startScreenCaptureByWindowId(view_t windowId, const Rectangle& regionRect,
+                                           const ScreenCaptureParameters& captureParams) = 0;
+
+  /**
+   * Sets the content hint for screen sharing.
+   *
+   * A content hint suggests the type of the content being shared, so that the SDK applies different
+   * optimization algorithm to different types of content.
+   *
+   * @param contentHint Sets the content hint for screen sharing: #VIDEO_CONTENT_HINT.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure:
+   * - ERR_NOT_SUPPORTED (4): unable to set screencapture content hint
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when set screen capture content hint.
+   */
+  virtual int setScreenCaptureContentHint(VIDEO_CONTENT_HINT contentHint) = 0;
+
+  /**
+   * Updates the screen sharing region.
+   *
+   * @param regionRect Sets the relative location of the region to the screen or
+   * window. NULL means sharing the whole screen or window. See Rectangle.
+   * If the specified region overruns the screen or window, the SDK shares only
+   * the region within it; if you set width or height as 0, the SDK shares the
+   * whole screen or window.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure:
+   * - ERR_NOT_SUPPORTED (4): unable to update screen capture region
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when update screen capture regoin.
+   */
+  virtual int updateScreenCaptureRegion(const Rectangle& regionRect) = 0;
+
+  /**
+   * Updates the screen sharing parameters.
+   *
+   * @param captureParams Sets the screen sharing encoding parameters: ScreenCaptureParameters.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   * - ERR_NOT_SUPPORTED (4): unable to update screen capture parameters
+   * - ERR_INVALID_ARGUMENT (2): The argument is invalid.
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when update screen capture parameters.
+   */
+  virtual int updateScreenCaptureParameters(const ScreenCaptureParameters& captureParams) = 0;
+#endif // _WIN32 || (__APPLE__ && !TARGET_OS_IPHONE && TARGET_OS_MAC)
+
+#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS)
+  /**
+   * Starts screen sharing.
+   *
+   * @param captureParams The configuration of the screen sharing. See {@link
+   *     ScreenCaptureParameters ScreenCaptureParameters}.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startScreenCapture(const ScreenCaptureParameters2& captureParams) = 0;
+
+  /**
+   * Updates the screen sharing configuration.
+   *
+   * @param captureParams The configuration of the screen sharing. See {@link
+   *     ScreenCaptureParameters ScreenCaptureParameters}.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int updateScreenCapture(const ScreenCaptureParameters2& captureParams) = 0;
+    
+   /**
+   * Queries the ability of screen sharing to support the maximum frame rate.
+   *
+   * @since v4.2.0
+   * 
+   * @return
+   * - 0: support 15 fps, Low devices.
+   * - 1: support 30 fps, Usually low - to mid-range devices.
+   * - 2: support 60 fps, Advanced devices.
+   * - < 0: Failure.
+   */
+  virtual int queryScreenCaptureCapability() = 0;
+
+  /**
+   * Query all focal attributes supported by the camera.
+   * 
+   * @param focalLengthInfos The camera supports the collection of focal segments.Ensure the size of array is not less than 8.
+   * 
+   * @param size The camera supports the size of the focal segment set. Ensure the size is not less than 8.
+   * 
+   * @return
+   * - 0: Success.
+   * - < 0: Failure..
+   */
+  virtual int queryCameraFocalLengthCapability(agora::rtc::FocalLengthInfo* focalLengthInfos, int& size) = 0;
+
+#if defined(__ANDROID__)
+  /**
+   * Set screen sharing MediaProjection.
+   *
+   * When screen capture stopped, the SDK will automatically release the MediaProjection internally.
+   *
+   * @param mediaProjection MediaProjection is an Android class that provides access to screen capture and recording capabiliies.
+   *
+   * @note
+   * It is mainly used in some specific scenarios, such as iot custom devices, or child process screen sharing. 
+   * MediaProjection is not easily obtained or for other reasons.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setExternalMediaProjection(void* mediaProjection) = 0;
+#endif
+#endif
+
+#if defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__)
+  /**
+   * Sets the screen sharing scenario.
+   *
+   *
+   * When you start screen sharing or window sharing, you can call this method to set the screen sharing scenario. The SDK adjusts the video quality and experience of the sharing according to the scenario.
+   *
+   *
+   * @param screenScenario The screen sharing scenario. See #SCREEN_SCENARIO_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   * - ERR_NOT_SUPPORTED (4): unable to set screencapture scenario
+   * - ERR_FAILED (1): A general error occurs (no specified reason).
+   * - ERR_NOT_INITIALIZED (7): You have not initialized IRtcEngine when set screencapture scenario.
+   */
+  virtual int setScreenCaptureScenario(SCREEN_SCENARIO_TYPE screenScenario) = 0;
+  
+  /**
+   * Stops the screen sharing.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopScreenCapture() = 0;
+#endif  // _WIN32 || (__APPLE__ && !TARGET_OS_IPHONE && TARGET_OS_MAC) || __ANDROID__
+
+  /**
+   * Gets the current call ID.
+   *
+   * When a user joins a channel on a client, a `callId` is generated to identify
+   * the call.
+   *
+   * After a call ends, you can call `rate` or `complain` to gather feedback from the customer.
+   * These methods require a `callId` parameter. To use these feedback methods, call the this
+   * method first to retrieve the `callId` during the call, and then pass the value as an
+   * argument in the `rate` or `complain` method after the call ends.
+   *
+   * @param callId The reference to the call ID.
+   * @return
+   * - The call ID if the method call is successful.
+   * - < 0: Failure.
+  */
+  virtual int getCallId(agora::util::AString& callId) = 0;
+
+  /**
+   * Allows a user to rate the call.
+   *
+   * It is usually called after the call ends.
+   *
+   * @param callId The call ID retrieved from the \ref getCallId "getCallId" method.
+   * @param rating The rating of the call between 1 (the lowest score) to 5 (the highest score).
+   * @param description (Optional) The description of the rating. The string length must be less than
+   * 800 bytes.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int rate(const char* callId, int rating, const char* description) = 0;  // 0~10
+
+  /**
+   * Allows a user to complain about the call quality.
+   *
+   * This method is usually called after the call ends.
+   *
+   * @param callId The call ID retrieved from the `getCallId` method.
+   * @param description (Optional) The description of the complaint. The string length must be less than
+   * 800 bytes.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int complain(const char* callId, const char* description) = 0;
+
+  /** Publishes the local stream without transcoding to a specified CDN live RTMP address.  (CDN live only.)
+
+    * The SDK returns the result of this method call in the \ref IRtcEngineEventHandler::onStreamPublished "onStreamPublished" callback.
+
+    * The \ref agora::rtc::IRtcEngine::startRtmpStreamWithoutTranscoding "startRtmpStreamWithoutTranscoding" method call triggers the \ref agora::rtc::IRtcEngineEventHandler::onRtmpStreamingStateChanged "onRtmpStreamingStateChanged" callback on the local client to report the state of adding a local stream to the CDN.
+    * @note
+    * - Ensure that the user joins the channel before calling this method.
+    * - This method adds only one stream RTMP URL address each time it is called.
+    * - The RTMP URL address must not contain special characters, such as Chinese language characters.
+    * - This method applies to Live Broadcast only.
+
+    * @param url The CDN streaming URL in the RTMP format. The maximum length of this parameter is 1024 bytes.
+
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    *   - #ERR_INVALID_ARGUMENT (2): The RTMP URL address is NULL or has a string length of 0.
+    *   - #ERR_NOT_INITIALIZED (7): You have not initialized the RTC engine when publishing the stream.
+    *   - #ERR_ALREADY_IN_USE (19): This streaming URL is already in use. Use a new streaming URL for CDN streaming.
+    */
+  virtual int startRtmpStreamWithoutTranscoding(const char* url) = 0;
+
+  /** Publishes the local stream with transcoding to a specified CDN live RTMP address.  (CDN live only.)
+
+    * The SDK returns the result of this method call in the \ref IRtcEngineEventHandler::onStreamPublished "onStreamPublished" callback.
+
+    * The \ref agora::rtc::IRtcEngine::startRtmpStreamWithTranscoding "startRtmpStreamWithTranscoding" method call triggers the \ref agora::rtc::IRtcEngineEventHandler::onRtmpStreamingStateChanged "onRtmpStreamingStateChanged" callback on the local client to report the state of adding a local stream to the CDN.
+    * @note
+    * - Ensure that the user joins the channel before calling this method.
+    * - This method adds only one stream RTMP URL address each time it is called.
+    * - The RTMP URL address must not contain special characters, such as Chinese language characters.
+    * - This method applies to Live Broadcast only.
+
+    * @param url The CDN streaming URL in the RTMP format. The maximum length of this parameter is 1024 bytes.
+    * @param transcoding Sets the CDN live audio/video transcoding settings.  See LiveTranscoding.
+
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    *   - #ERR_INVALID_ARGUMENT (2): The RTMP URL address is NULL or has a string length of 0.
+    *   - #ERR_NOT_INITIALIZED (7): You have not initialized the RTC engine when publishing the stream.
+    *   - #ERR_ALREADY_IN_USE (19): This streaming URL is already in use. Use a new streaming URL for CDN streaming.
+    */
+  virtual int startRtmpStreamWithTranscoding(const char* url, const LiveTranscoding& transcoding) = 0;
+
+  /** Update the video layout and audio settings for CDN live. (CDN live only.)
+    * @note This method applies to Live Broadcast only.
+
+    * @param transcoding Sets the CDN live audio/video transcoding settings. See LiveTranscoding.
+
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+  virtual int updateRtmpTranscoding(const LiveTranscoding& transcoding) = 0;
+
+  virtual int startLocalVideoTranscoder(const LocalTranscoderConfiguration& config) = 0;
+  virtual int updateLocalTranscoderConfiguration(const LocalTranscoderConfiguration& config) = 0;
+
+  /** Stop an RTMP stream with transcoding or without transcoding from the CDN. (CDN live only.)
+
+    * This method removes the RTMP URL address (added by the \ref IRtcEngine::startRtmpStreamWithoutTranscoding "startRtmpStreamWithoutTranscoding" method
+    * or IRtcEngine::startRtmpStreamWithTranscoding "startRtmpStreamWithTranscoding" method) from a CDN live stream.
+    * The SDK returns the result of this method call in the \ref IRtcEngineEventHandler::onStreamUnpublished "onStreamUnpublished" callback.
+
+    * The \ref agora::rtc::IRtcEngine::stopRtmpStream "stopRtmpStream" method call triggers the \ref agora::rtc::IRtcEngineEventHandler::onRtmpStreamingStateChanged "onRtmpStreamingStateChanged" callback on the local client to report the state of removing an RTMP stream from the CDN.
+    * @note
+    * - This method removes only one RTMP URL address each time it is called.
+    * - The RTMP URL address must not contain special characters, such as Chinese language characters.
+    * - This method applies to Live Broadcast only.
+
+    * @param url The RTMP URL address to be removed. The maximum length of this parameter is 1024 bytes.
+
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+  virtual int stopRtmpStream(const char* url) = 0;
+
+  virtual int stopLocalVideoTranscoder() = 0;
+  /**
+   * Starts video capture with a camera.
+   *
+   * @param config The configuration of the video capture with a primary camera. For details, see CameraCaptureConfiguration.
+   * @param sourceType Source type of camera. See #VIDEO_SOURCE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startCameraCapture(VIDEO_SOURCE_TYPE sourceType, const CameraCapturerConfiguration& config) = 0;
+
+  /**
+   * Stops capturing video through camera.
+   *
+   * You can call this method to stop capturing video through the first camera after calling `startCameraCapture`.
+   *
+   * @param sourceType Source type of camera. See #VIDEO_SOURCE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopCameraCapture(VIDEO_SOURCE_TYPE sourceType) = 0;
+  /**
+   * Sets the rotation angle of the video captured by the camera.
+   *
+   * When the video capture device does not have the gravity sensing function, you can call this method to manually adjust the rotation angle of the captured video.
+   *
+   * @param type The video source type. See #VIDEO_SOURCE_TYPE.
+   * @param orientation The clockwise rotation angle. See #VIDEO_ORIENTATION.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setCameraDeviceOrientation(VIDEO_SOURCE_TYPE type, VIDEO_ORIENTATION orientation) = 0;
+  /**
+   * Sets the rotation angle of the video captured by the screen.
+   *
+   * When the screen capture device does not have the gravity sensing function, you can call this method to manually adjust the rotation angle of the captured video.
+   *
+   * @param type The video source type. See #VIDEO_SOURCE_TYPE.
+   * @param orientation The clockwise rotation angle. See #VIDEO_ORIENTATION.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setScreenCaptureOrientation(VIDEO_SOURCE_TYPE type, VIDEO_ORIENTATION orientation) = 0;
+
+  /**
+   * Starts sharing a screen.
+   *
+   * @param config The configuration of the captured screen. For details, see ScreenCaptureConfiguration.
+   * @param sourceType source type of screen. See #VIDEO_SOURCE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startScreenCapture(VIDEO_SOURCE_TYPE sourceType, const ScreenCaptureConfiguration& config) = 0;
+
+  /**
+   * Stop sharing the screen.
+   *
+   * After calling `startScreenCapture`, you can call this method to stop sharing the first screen.
+   * 
+   * @param sourceType source type of screen. See #VIDEO_SOURCE_TYPE.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopScreenCapture(VIDEO_SOURCE_TYPE sourceType) = 0;
+
+  /** Gets the current connection state of the SDK.
+
+   @return #CONNECTION_STATE_TYPE.
+   */
+  virtual CONNECTION_STATE_TYPE getConnectionState() = 0;
+
+  // The following APIs are not implemented yet.
+  virtual bool registerEventHandler(IRtcEngineEventHandler* eventHandler) = 0;
+  virtual bool unregisterEventHandler(IRtcEngineEventHandler* eventHandler) = 0;
+  virtual int setRemoteUserPriority(uid_t uid, PRIORITY_TYPE userPriority) = 0;
+
+  /**
+   * Registers a packet observer.
+   *
+   * The Agora Native SDK allows your app to register a packet observer to
+   * receive events whenever a voice or video packet is transmitting.
+   *
+   * @param observer The IPacketObserver object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerPacketObserver(IPacketObserver* observer) = 0;
+
+  /**
+   * Sets the built-in encryption mode.
+   *
+   * @deprecated This method is deprecated. Use enableEncryption(bool enabled, const EncryptionConfig&) instead.
+   *
+   * The Agora Native SDK supports built-in encryption.
+   * Call this API to set the encryption mode.
+   *
+   * All users in the same channel must use the same encryption mode and password.
+   * Refer to information related to the encryption algorithm on the differences
+   * between encryption modes.
+   *
+   * @note
+   * Call \ref setEncryptionSecret "setEncryptionSecret" to enable the built-in encryption function
+   * before calling this API.
+   * @param encryptionMode Encryption mode:
+   * - "sm4-128-ecb": 128-bit SM4 encryption, ECB mode.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setEncryptionMode(const char* encryptionMode) __deprecated = 0;
+
+  /**
+   * Enables built-in encryption.
+   *
+   * @deprecated This method is deprecated. Use enableEncryption(bool enabled, const EncryptionConfig&) instead.
+   *
+   * Use this method to specify an encryption password to enable built-in
+   * encryption before joining a channel. All users in a channel must set the same
+   * encryption password. The encryption password is automatically cleared once a
+   * user has left the channel. If the encryption password is not specified or set to
+   * empty, the encryption function will be disabled.
+   *
+   * @param secret The encryption password.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setEncryptionSecret(const char* secret) __deprecated = 0;
+
+  /** Enables/Disables the built-in encryption.
+   *
+   * In scenarios requiring high security, Agora recommends calling this method to enable the built-in encryption before joining a channel.
+   *
+   * All users in the same channel must use the same encryption mode and encryption key. Once all users leave the channel, the encryption key of this channel is automatically cleared.
+   *
+   * @note
+   * - If you enable the built-in encryption, you cannot use the RTMP streaming function.
+   *
+   * @param enabled Whether to enable the built-in encryption:
+   * - true: Enable the built-in encryption.
+   * - false: Disable the built-in encryption.
+   * @param config Configurations of built-in encryption schemas. See EncryptionConfig.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *  - -2(ERR_INVALID_ARGUMENT): An invalid parameter is used. Set the parameter with a valid value.
+   *  - -4(ERR_NOT_SUPPORTED): The encryption mode is incorrect or the SDK fails to load the external encryption library. Check the enumeration or reload the external encryption library.
+   *  - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. Initialize the `IRtcEngine` instance before calling this method.
+   */
+  virtual int enableEncryption(bool enabled, const EncryptionConfig& config) = 0;
+
+  /** Creates a data stream.
+   *
+   * You can call this method to create a data stream and improve the
+   * reliability and ordering of data tranmission.
+   *
+   * @note
+   * - Ensure that you set the same value for `reliable` and `ordered`.
+   * - Each user can only create a maximum of 5 data streams during a RtcEngine
+   * lifecycle.
+   * - The data channel allows a data delay of up to 5 seconds. If the receiver
+   * does not receive the data stream within 5 seconds, the data channel reports
+   * an error.
+   *
+   * @param[out] streamId The ID of the stream data.
+   * @param reliable Sets whether the recipients are guaranteed to receive
+   * the data stream from the sender within five seconds:
+   * - true: The recipients receive the data stream from the sender within
+   * five seconds. If the recipient does not receive the data stream within
+   * five seconds, an error is reported to the application.
+   * - false: There is no guarantee that the recipients receive the data stream
+   * within five seconds and no error message is reported for any delay or
+   * missing data stream.
+   * @param ordered Sets whether the recipients receive the data stream
+   * in the sent order:
+   * - true: The recipients receive the data stream in the sent order.
+   * - false: The recipients do not receive the data stream in the sent order.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int createDataStream(int* streamId, bool reliable, bool ordered) = 0;
+
+  /** Creates a data stream.
+   *
+   * Each user can create up to five data streams during the lifecycle of the IChannel.
+   * @param streamId The ID of the created data stream.
+   * @param config  The config of data stream.
+   * @return int
+   * - Returns 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int createDataStream(int* streamId, const DataStreamConfig& config) = 0;
+
+  /** Sends a data stream.
+   *
+   * After calling \ref IRtcEngine::createDataStream "createDataStream", you can call
+   * this method to send a data stream to all users in the channel.
+   *
+   * The SDK has the following restrictions on this method:
+   * - Up to 60 packets can be sent per second in a channel with each packet having a maximum size of 1 KB.
+   * - Each client can send up to 30 KB of data per second.
+   * - Each user can have up to five data streams simultaneously.
+   *
+   * After the remote user receives the data stream within 5 seconds, the SDK triggers the
+   * \ref IRtcEngineEventHandler::onStreamMessage "onStreamMessage" callback on
+   * the remote client. After the remote user does not receive the data stream within 5 seconds,
+   * the SDK triggers the \ref IRtcEngineEventHandler::onStreamMessageError "onStreamMessageError"
+   * callback on the remote client.
+   *
+   * @note
+   * - Call this method after calling \ref IRtcEngine::createDataStream "createDataStream".
+   * - This method applies only to the `COMMUNICATION` profile or to
+   * the hosts in the `LIVE_BROADCASTING` profile. If an audience in the
+   * `LIVE_BROADCASTING` profile calls this method, the audience may be switched to a host.
+   *
+   * @param streamId The ID of the stream data.
+   * @param data The data stream.
+   * @param length The length (byte) of the data stream.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int sendStreamMessage(int streamId, const char* data, size_t length) = 0;
+
+  /** **DEPRECATED** Adds a watermark image to the local video or CDN live stream.
+
+   This method is not recommend, Use \ref agora::rtc::IRtcEngine::addVideoWatermark(const char* watermarkUrl, const WatermarkOptions& options) "addVideoWatermark"2 instead.
+
+   This method adds a PNG watermark image to the local video stream for the recording device, channel audience, and CDN live audience to view and capture.
+
+   To add the PNG file to the CDN live publishing stream, see the \ref IRtcEngine::setLiveTranscoding "setLiveTranscoding" method.
+
+   @param watermark Pointer to the watermark image to be added to the local video stream. See RtcImage.
+
+   @note
+   - The URL descriptions are different for the local video and CDN live streams:
+      - In a local video stream, `url` in RtcImage refers to the absolute path of the added watermark image file in the local video stream.
+      - In a CDN live stream, `url` in RtcImage refers to the URL address of the added watermark image in the CDN live broadcast.
+   - The source file of the watermark image must be in the PNG file format. If the width and height of the PNG file differ from your settings in this method, the PNG file will be cropped to conform to your settings.
+   - The Agora SDK supports adding only one watermark image onto a local video or CDN live stream. The newly added watermark image replaces the previous one.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int addVideoWatermark(const RtcImage& watermark) __deprecated = 0;
+
+  /** Adds a watermark image to the local video.
+
+   This method adds a PNG watermark image to the local video in a live broadcast. Once the watermark image is added, all the audience in the channel (CDN audience included),
+   and the recording device can see and capture it. Agora supports adding only one watermark image onto the local video, and the newly watermark image replaces the previous one.
+
+   The watermark position depends on the settings in the \ref IRtcEngine::setVideoEncoderConfiguration "setVideoEncoderConfiguration" method:
+   - If the orientation mode of the encoding video is #ORIENTATION_MODE_FIXED_LANDSCAPE, or the landscape mode in #ORIENTATION_MODE_ADAPTIVE, the watermark uses the landscape orientation.
+   - If the orientation mode of the encoding video is #ORIENTATION_MODE_FIXED_PORTRAIT, or the portrait mode in #ORIENTATION_MODE_ADAPTIVE, the watermark uses the portrait orientation.
+   - When setting the watermark position, the region must be less than the dimensions set in the `setVideoEncoderConfiguration` method. Otherwise, the watermark image will be cropped.
+
+   @note
+   - Ensure that you have called the \ref agora::rtc::IRtcEngine::enableVideo "enableVideo" method to enable the video module before calling this method.
+   - If you only want to add a watermark image to the local video for the audience in the CDN live broadcast channel to see and capture, you can call this method or the \ref agora::rtc::IRtcEngine::setLiveTranscoding "setLiveTranscoding" method.
+   - This method supports adding a watermark image in the PNG file format only. Supported pixel formats of the PNG image are RGBA, RGB, Palette, Gray, and Alpha_gray.
+   - If the dimensions of the PNG image differ from your settings in this method, the image will be cropped or zoomed to conform to your settings.
+   - If you have enabled the local video preview by calling the \ref agora::rtc::IRtcEngine::startPreview "startPreview" method, you can use the `visibleInPreview` member in the WatermarkOptions class to set whether or not the watermark is visible in preview.
+   - If you have enabled the mirror mode for the local video, the watermark on the local video is also mirrored. To avoid mirroring the watermark, Agora recommends that you do not use the mirror and watermark functions for the local video at the same time. You can implement the watermark function in your application layer.
+
+   @param watermarkUrl The local file path of the watermark image to be added. This method supports adding a watermark image from the local absolute or relative file path.
+   @param options Pointer to the watermark's options to be added. See WatermarkOptions for more infomation.
+
+   @return int
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int addVideoWatermark(const char* watermarkUrl, const WatermarkOptions& options) = 0;
+
+  /** Removes the watermark image on the video stream added by
+  addVideoWatermark().
+
+  @return
+  - 0: Success.
+  - < 0: Failure.
+  */
+  virtual int clearVideoWatermarks() = 0;
+
+  // The following APIs are either deprecated and going to deleted.
+
+  /** @deprecated Use disableAudio() instead.
+
+   Disables the audio function in the channel.
+
+   @return int
+   - 0: Success.
+   - < 0: Failure.
+   */
+   virtual int pauseAudio() __deprecated = 0;
+   /** @deprecated Use enableAudio() instead.
+
+   Resumes the audio function in the channel.
+
+   @return int
+   - 0: Success.
+   - < 0: Failure.
+   */
+   virtual int resumeAudio() __deprecated = 0;
+
+  /**
+   * Enables interoperability with the Agora Web SDK (Live Broadcast only).
+   *
+   * @deprecated The Agora NG SDK enables the interoperablity with the Web SDK.
+   *
+   * Use this method when the channel profile is Live Broadcast. Interoperability
+   * with the Agora Web SDK is enabled by default when the channel profile is
+   * Communication.
+   *
+   * @param enabled Determines whether to enable interoperability with the Agora Web SDK.
+   * - true: (Default) Enable interoperability with the Agora Native SDK.
+   * - false: Disable interoperability with the Agora Native SDK.
+   *
+   * @return int
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableWebSdkInteroperability(bool enabled) __deprecated = 0;
+
+  /** Agora supports reporting and analyzing customized messages.
+   *
+   * This function is in the beta stage with a free trial. The ability provided
+   * in its beta test version is reporting a maximum of 10 message pieces within
+   * 6 seconds, with each message piece not exceeding 256 bytes.
+   *
+   * To try out this function, contact [support@agora.io](mailto:support@agora.io)
+   * and discuss the format of customized messages with us.
+   */
+  virtual int sendCustomReportMessage(const char* id, const char* category, const char* event, const char* label, int value) = 0;
+
+  /** Registers the metadata observer.
+
+   You need to implement the IMetadataObserver class and specify the metadata type
+   in this method. This method enables you to add synchronized metadata in the video
+   stream for more diversified live interactive streaming, such as sending
+   shopping links, digital coupons, and online quizzes.
+
+   A successful call of this method triggers
+   the \ref agora::rtc::IMetadataObserver::getMaxMetadataSize "getMaxMetadataSize" callback.
+
+   @note
+   - Call this method before the `joinChannel` method.
+   - This method applies to the `LIVE_BROADCASTING` channel profile.
+
+   @param observer IMetadataObserver.
+   @param type The metadata type. See \ref IMetadataObserver::METADATA_TYPE "METADATA_TYPE". The SDK supports VIDEO_METADATA (0) only for now.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+  */
+  virtual int registerMediaMetadataObserver(IMetadataObserver *observer, IMetadataObserver::METADATA_TYPE type) = 0;
+
+  /** Unregisters the metadata observer.
+   @param observer IMetadataObserver.
+   @param type The metadata type. See \ref IMetadataObserver::METADATA_TYPE "METADATA_TYPE". The SDK supports VIDEO_METADATA (0) only for now.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+  */
+  virtual int unregisterMediaMetadataObserver(IMetadataObserver* observer, IMetadataObserver::METADATA_TYPE type) = 0;
+
+  /** Start audio frame dump.
+
+   Optional `location` is: "pre_apm_proc", "apm", "pre_send_proc", "filter", "enc", "tx_mixer",
+                         "at_record", "atw_record" for audio sending.
+                         "dec", "mixed", "play", "rx_mixer", "playback_mixer", "pcm_source_playback_mixer",
+                         "pre_play_proc", "at_playout", "atw_playout" for audio receiving.
+
+   */
+  virtual int startAudioFrameDump(const char* channel_id, uid_t uid, const char* location, const char* uuid, const char* passwd, long duration_ms, bool auto_upload) = 0;
+
+  /**
+   * Stops the audio frame dump.
+   */
+  virtual int stopAudioFrameDump(const char* channel_id, uid_t uid, const char* location) = 0;
+
+ /**
+   * Enables/Disables Agora AI Noise Suppression(AINS) with preset mode.
+   *
+   * @param enabled Sets whether or not to enable AINS.
+   * - true: Enables the AINS.
+   * - false: Disables the AINS.
+   * @param mode The preset AINS mode, range is [0,1,2]:
+   * 0: AINS mode with soft suppression level.
+   * 1: AINS mode with aggressive suppression level.
+   * 2: AINS mode with aggressive suppression level and low algorithm latency.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAINSMode(bool enabled,  AUDIO_AINS_MODE mode) = 0;
+
+  /** Registers a user account.
+   *
+   * Once registered, the user account can be used to identify the local user when the user joins the channel.
+   * After the user successfully registers a user account, the SDK triggers the \ref agora::rtc::IRtcEngineEventHandler::onLocalUserRegistered "onLocalUserRegistered" callback on the local client,
+   * reporting the user ID and user account of the local user.
+   *
+   * To join a channel with a user account, you can choose either of the following:
+   *
+   * - Call the \ref agora::rtc::IRtcEngine::registerLocalUserAccount "registerLocalUserAccount" method to create a user account, and then the \ref agora::rtc::IRtcEngine::joinChannelWithUserAccount "joinChannelWithUserAccount" method to join the channel.
+   * - Call the \ref agora::rtc::IRtcEngine::joinChannelWithUserAccount "joinChannelWithUserAccount" method to join the channel.
+   *
+   * The difference between the two is that for the former, the time elapsed between calling the \ref agora::rtc::IRtcEngine::joinChannelWithUserAccount "joinChannelWithUserAccount" method
+   * and joining the channel is shorter than the latter.
+   *
+   * @note
+   * - Ensure that you set the `userAccount` parameter. Otherwise, this method does not take effect.
+   * - Ensure that the value of the `userAccount` parameter is unique in the channel.
+   * - To ensure smooth communication, use the same parameter type to identify the user. For example, if a user joins the channel with a user ID, then ensure all the other users use the user ID too. The same applies to the user account. If a user joins the channel with the Agora Web SDK, ensure that the uid of the user is set to the same parameter type.
+   *
+   * @param appId The App ID of your project.
+   * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int registerLocalUserAccount(const char* appId, const char* userAccount) = 0;
+
+  /** Joins the channel with a user account.
+   *
+   * After the user successfully joins the channel, the SDK triggers the following callbacks:
+   *
+   * - The local client: \ref agora::rtc::IRtcEngineEventHandler::onLocalUserRegistered "onLocalUserRegistered" and \ref agora::rtc::IRtcEngineEventHandler::onJoinChannelSuccess "onJoinChannelSuccess" .
+   * - The remote client: \ref agora::rtc::IRtcEngineEventHandler::onUserJoined "onUserJoined" and \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" , if the user joining the channel is in the `COMMUNICATION` profile, or is a host in the `LIVE_BROADCASTING` profile.
+   *
+   * @note To ensure smooth communication, use the same parameter type to identify the user. For example, if a user joins the channel with a user ID, then ensure all the other users use the user ID too. The same applies to the user account.
+   * If a user joins the channel with the Agora Web SDK, ensure that the uid of the user is set to the same parameter type.
+   *
+   * @param token The token generated at your server:
+   * - For low-security requirements: You can use the temporary token generated at Console. For details, see [Get a temporary toke](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-temporary-token).
+   * - For high-security requirements: Set it as the token generated at your server. For details, see [Get a token](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-token).
+   * @param channelId The channel name. The maximum length of this parameter is 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int joinChannelWithUserAccount(const char* token, const char* channelId, const char* userAccount) = 0;
+
+  /** Joins the channel with a user account.
+   *
+   * After the user successfully joins the channel, the SDK triggers the following callbacks:
+   *
+   * - The local client: \ref agora::rtc::IRtcEngineEventHandler::onLocalUserRegistered "onLocalUserRegistered" and \ref agora::rtc::IRtcEngineEventHandler::onJoinChannelSuccess "onJoinChannelSuccess" .
+   * - The remote client: \ref agora::rtc::IRtcEngineEventHandler::onUserJoined "onUserJoined" and \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" , if the user joining the channel is in the `COMMUNICATION` profile, or is a host in the `LIVE_BROADCASTING` profile.
+   *
+   * @note To ensure smooth communication, use the same parameter type to identify the user. For example, if a user joins the channel with a user ID, then ensure all the other users use the user ID too. The same applies to the user account.
+   * If a user joins the channel with the Agora Web SDK, ensure that the uid of the user is set to the same parameter type.
+   *
+   * @param token The token generated at your server:
+   * - For low-security requirements: You can use the temporary token generated at Console. For details, see [Get a temporary toke](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-temporary-token).
+   * - For high-security requirements: Set it as the token generated at your server. For details, see [Get a token](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-token).
+   * @param channelId The channel name. The maximum length of this parameter is 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param options  The channel media options: \ref agora::rtc::ChannelMediaOptions::ChannelMediaOptions "ChannelMediaOptions"
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int joinChannelWithUserAccount(const char* token, const char* channelId, const char* userAccount, const ChannelMediaOptions& options) = 0;
+
+  /** Joins the channel with a user account.
+   *
+   * After the user successfully joins the channel, the SDK triggers the following callbacks:
+   *
+   * - The local client: \ref agora::rtc::IRtcEngineEventHandler::onLocalUserRegistered "onLocalUserRegistered" and \ref agora::rtc::IRtcEngineEventHandler::onJoinChannelSuccess "onJoinChannelSuccess" .
+   * - The remote client: \ref agora::rtc::IRtcEngineEventHandler::onUserJoined "onUserJoined" and \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" , if the user joining the channel is in the `COMMUNICATION` profile, or is a host in the `LIVE_BROADCASTING` profile.
+   *
+   * @note To ensure smooth communication, use the same parameter type to identify the user. For example, if a user joins the channel with a user ID, then ensure all the other users use the user ID too. The same applies to the user account.
+   * If a user joins the channel with the Agora Web SDK, ensure that the uid of the user is set to the same parameter type.
+   *
+   * @param token The token generated at your server:
+   * - For low-security requirements: You can use the temporary token generated at Console. For details, see [Get a temporary toke](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-temporary-token).
+   * - For high-security requirements: Set it as the token generated at your server. For details, see [Get a token](https://docs.agora.io/en/Voice/token?platform=All%20Platforms#get-a-token).
+   * @param channelId The channel name. The maximum length of this parameter is 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   * @param options  The channel media options: \ref agora::rtc::ChannelMediaOptions::ChannelMediaOptions "ChannelMediaOptions"
+   * @param eventHandler The pointer to the IRtcEngine event handler: IRtcEngineEventHandler.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int joinChannelWithUserAccountEx(const char* token, const char* channelId,
+                                           const char* userAccount, const ChannelMediaOptions& options,
+                                           IRtcEngineEventHandler* eventHandler) = 0;
+
+  /** Gets the user information by passing in the user account.
+   *
+   * After a remote user joins the channel, the SDK gets the user ID and user account of the remote user, caches them
+   * in a mapping table object (`userInfo`), and triggers the \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" callback on the local client.
+   *
+   * After receiving the o\ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" callback, you can call this method to get the user ID of the
+   * remote user from the `userInfo` object by passing in the user account.
+   *
+   * @param userAccount The user account of the user. Ensure that you set this parameter.
+   * @param [in,out] userInfo  A userInfo object that identifies the user:
+   * - Input: A userInfo object.
+   * - Output: A userInfo object that contains the user account and user ID of the user.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getUserInfoByUserAccount(const char* userAccount, rtc::UserInfo* userInfo) = 0;
+
+  /** Gets the user information by passing in the user ID.
+   *
+   * After a remote user joins the channel, the SDK gets the user ID and user account of the remote user,
+   * caches them in a mapping table object (`userInfo`), and triggers the \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" callback on the local client.
+   *
+   * After receiving the \ref agora::rtc::IRtcEngineEventHandler::onUserInfoUpdated "onUserInfoUpdated" callback, you can call this method to get the user account of the remote user
+   * from the `userInfo` object by passing in the user ID.
+   *
+   * @param uid The user ID of the remote user. Ensure that you set this parameter.
+   * @param[in,out] userInfo A userInfo object that identifies the user:
+   * - Input: A userInfo object.
+   * - Output: A userInfo object that contains the user account and user ID of the user.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getUserInfoByUid(uid_t uid, rtc::UserInfo* userInfo) = 0;
+
+  /** Starts relaying media streams across channels or updates the channels for media relay.
+    *
+    * After a successful method call, the SDK triggers the
+    * \ref agora::rtc::IRtcEngineEventHandler::onChannelMediaRelayStateChanged
+    *  "onChannelMediaRelayStateChanged" callback, and this callback return the state of the media stream relay.
+    * - If the
+    * \ref agora::rtc::IRtcEngineEventHandler::onChannelMediaRelayStateChanged
+    *  "onChannelMediaRelayStateChanged" callback returns
+    * #RELAY_STATE_RUNNING (2) and #RELAY_OK (0), the host starts sending data to the destination channel.
+    * - If the
+    * \ref agora::rtc::IRtcEngineEventHandler::onChannelMediaRelayStateChanged
+    *  "onChannelMediaRelayStateChanged" callback returns
+    * #RELAY_STATE_FAILURE (3), an exception occurs during the media stream
+    * relay.
+    *
+    * @note
+    * - Call this method after the \ref joinChannel() "joinChannel" method.
+    * - This method takes effect only when you are a host in a
+    * `LIVE_BROADCASTING` channel.
+    * - Contact sales-us@agora.io before implementing this function.
+    * - We do not support string user accounts in this API.
+    *
+    * @since v4.2.0
+    * @param configuration The configuration of the media stream relay:
+    * ChannelMediaRelayConfiguration.
+    *
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+    *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+    *   - -5(ERR_REFUSED): The request is rejected.
+    *   - -8(ERR_INVALID_STATE): The current status is invalid, only allowed to be called when the role is the broadcaster.
+  **/
+  virtual int startOrUpdateChannelMediaRelay(const ChannelMediaRelayConfiguration &configuration) = 0;
+
+  /** Stops the media stream relay.
+    *
+    * Once the relay stops, the host quits all the destination
+    * channels.
+    *
+    * After a successful method call, the SDK triggers the
+    * \ref agora::rtc::IRtcEngineEventHandler::onChannelMediaRelayStateChanged
+    *  "onChannelMediaRelayStateChanged" callback. If the callback returns
+    * #RELAY_STATE_IDLE (0) and #RELAY_OK (0), the host successfully
+    * stops the relay.
+    *
+    * @note
+    * If the method call fails, the SDK triggers the
+    * \ref agora::rtc::IRtcEngineEventHandler::onChannelMediaRelayStateChanged
+    *  "onChannelMediaRelayStateChanged" callback with the
+    * #RELAY_ERROR_SERVER_NO_RESPONSE (2) or
+    * #RELAY_ERROR_SERVER_CONNECTION_LOST (8) state code. You can leave the
+    * channel by calling the \ref leaveChannel() "leaveChannel" method, and
+    * the media stream relay automatically stops.
+    *
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+    *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+    *   - -5(ERR_REFUSED): The request is rejected.
+    *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+    */
+  virtual int stopChannelMediaRelay() = 0;
+
+  /** pause the channels for media stream relay.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+   */
+  virtual int pauseAllChannelMediaRelay() = 0;
+
+  /** resume the channels for media stream relay.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+   */
+  virtual int resumeAllChannelMediaRelay() = 0;
+
+  /** Set audio parameters for direct streaming to CDN
+   *
+   * @note
+   * Must call this api before "startDirectCdnStreaming"
+   *
+   * @param profile Sets the sample rate, bitrate, encoding mode, and the number of channels:
+   * #AUDIO_PROFILE_TYPE.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDirectCdnStreamingAudioConfiguration(AUDIO_PROFILE_TYPE profile) = 0;
+
+  /** Set video parameters for direct streaming to CDN
+   *
+   * Each configuration profile corresponds to a set of video parameters, including
+   * the resolution, frame rate, and bitrate.
+   *
+   * @note
+   * Must call this api before "startDirectCdnStreaming"
+   *
+   * @param config The local video encoder configuration: VideoEncoderConfiguration.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDirectCdnStreamingVideoConfiguration(const VideoEncoderConfiguration& config) = 0;
+
+  /** Start direct cdn streaming
+   *
+   * @param eventHandler A pointer to the direct cdn streaming event handler: \ref agora::rtc::IDirectCdnStreamingEventHandler
+   * "IDirectCdnStreamingEventHandler".
+   * @param publishUrl The url of the cdn used to publish the stream.
+   * @param options The direct cdn streaming media options: DirectCdnStreamingMediaOptions.
+   * This API must pass an audio-related option, and temporarily cannot pass more than one. 
+   * For video-related options, you can either choose to not pass any, or only one.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startDirectCdnStreaming(IDirectCdnStreamingEventHandler* eventHandler,
+                                      const char* publishUrl, const DirectCdnStreamingMediaOptions& options) = 0;
+
+  /** Stop direct cdn streaming
+   *
+   * @note
+   * This method is synchronous.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopDirectCdnStreaming() = 0;
+
+  /** Change the media source during the pushing
+   *
+   * @note
+   * This method is temporarily not supported.
+   *
+   * @param options The direct cdn streaming media options: DirectCdnStreamingMediaOptions.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int updateDirectCdnStreamingMediaOptions(const DirectCdnStreamingMediaOptions& options) = 0;
+
+  /** Enables the rhythm player.
+   *
+   * @param sound1 The absolute path or URL address (including the filename extensions) of the file for the downbeat.
+   * @param sound2 The absolute path or URL address (including the filename extensions) of the file for the upbeats.
+   * @param config The configuration of rhythm player.
+   *
+   * @return int
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startRhythmPlayer(const char* sound1, const char* sound2, const AgoraRhythmPlayerConfig& config) = 0;
+
+  /** Disables the rhythm player.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopRhythmPlayer() = 0;
+
+  /** Configures the rhythm player.
+   *
+   * @param config The configuration of rhythm player.
+   *
+   * @return int
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int configRhythmPlayer(const AgoraRhythmPlayerConfig& config) = 0;
+
+  /**
+   * Takes a snapshot of a video stream.
+   *
+   * This method takes a snapshot of a video stream from the specified user, generates a JPG
+   * image, and saves it to the specified path.
+   *
+   * The method is asynchronous, and the SDK has not taken the snapshot when the method call
+   * returns. After a successful method call, the SDK triggers the `onSnapshotTaken` callback
+   * to report whether the snapshot is successfully taken, as well as the details for that
+   * snapshot.
+   *
+   * @note
+   * - Call this method after joining a channel.
+   * - This method takes a snapshot of the published video stream specified in `ChannelMediaOptions`.
+   * - If the user's video has been preprocessed, for example, watermarked or beautified, the resulting
+   * snapshot includes the pre-processing effect.
+   *
+   * @param uid The user ID. Set uid as 0 if you want to take a snapshot of the local user's video.
+   * @param filePath The local path (including filename extensions) of the snapshot. For example:
+   * - Windows: `C:\Users\<user_name>\AppData\Local\Agora\<process_name>\example.jpg`
+   * - iOS: `/App Sandbox/Library/Caches/example.jpg`
+   * - macOS: `~/Library/Logs/example.jpg`
+   * - Android: `/storage/emulated/0/Android/data/<package name>/files/example.jpg`
+   *
+   * Ensure that the path you specify exists and is writable.
+   * @return
+   * - 0 : Success.
+   * - < 0 : Failure.
+   */
+  virtual int takeSnapshot(uid_t uid, const char* filePath)  = 0;
+
+  /**
+   * Takes a snapshot of a video stream.
+   *
+   * This method takes a snapshot of a video stream from the specified user, generates a JPG
+   * image, and saves it to the specified path.
+   *
+   * The method is asynchronous, and the SDK has not taken the snapshot when the method call
+   * returns. After a successful method call, the SDK triggers the `onSnapshotTaken` callback
+   * to report whether the snapshot is successfully taken, as well as the details for that
+   * snapshot.
+   *
+   * @note
+   * - Call this method after joining a channel.
+   * - This method takes a snapshot of the published video stream specified in `ChannelMediaOptions`.
+   *
+   * @param uid The user ID. Set uid as 0 if you want to take a snapshot of the local user's video.
+   * @param config The configuration for the take snapshot. See SnapshotConfig.
+   *
+   * Ensure that the path you specify exists and is writable.
+   * @return
+   * - 0 : Success.
+   * - &lt; 0: Failure.
+   *   - -4: Incorrect observation position. Modify the input observation position according to the reqiurements specified in SnapshotConfig.
+   */
+  virtual int takeSnapshot(uid_t uid, const media::SnapshotConfig& config)  = 0;
+
+    /** Enables the content inspect.
+    @param enabled Whether to enable content inspect:
+    - `true`: Yes.
+    - `false`: No.
+    @param config The configuration for the content inspection.
+    @return
+    - 0: Success.
+    - < 0: Failure.
+    */
+  virtual int enableContentInspect(bool enabled, const media::ContentInspectConfig &config) = 0;
+  /*
+   * Adjust the custom audio publish volume by track id.
+   * @param trackId custom audio track id.
+   * @param volume The volume, range is [0,100]:
+   * 0: mute, 100: The original volume
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int adjustCustomAudioPublishVolume(track_id_t trackId, int volume) = 0;
+
+  /*
+   * Adjust the custom audio playout volume by track id.
+   * @param trackId custom audio track id.
+   * @param volume The volume, range is [0,100]:
+   * 0: mute, 100: The original volume
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int adjustCustomAudioPlayoutVolume(track_id_t trackId, int volume) = 0;
+
+  /** Sets the Agora cloud proxy service.
+   *
+   * @since v3.3.0
+   *
+   * When the user's firewall restricts the IP address and port, refer to *Use Cloud Proxy* to add the specific
+   * IP addresses and ports to the firewall allowlist; then, call this method to enable the cloud proxy and set
+   * the `proxyType` parameter as `UDP_PROXY(1)`, which is the cloud proxy for the UDP protocol.
+   *
+   * After a successfully cloud proxy connection, the SDK triggers
+   * the \ref IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" (CONNECTION_STATE_CONNECTING, CONNECTION_CHANGED_SETTING_PROXY_SERVER) callback.
+   *
+   * To disable the cloud proxy that has been set, call `setCloudProxy(NONE_PROXY)`. To change the cloud proxy type that has been set,
+   * call `setCloudProxy(NONE_PROXY)` first, and then call `setCloudProxy`, and pass the value that you expect in `proxyType`.
+   *
+   * @note
+   * - Agora recommends that you call this method before joining the channel or after leaving the channel.
+   * - For the SDK v3.3.x, the services for pushing streams to CDN and co-hosting across channels are not available
+   * when you use the cloud proxy for the UDP protocol. For the SDK v3.4.0 and later, the services for pushing streams
+   * to CDN and co-hosting across channels are not available when the user is in a network environment with a firewall
+   * and uses the cloud proxy for the UDP protocol.
+   *
+   * @param proxyType The cloud proxy type, see #CLOUD_PROXY_TYPE. This parameter is required, and the SDK reports an error if you do not pass in a value.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   *  - `-2(ERR_INVALID_ARGUMENT)`: The parameter is invalid.
+   *  - `-7(ERR_NOT_INITIALIZED)`: The SDK is not initialized.
+   */
+  virtual int setCloudProxy(CLOUD_PROXY_TYPE proxyType) = 0;
+  /** set local access point addresses in local proxy mode. use this method before join channel.
+
+   @param config The LocalAccessPointConfiguration class, See the definition of LocalAccessPointConfiguration for details.
+
+   @return
+   - 0: Success
+   - < 0: Failure
+   */
+  virtual int setLocalAccessPoint(const LocalAccessPointConfiguration& config) = 0;
+
+  /** set advanced audio options.
+   @param options The AdvancedAudioOptions class, See the definition of AdvancedAudioOptions for details.
+
+   @return
+   - 0: Success
+   - < 0: Failure
+   */
+  virtual int setAdvancedAudioOptions(AdvancedAudioOptions& options, int sourceType = 0) = 0;
+
+  /** Bind local user and a remote user as an audio&video sync group. The remote user is defined by cid and uid.
+   *  There’s a usage limit that local user must be a video stream sender. On the receiver side, media streams from same sync group will be time-synced
+   *
+   * @param channelId The channel id
+   * @param uid The user ID of the remote user to be bound with (local user)
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setAVSyncSource(const char* channelId, uid_t uid) = 0;
+
+  /**
+   * @brief enable or disable video image source to replace the current video source published or resume it
+   *
+   * @param enable true for enable, false for disable
+   * @param options options for image track
+   */
+  virtual int enableVideoImageSource(bool enable, const ImageTrackOptions& options) = 0;
+
+  /*
+   * Get monotonic time in ms which can be used by capture time,
+   * typical scenario is as follows:
+   *
+   *  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   *  |  // custom audio/video base capture time, e.g. the first audio/video capture time.             |
+   *  |  int64_t custom_capture_time_base;                                                             |
+   *  |                                                                                                |
+   *  |  int64_t agora_monotonic_time = getCurrentMonotonicTimeInMs();                                 |
+   *  |                                                                                                |
+   *  |  // offset is fixed once calculated in the begining.                                           |
+   *  |  const int64_t offset = agora_monotonic_time - custom_capture_time_base;                       |
+   *  |                                                                                                |
+   *  |  // realtime_custom_audio/video_capture_time is the origin capture time that customer provided.|
+   *  |  // actual_audio/video_capture_time is the actual capture time transfered to sdk.              |
+   *  |  int64_t actual_audio_capture_time = realtime_custom_audio_capture_time + offset;              |
+   *  |  int64_t actual_video_capture_time = realtime_custom_video_capture_time + offset;              |
+   *  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   *
+   * @return
+   * - >= 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int64_t getCurrentMonotonicTimeInMs() = 0;
+
+  /**
+   * Turns WIFI acceleration on or off.
+   *
+   * @note
+   * - This method is called before and after joining a channel.
+   * - Users check the WIFI router app for information about acceleration. Therefore, if this interface is invoked, the caller accepts that the caller's name will be displayed to the user in the WIFI router application on behalf of the caller.
+   *
+   * @param enabled
+   * - true:Turn WIFI acceleration on.
+   * - false:Turn WIFI acceleration off.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int enableWirelessAccelerate(bool enabled) = 0;
+
+  /**
+  * get network type value
+  *
+  * @return
+  * - 0: DISCONNECTED.
+  * - 1: LAN.
+  * - 2: WIFI.
+  * - 3: MOBILE_2G.
+  * - 4: MOBILE_3G.
+  * - 5: MOBILE_4G.
+  * - 6: MOBILE_5G.
+  * - -1: UNKNOWN.
+  */
+
+  virtual int getNetworkType() = 0;
+
+  /** Provides the technical preview functionalities or special customizations by configuring the SDK with JSON options.
+
+   @param parameters Pointer to the set parameters in a JSON string.
+
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int setParameters(const char* parameters) = 0;
+
+  /**
+   @brief Start tracing media rendering events.
+   @since v4.1.1
+   @discussion
+   - SDK will trace media rendering events when this API is called.
+   - The tracing result can be obtained through callback `IRtcEngineEventHandler::onVideoRenderingTracingResult`
+   @note
+   - By default, SDK will trace media rendering events when `IRtcEngine::joinChannel` is called.
+   - The start point of event tracing will be reset after leaving channel.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+    - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. Initialize the `IRtcEngine` instance before calling this method.
+   */
+  virtual int startMediaRenderingTracing() = 0;
+
+  /**
+   @brief Enable instant media rendering.
+   @since v4.1.1
+   @discussion
+   - This method enable SDK to render video or playout audio faster.
+   @note
+   - Once enable this mode, we should destroy rtc engine to disable it.
+   - Enable this mode, will sacrifice some part of experience.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+    - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. Initialize the `IRtcEngine` instance before calling this method.
+   */
+  virtual int enableInstantMediaRendering() = 0;
+
+  /**
+   * Return current NTP(unix timestamp) time in milliseconds.
+   */
+  virtual uint64_t getNtpWallTimeInMs() = 0;
+
+  /** 
+   * @brief Whether the target feature is available for the device.
+   * @since v4.3.0
+   * @param type The feature type. See FeatureType.
+   * @return
+   * - true: available.
+   * - false: not available.
+   */
+  virtual bool isFeatureAvailableOnDevice(FeatureType type) = 0;
+
+  /**
+   * @brief send audio metadata
+   * @since v4.3.1
+   * @param metadata The pointer of metadata
+   * @param length Size of metadata
+   * @return
+   * - 0: success
+   * - <0: failure
+   * @technical preview
+  */
+  virtual int sendAudioMetadata(const char* metadata, size_t length) = 0;
+};
+
+// The following types are either deprecated or not implmented yet.
+enum QUALITY_REPORT_FORMAT_TYPE {
+  /** 0: The quality report in JSON format,
+   */
+  QUALITY_REPORT_JSON = 0,
+  /** 1: The quality report in HTML format.
+   */
+  QUALITY_REPORT_HTML = 1,
+};
+
+/** Media device states. */
+enum MEDIA_DEVICE_STATE_TYPE {
+  /** 0: The device is ready for use.
+   */
+  MEDIA_DEVICE_STATE_IDLE = 0,
+  /** 1: The device is active.
+   */
+  MEDIA_DEVICE_STATE_ACTIVE = 1,
+  /** 2: The device is disabled.
+   */
+  MEDIA_DEVICE_STATE_DISABLED = 2,
+  /** 4: The device is not present.
+   */
+  MEDIA_DEVICE_STATE_NOT_PRESENT = 4,
+  /** 8: The device is unplugged.
+   */
+  MEDIA_DEVICE_STATE_UNPLUGGED = 8
+};
+
+enum VIDEO_PROFILE_TYPE {
+  /** 0: 160 x 120  @ 15 fps */      // res       fps
+  VIDEO_PROFILE_LANDSCAPE_120P = 0,  // 160x120   15
+  /** 2: 120 x 120 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_120P_3 = 2,   // 120x120   15
+                                        /** 10: 320 x 180 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_180P = 10,    // 320x180   15
+                                        /** 12: 180 x 180  @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_180P_3 = 12,  // 180x180   15
+                                        /** 13: 240 x 180 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_180P_4 = 13,  // 240x180   15
+                                        /** 20: 320 x 240 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_240P = 20,    // 320x240   15
+  /** 22: 240 x 240 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_240P_3 = 22,  // 240x240   15
+  /** 23: 424 x 240 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_240P_4 = 23,  // 424x240   15
+  /** 30: 640 x 360 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P = 30,  // 640x360   15
+  /** 32: 360 x 360 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_3 = 32,  // 360x360   15
+  /** 33: 640 x 360 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_4 = 33,  // 640x360   30
+  /** 35: 360 x 360 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_6 = 35,  // 360x360   30
+  /** 36: 480 x 360 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_7 = 36,  // 480x360   15
+  /** 37: 480 x 360 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_8 = 37,  // 480x360   30
+  /** 38: 640 x 360 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_9 = 38,   // 640x360   15
+                                         /** 39: 640 x 360 @ 24 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_10 = 39,  // 640x360   24
+  /** 100: 640 x 360 @ 24 fps */
+  VIDEO_PROFILE_LANDSCAPE_360P_11 = 100,  // 640x360   24
+  /** 40: 640 x 480 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P = 40,  // 640x480   15
+  /** 42: 480 x 480 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_3 = 42,  // 480x480   15
+  /** 43: 640 x 480 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_4 = 43,  // 640x480   30
+                                        /** 45: 480 x 480 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_6 = 45,  // 480x480   30
+  /** 47: 848 x 480 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_8 = 47,  // 848x480   15
+                                        /** 48: 848 x 480 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_9 = 48,  // 848x480   30
+  /** 49: 640 x 480 @ 10 fps */
+  VIDEO_PROFILE_LANDSCAPE_480P_10 = 49,  // 640x480   10
+  /** 50: 1280 x 720 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_720P = 50,  // 1280x720  15
+  /** 52: 1280 x 720 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_720P_3 = 52,  // 1280x720  30
+  /** 54: 960 x 720 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_720P_5 = 54,  // 960x720   15
+  /** 55: 960 x 720 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_720P_6 = 55,  // 960x720   30
+  /** 60: 1920 x 1080 @ 15 fps */
+  VIDEO_PROFILE_LANDSCAPE_1080P = 60,  // 1920x1080 15
+  /** 62: 1920 x 1080 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_1080P_3 = 62,  // 1920x1080 30
+  /** 64: 1920 x 1080 @ 60 fps */
+  VIDEO_PROFILE_LANDSCAPE_1080P_5 = 64,  // 1920x1080 60
+  /** 66: 2560 x 1440 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_1440P = 66,  // 2560x1440 30
+  /** 67: 2560 x 1440 @ 60 fps */
+  VIDEO_PROFILE_LANDSCAPE_1440P_2 = 67,  // 2560x1440 60
+  /** 70: 3840 x 2160 @ 30 fps */
+  VIDEO_PROFILE_LANDSCAPE_4K = 70,  // 3840x2160 30
+  /** 72: 3840 x 2160 @ 60 fps */
+  VIDEO_PROFILE_LANDSCAPE_4K_3 = 72,     // 3840x2160 60
+                                         /** 1000: 120 x 160 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_120P = 1000,    // 120x160   15
+                                         /** 1002: 120 x 120 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_120P_3 = 1002,  // 120x120   15
+                                         /** 1010: 180 x 320 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_180P = 1010,    // 180x320   15
+  /** 1012: 180 x 180 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_180P_3 = 1012,  // 180x180   15
+  /** 1013: 180 x 240 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_180P_4 = 1013,  // 180x240   15
+  /** 1020: 240 x 320 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_240P = 1020,  // 240x320   15
+  /** 1022: 240 x 240 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_240P_3 = 1022,  // 240x240   15
+  /** 1023: 240 x 424 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_240P_4 = 1023,  // 240x424   15
+  /** 1030: 360 x 640 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_360P = 1030,  // 360x640   15
+  /** 1032: 360 x 360 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_3 = 1032,  // 360x360   15
+  /** 1033: 360 x 640 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_4 = 1033,  // 360x640   30
+                                         /** 1035: 360 x 360 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_6 = 1035,  // 360x360   30
+  /** 1036: 360 x 480 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_7 = 1036,  // 360x480   15
+  /** 1037: 360 x 480 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_8 = 1037,  // 360x480   30
+                                         /** 1038: 360 x 640 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_9 = 1038,  // 360x640   15
+  /** 1039: 360 x 640 @ 24 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_10 = 1039,  // 360x640   24
+  /** 1100: 360 x 640 @ 24 fps */
+  VIDEO_PROFILE_PORTRAIT_360P_11 = 1100,  // 360x640   24
+  /** 1040: 480 x 640 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_480P = 1040,  // 480x640   15
+  /** 1042: 480 x 480 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_3 = 1042,  // 480x480   15
+  /** 1043: 480 x 640 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_4 = 1043,  // 480x640   30
+                                         /** 1045: 480 x 480 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_6 = 1045,  // 480x480   30
+                                         /** 1047: 480 x 848 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_8 = 1047,  // 480x848   15
+  /** 1048: 480 x 848 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_9 = 1048,  // 480x848   30
+  /** 1049: 480 x 640 @ 10 fps */
+  VIDEO_PROFILE_PORTRAIT_480P_10 = 1049,  // 480x640   10
+  /** 1050: 720 x 1280 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_720P = 1050,  // 720x1280  15
+  /** 1052: 720 x 1280 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_720P_3 = 1052,  // 720x1280  30
+  /** 1054: 720 x 960 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_720P_5 = 1054,  // 720x960   15
+                                         /** 1055: 720 x 960 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_720P_6 = 1055,  // 720x960   30
+  /** 1060: 1080 x 1920 @ 15 fps */
+  VIDEO_PROFILE_PORTRAIT_1080P = 1060,    // 1080x1920 15
+                                          /** 1062: 1080 x 1920 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_1080P_3 = 1062,  // 1080x1920 30
+                                          /** 1064: 1080 x 1920 @ 60 fps */
+  VIDEO_PROFILE_PORTRAIT_1080P_5 = 1064,  // 1080x1920 60
+  /** 1066: 1440 x 2560 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_1440P = 1066,  // 1440x2560 30
+  /** 1067: 1440 x 2560 @ 60 fps */
+  VIDEO_PROFILE_PORTRAIT_1440P_2 = 1067,  // 1440x2560 60
+                                          /** 1070: 2160 x 3840 @ 30 fps */
+  VIDEO_PROFILE_PORTRAIT_4K = 1070,       // 2160x3840 30
+  /** 1072: 2160 x 3840 @ 60 fps */
+  VIDEO_PROFILE_PORTRAIT_4K_3 = 1072,  // 2160x3840 60
+  /** Default 640 x 360 @ 15 fps */
+  VIDEO_PROFILE_DEFAULT = VIDEO_PROFILE_LANDSCAPE_360P,
+};
+
+class AAudioDeviceManager : public agora::util::AutoPtr<IAudioDeviceManager> {
+ public:
+  AAudioDeviceManager(IRtcEngine* engine) {
+    queryInterface(engine, AGORA_IID_AUDIO_DEVICE_MANAGER);
+  }
+};
+
+class AVideoDeviceManager : public agora::util::AutoPtr<IVideoDeviceManager> {
+ public:
+  AVideoDeviceManager(IRtcEngine* engine) {
+    queryInterface(engine, AGORA_IID_VIDEO_DEVICE_MANAGER);
+  }
+};
+
+}  // namespace rtc
+}  // namespace agora
+
+/** Gets the SDK version number.
+
+@param build Build number of Agora the SDK.
+* @return String of the SDK version.
+*/
+#define getAgoraRtcEngineVersion getAgoraSdkVersion
+
+////////////////////////////////////////////////////////
+/** \addtogroup createAgoraRtcEngine
+ @{
+ */
+////////////////////////////////////////////////////////
+
+/** Creates the RTC engine object and returns the pointer.
+
+* @return Pointer of the RTC engine object.
+*/
+AGORA_API agora::rtc::IRtcEngine* AGORA_CALL createAgoraRtcEngine();
+
+////////////////////////////////////////////////////////
+/** @} */
+////////////////////////////////////////////////////////
+
+/** Creates the RTC engine object and returns the pointer.
+
+ @param err Error Code.
+* @return Description of the Error Code: agora::ERROR_CODE_TYPE
+*/
+#define getAgoraRtcEngineErrorDescription getAgoraSdkErrorDescription
+#define setAgoraRtcEngineExternalSymbolLoader setAgoraSdkExternalSymbolLoader
diff --git a/headers/rtc_4.3.2.11/include/IAgoraRtcEngineEx.h b/headers/rtc_4.3.2.11/include/IAgoraRtcEngineEx.h
new file mode 100644
index 0000000..588d4df
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraRtcEngineEx.h
@@ -0,0 +1,2169 @@
+//
+//  Agora Media SDK
+//
+//  Created by Sting Feng in 2015-05.
+//  Updated by Tommy Miao in 2020-11.
+//  Copyright (c) 2015 Agora IO. All rights reserved.
+//
+#pragma once
+
+#include "IAgoraRtcEngine.h"
+
+namespace agora {
+namespace rtc {
+
+// OPTIONAL_ENUM_CLASS RTC_EVENT;
+
+/**
+ * Rtc Connection.
+ */
+struct RtcConnection {
+  /**
+   *  The unique channel name for the AgoraRTC session in the string format. The string
+   * length must be less than 64 bytes. Supported character scopes are:
+   * - All lowercase English letters: a to z.
+   * - All uppercase English letters: A to Z.
+   * - All numeric characters: 0 to 9.
+   * - The space character.
+   * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-",
+   * ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+   */
+  const char* channelId;
+  /**
+   * User ID: A 32-bit unsigned integer ranging from 1 to (2^32-1). It must be unique.
+   */
+  uid_t localUid;
+
+  RtcConnection() : channelId(NULL), localUid(0) {}
+  RtcConnection(const char* channel_id, uid_t local_uid)
+      : channelId(channel_id), localUid(local_uid) {}
+};
+
+class IRtcEngineEventHandlerEx : public IRtcEngineEventHandler {
+ public:
+  using IRtcEngineEventHandler::eventHandlerType;
+  using IRtcEngineEventHandler::onJoinChannelSuccess;
+  using IRtcEngineEventHandler::onRejoinChannelSuccess;
+  using IRtcEngineEventHandler::onAudioQuality;
+  using IRtcEngineEventHandler::onAudioVolumeIndication;
+  using IRtcEngineEventHandler::onLeaveChannel;
+  using IRtcEngineEventHandler::onRtcStats;
+  using IRtcEngineEventHandler::onNetworkQuality;
+  using IRtcEngineEventHandler::onIntraRequestReceived;
+  using IRtcEngineEventHandler::onFirstLocalVideoFramePublished;
+  using IRtcEngineEventHandler::onFirstRemoteVideoDecoded;
+  using IRtcEngineEventHandler::onVideoSizeChanged;
+  using IRtcEngineEventHandler::onLocalVideoStateChanged;
+  using IRtcEngineEventHandler::onRemoteVideoStateChanged;
+  using IRtcEngineEventHandler::onFirstRemoteVideoFrame;
+  using IRtcEngineEventHandler::onUserJoined;
+  using IRtcEngineEventHandler::onUserOffline;
+  using IRtcEngineEventHandler::onUserMuteAudio;
+  using IRtcEngineEventHandler::onUserMuteVideo;
+  using IRtcEngineEventHandler::onUserEnableVideo;
+  using IRtcEngineEventHandler::onUserEnableLocalVideo;
+  using IRtcEngineEventHandler::onUserStateChanged;
+  using IRtcEngineEventHandler::onLocalAudioStats;
+  using IRtcEngineEventHandler::onRemoteAudioStats;
+  using IRtcEngineEventHandler::onLocalVideoStats;
+  using IRtcEngineEventHandler::onRemoteVideoStats;
+  using IRtcEngineEventHandler::onConnectionLost;
+  using IRtcEngineEventHandler::onConnectionInterrupted;
+  using IRtcEngineEventHandler::onConnectionBanned;
+  using IRtcEngineEventHandler::onStreamMessage;
+  using IRtcEngineEventHandler::onStreamMessageError;
+  using IRtcEngineEventHandler::onRequestToken;
+  using IRtcEngineEventHandler::onTokenPrivilegeWillExpire;
+  using IRtcEngineEventHandler::onLicenseValidationFailure;
+  using IRtcEngineEventHandler::onFirstLocalAudioFramePublished;
+  using IRtcEngineEventHandler::onFirstRemoteAudioFrame;
+  using IRtcEngineEventHandler::onFirstRemoteAudioDecoded;
+  using IRtcEngineEventHandler::onLocalAudioStateChanged;
+  using IRtcEngineEventHandler::onRemoteAudioStateChanged;
+  using IRtcEngineEventHandler::onActiveSpeaker;
+  using IRtcEngineEventHandler::onClientRoleChanged;
+  using IRtcEngineEventHandler::onClientRoleChangeFailed;
+  using IRtcEngineEventHandler::onRemoteAudioTransportStats;
+  using IRtcEngineEventHandler::onRemoteVideoTransportStats;
+  using IRtcEngineEventHandler::onConnectionStateChanged;
+  using IRtcEngineEventHandler::onWlAccMessage;
+  using IRtcEngineEventHandler::onWlAccStats;
+  using IRtcEngineEventHandler::onNetworkTypeChanged;
+  using IRtcEngineEventHandler::onEncryptionError;
+  using IRtcEngineEventHandler::onUploadLogResult;
+  using IRtcEngineEventHandler::onUserInfoUpdated;
+  using IRtcEngineEventHandler::onUserAccountUpdated;
+  using IRtcEngineEventHandler::onAudioSubscribeStateChanged;
+  using IRtcEngineEventHandler::onVideoSubscribeStateChanged;
+  using IRtcEngineEventHandler::onAudioPublishStateChanged;
+  using IRtcEngineEventHandler::onVideoPublishStateChanged;
+  using IRtcEngineEventHandler::onSnapshotTaken;
+  using IRtcEngineEventHandler::onVideoRenderingTracingResult;
+  using IRtcEngineEventHandler::onSetRtmFlagResult;
+  using IRtcEngineEventHandler::onTranscodedStreamLayoutInfo;
+  using IRtcEngineEventHandler::onAudioMetadataReceived;
+
+  virtual const char* eventHandlerType() const { return "event_handler_ex"; }
+
+  /**
+   * Occurs when a user joins a channel.
+   *
+   * This callback notifies the application that a user joins a specified channel.
+   *
+   * @param connection The RtcConnection object.
+   * @param elapsed The time elapsed (ms) from the local user calling joinChannel until the SDK triggers this callback.
+   */
+  virtual void onJoinChannelSuccess(const RtcConnection& connection, int elapsed) {
+    (void)connection;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a user rejoins the channel.
+   *
+   * When a user loses connection with the server because of network problems, the SDK automatically tries to reconnect
+   * and triggers this callback upon reconnection.
+   *
+   * @param connection The RtcConnection object.
+   * @param elapsed Time elapsed (ms) from the local user calling the joinChannel method until this callback is triggered.
+   */
+  virtual void onRejoinChannelSuccess(const RtcConnection& connection, int elapsed) {
+    (void)connection;
+    (void)elapsed;
+  }
+
+  /** Reports the statistics of the audio stream from each remote
+  user/broadcaster.
+
+  @deprecated This callback is deprecated. Use onRemoteAudioStats instead.
+
+  The SDK triggers this callback once every two seconds to report the audio
+  quality of each remote user/host sending an audio stream. If a channel has
+  multiple remote users/hosts sending audio streams, the SDK triggers this
+  callback as many times.
+
+  @param connection The RtcConnection object.
+  @param remoteUid The user ID of the remote user sending the audio stream.
+  @param quality The audio quality of the user: #QUALITY_TYPE
+  @param delay The network delay (ms) from the sender to the receiver, including the delay caused by audio sampling pre-processing, network transmission, and network jitter buffering.
+  @param lost The audio packet loss rate (%) from the sender to the receiver.
+  */
+  virtual void onAudioQuality(const RtcConnection& connection, uid_t remoteUid, int quality, unsigned short delay, unsigned short lost) __deprecated {
+    (void)connection;
+    (void)remoteUid;
+    (void)quality;
+    (void)delay;
+    (void)lost;
+  }
+  /**
+   * Reports the volume information of users.
+   *
+   * By default, this callback is disabled. You can enable it by calling `enableAudioVolumeIndication`. Once this
+   * callback is enabled and users send streams in the channel, the SDK triggers the `onAudioVolumeIndication`
+   * callback at the time interval set in `enableAudioVolumeIndication`. The SDK triggers two independent
+   * `onAudioVolumeIndication` callbacks simultaneously, which separately report the volume information of the
+   * local user who sends a stream and the remote users (up to three) whose instantaneous volume is the highest.
+   *
+   * @note After you enable this callback, calling muteLocalAudioStream affects the SDK's behavior as follows:
+   * - If the local user stops publishing the audio stream, the SDK stops triggering the local user's callback.
+   * - 20 seconds after a remote user whose volume is one of the three highest stops publishing the audio stream,
+   * the callback excludes this user's information; 20 seconds after all remote users stop publishing audio streams,
+   * the SDK stops triggering the callback for remote users.
+   *
+   * @param connection The RtcConnection object.
+   * @param speakers The volume information of the users, see AudioVolumeInfo. An empty `speakers` array in the
+   * callback indicates that no remote user is in the channel or sending a stream at the moment.
+   * @param speakerNumber The total number of speakers.
+   * - In the local user's callback, when the local user sends a stream, `speakerNumber` is 1.
+   * - In the callback for remote users, the value range of speakerNumber is [0,3]. If the number of remote users who
+   * send streams is greater than or equal to three, the value of `speakerNumber` is 3.
+   * @param totalVolume The volume of the speaker. The value ranges between 0 (lowest volume) and 255 (highest volume).
+   * - In the local user's callback, `totalVolume` is the volume of the local user who sends a stream.
+   * - In the remote users' callback, `totalVolume` is the sum of all remote users (up to three) whose instantaneous
+   * volume is the highest. If the user calls `startAudioMixing`, `totalVolume` is the volume after audio mixing.
+   */
+  virtual void onAudioVolumeIndication(const RtcConnection& connection, const AudioVolumeInfo* speakers,
+                                       unsigned int speakerNumber, int totalVolume) {
+    (void)connection;
+    (void)speakers;
+    (void)speakerNumber;
+    (void)totalVolume;
+  }
+
+  /**
+   * Occurs when a user leaves a channel.
+   *
+   * This callback notifies the app that the user leaves the channel by calling `leaveChannel`. From this callback,
+   * the app can get information such as the call duration and quality statistics.
+   *
+   * @param connection The RtcConnection object.
+   * @param stats The statistics on the call: RtcStats.
+   */
+  virtual void onLeaveChannel(const RtcConnection& connection, const RtcStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /**
+   * Reports the statistics of the current call.
+   *
+   * The SDK triggers this callback once every two seconds after the user joins the channel.
+   *
+   * @param connection The RtcConnection object.
+   * @param stats The statistics of the current call: RtcStats.
+   */
+  virtual void onRtcStats(const RtcConnection& connection, const RtcStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /**
+   * Reports the last mile network quality of each user in the channel.
+   *
+   * This callback reports the last mile network conditions of each user in the channel. Last mile refers to the
+   * connection between the local device and Agora's edge server.
+   *
+   * The SDK triggers this callback once every two seconds. If a channel includes multiple users, the SDK triggers
+   * this callback as many times.
+   *
+   * @note `txQuality` is UNKNOWN when the user is not sending a stream; `rxQuality` is UNKNOWN when the user is not
+   * receiving a stream.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The user ID. The network quality of the user with this user ID is reported.
+   * @param txQuality Uplink network quality rating of the user in terms of the transmission bit rate, packet loss rate,
+   * average RTT (Round-Trip Time) and jitter of the uplink network. This parameter is a quality rating helping you
+   * understand how well the current uplink network conditions can support the selected video encoder configuration.
+   * For example, a 1000 Kbps uplink network may be adequate for video frames with a resolution of 640 × 480 and a frame
+   * rate of 15 fps in the LIVE_BROADCASTING profile, but may be inadequate for resolutions higher than 1280 × 720.
+   * See #QUALITY_TYPE.
+   * @param rxQuality Downlink network quality rating of the user in terms of packet loss rate, average RTT, and jitter
+   * of the downlink network. See #QUALITY_TYPE.
+   */
+  virtual void onNetworkQuality(const RtcConnection& connection, uid_t remoteUid, int txQuality, int rxQuality) {
+    (void)connection;
+    (void)remoteUid;
+    (void)txQuality;
+    (void)rxQuality;
+  }
+
+  /**
+   * Occurs when intra request from remote user is received.
+   *
+   * This callback is triggered once remote user needs one Key frame.
+   *
+   * @param connection The RtcConnection object.
+   */
+  virtual void onIntraRequestReceived(const RtcConnection& connection) {
+    (void)connection;
+  }
+
+  /** Occurs when the first local video frame is published.
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The local client enables the video module and calls `joinChannel` successfully.
+   * - The local client calls `muteLocalVideoStream(true)` and muteLocalVideoStream(false) in sequence.
+   * - The local client calls `disableVideo` and `enableVideo` in sequence.
+   * - The local client calls `pushVideoFrame` to successfully push the video frame to the SDK.
+   *
+   * @param connection The RtcConnection object.
+   * @param elapsed The time elapsed (ms) from the local user calling joinChannel` to the SDK triggers
+   * this callback.
+  */
+  virtual void onFirstLocalVideoFramePublished(const RtcConnection& connection, int elapsed) {
+    (void)connection;
+    (void)elapsed;
+  }
+
+  /** Occurs when the first remote video frame is received and decoded.
+
+  The SDK triggers this callback under one of the following circumstances:
+  - The remote user joins the channel and sends the video stream.
+  - The remote user stops sending the video stream and re-sends it after 15 seconds. Reasons for such an interruption include:
+   - The remote user leaves the channel.
+   - The remote user drops offline.
+   - The remote user calls `muteLocalVideoStream` to stop sending the video stream.
+   - The remote user calls `disableVideo` to disable video.
+
+  @param connection The RtcConnection object.
+  @param remoteUid The user ID of the remote user sending the video stream.
+  @param width The width (pixels) of the video stream.
+  @param height The height (pixels) of the video stream.
+  @param elapsed The time elapsed (ms) from the local user calling `joinChannel`
+  until the SDK triggers this callback.
+  */
+  virtual void onFirstRemoteVideoDecoded(const RtcConnection& connection, uid_t remoteUid, int width, int height, int elapsed) {
+    (void)connection;
+    (void)remoteUid;
+    (void)width;
+    (void)height;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when the local or remote video size or rotation has changed.
+   *
+   * @param connection The RtcConnection object.
+   * @param sourceType The video source type: #VIDEO_SOURCE_TYPE.
+   * @param uid The user ID. 0 indicates the local user.
+   * @param width The new width (pixels) of the video.
+   * @param height The new height (pixels) of the video.
+   * @param rotation The rotation information of the video.
+   */
+  virtual void onVideoSizeChanged(const RtcConnection& connection, VIDEO_SOURCE_TYPE sourceType, uid_t uid, int width, int height, int rotation) {
+    (void)connection;
+    (void)sourceType;
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)rotation;
+  }
+
+  /** Occurs when the local video stream state changes.
+   *
+   * When the state of the local video stream changes (including the state of the video capture and
+   * encoding), the SDK triggers this callback to report the current state. This callback indicates
+   * the state of the local video stream, including camera capturing and video encoding, and allows
+   * you to troubleshoot issues when exceptions occur.
+   *
+   * The SDK triggers the onLocalVideoStateChanged callback with the state code of `LOCAL_VIDEO_STREAM_STATE_FAILED`
+   * and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE` in the following situations:
+   * - The app switches to the background, and the system gets the camera resource.
+   * - The camera starts normally, but does not output video for four consecutive seconds.
+   *
+   * When the camera outputs the captured video frames, if the video frames are the same for 15
+   * consecutive frames, the SDK triggers the `onLocalVideoStateChanged` callback with the state code
+   * of `LOCAL_VIDEO_STREAM_STATE_CAPTURING` and error code of `LOCAL_VIDEO_STREAM_REASON_CAPTURE_FAILURE`.
+   * Note that the video frame duplication detection is only available for video frames with a resolution
+   * greater than 200 × 200, a frame rate greater than or equal to 10 fps, and a bitrate less than 20 Kbps.
+   *
+   * @note For some device models, the SDK does not trigger this callback when the state of the local
+   * video changes while the local video capturing device is in use, so you have to make your own
+   * timeout judgment.
+   *
+   * @param connection The RtcConnection object.
+   * @param state The state of the local video. See #LOCAL_VIDEO_STREAM_STATE.
+   * @param reason The detailed error information. See #LOCAL_VIDEO_STREAM_REASON.
+   */
+  virtual void onLocalVideoStateChanged(const RtcConnection& connection,
+                                        LOCAL_VIDEO_STREAM_STATE state,
+                                        LOCAL_VIDEO_STREAM_REASON reason) {
+    (void)connection;
+    (void)state;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the remote video state changes.
+   *
+   * @note This callback does not work properly when the number of users (in the voice/video call
+   * channel) or hosts (in the live streaming channel) in the channel exceeds 17.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The ID of the user whose video state has changed.
+   * @param state The remote video state: #REMOTE_VIDEO_STATE.
+   * @param reason The reason of the remote video state change: #REMOTE_VIDEO_STATE_REASON.
+   * @param elapsed The time elapsed (ms) from the local client calling `joinChannel` until this callback is triggered.
+   */
+  virtual void onRemoteVideoStateChanged(const RtcConnection& connection, uid_t remoteUid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed) {
+    (void)connection;
+    (void)remoteUid;
+    (void)state;
+    (void)reason;
+    (void)elapsed;
+  }
+
+  /** Occurs when the renderer receives the first frame of the remote video.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The user ID of the remote user sending the video stream.
+   * @param width The width (px) of the video frame.
+   * @param height The height (px) of the video stream.
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` until the SDK triggers this callback.
+   */
+  virtual void onFirstRemoteVideoFrame(const RtcConnection& connection, uid_t remoteUid, int width, int height, int elapsed) {
+    (void)connection;
+    (void)remoteUid;
+    (void)width;
+    (void)height;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a remote user or broadcaster joins the channel.
+   *
+   * - In the COMMUNICATION channel profile, this callback indicates that a remote user joins the channel.
+   * The SDK also triggers this callback to report the existing users in the channel when a user joins the
+   * channel.
+   * In the LIVE_BROADCASTING channel profile, this callback indicates that a host joins the channel. The
+   * SDK also triggers this callback to report the existing hosts in the channel when a host joins the
+   * channel. Agora recommends limiting the number of hosts to 17.
+   *
+   * The SDK triggers this callback under one of the following circumstances:
+   * - A remote user/host joins the channel by calling the `joinChannel` method.
+   * - A remote user switches the user role to the host after joining the channel.
+   * - A remote user/host rejoins the channel after a network interruption.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The ID of the remote user or broadcaster joining the channel.
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` or `setClientRole`
+   * until this callback is triggered.
+  */
+  virtual void onUserJoined(const RtcConnection& connection, uid_t remoteUid, int elapsed) {
+    (void)connection;
+    (void)remoteUid;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when a remote user or broadcaster goes offline.
+   *
+   * There are two reasons for a user to go offline:
+   * - Leave the channel: When the user leaves the channel, the user sends a goodbye message. When this
+   * message is received, the SDK determines that the user leaves the channel.
+   * - Drop offline: When no data packet of the user is received for a certain period of time, the SDK assumes
+   * that the user drops offline. A poor network connection may lead to false detection, so we recommend using
+   * the RTM SDK for reliable offline detection.
+   * - The user switches the user role from a broadcaster to an audience.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The ID of the remote user or broadcaster who leaves the channel or drops offline.
+   * @param reason The reason why the remote user goes offline: #USER_OFFLINE_REASON_TYPE.
+   */
+  virtual void onUserOffline(const RtcConnection& connection, uid_t remoteUid, USER_OFFLINE_REASON_TYPE reason) {
+    (void)connection;
+    (void)remoteUid;
+    (void)reason;
+  }
+
+  /** Occurs when a remote user's audio stream playback pauses/resumes.
+   * The SDK triggers this callback when the remote user stops or resumes sending the audio stream by
+   * calling the `muteLocalAudioStream` method.
+   * @note This callback can be inaccurate when the number of users (in the `COMMUNICATION` profile) or hosts (in the `LIVE_BROADCASTING` profile) in the channel exceeds 17.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The user ID.
+   * @param muted Whether the remote user's audio stream is muted/unmuted:
+   * - true: Muted.
+   * - false: Unmuted.
+   */
+ virtual void onUserMuteAudio(const RtcConnection& connection, uid_t remoteUid, bool muted) __deprecated {
+    (void)connection;
+    (void)remoteUid;
+    (void)muted;
+  }
+
+  /** Occurs when a remote user pauses or resumes sending the video stream.
+   *
+   * When a remote user calls `muteLocalVideoStream` to stop or resume publishing the video stream, the
+   * SDK triggers this callback to report the state of the remote user's publishing stream to the local
+   * user.
+   *
+   * @note This callback can be inaccurate when the number of users or broadacasters in a
+   * channel exceeds 20.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid ID of the remote user.
+   * @param muted Whether the remote user stops publishing the video stream:
+   * - true: The remote user has paused sending the video stream.
+   * - false: The remote user has resumed sending the video stream.
+   */
+ virtual void onUserMuteVideo(const RtcConnection& connection, uid_t remoteUid, bool muted) {
+    (void)connection;
+    (void)remoteUid;
+    (void)muted;
+  }
+
+  /** Occurs when a remote user enables or disables the video module.
+
+  Once the video function is disabled, the users cannot see any video.
+
+  The SDK triggers this callback when a remote user enables or disables the video module by calling the
+  `enableVideo` or `disableVideo` method.
+
+  @param connection The RtcConnection object.
+  @param remoteUid The ID of the remote user.
+  @param enabled Whether the video of the remote user is enabled:
+  - true: The remote user has enabled video.
+  - false: The remote user has disabled video.
+  */
+ virtual void onUserEnableVideo(const RtcConnection& connection, uid_t remoteUid, bool enabled) {
+    (void)connection;
+    (void)remoteUid;
+    (void)enabled;
+  }
+
+  /** Occurs when a remote user enables or disables local video capturing.
+
+  The SDK triggers this callback when the remote user resumes or stops capturing the video stream by
+  calling the `enableLocalVideo` method.
+
+  @param connection The RtcConnection object.
+  @param remoteUid The ID of the remote user.
+  @param enabled Whether the specified remote user enables/disables local video:
+  - `true`: The remote user has enabled local video capturing.
+  - `false`: The remote user has disabled local video capturing.
+  */
+  virtual void onUserEnableLocalVideo(const RtcConnection& connection, uid_t remoteUid, bool enabled) __deprecated {
+    (void)connection;
+    (void)remoteUid;
+    (void)enabled;
+  }
+
+  /**
+   * Occurs when the remote user state is updated.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The uid of the remote user.
+   * @param state The remote user state: #REMOTE_USER_STATE.
+   */
+  virtual void onUserStateChanged(const RtcConnection& connection, uid_t remoteUid, uint32_t state) {
+    (void)connection;
+    (void)remoteUid;
+    (void)state;
+  }
+
+  /** Reports the statistics of the local audio stream.
+   *
+   * The SDK triggers this callback once every two seconds.
+   *
+   * @param connection The RtcConnection object.
+   * @param stats The statistics of the local audio stream.
+   * See LocalAudioStats.
+   */
+  virtual void onLocalAudioStats(const RtcConnection& connection, const LocalAudioStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /** Reports the statistics of the audio stream from each remote user/host.
+
+   The SDK triggers this callback once every two seconds for each remote user who is sending audio
+   streams. If a channel includes multiple remote users, the SDK triggers this callback as many times.
+   @param connection The RtcConnection object.
+   @param stats Statistics of the received remote audio streams. See RemoteAudioStats.
+   */
+  virtual void onRemoteAudioStats(const RtcConnection& connection, const RemoteAudioStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /** Reports the statistics of the local video stream.
+   *
+   * The SDK triggers this callback once every two seconds for each
+   * user/host. If there are multiple users/hosts in the channel, the SDK
+   * triggers this callback as many times.
+   *
+   * @note If you have called the `enableDualStreamMode`
+   * method, this callback reports the statistics of the high-video
+   * stream (high bitrate, and high-resolution video stream).
+   *
+   * @param connection The RtcConnection object.
+   * @param stats Statistics of the local video stream. See LocalVideoStats.
+   */
+  virtual void onLocalVideoStats(const RtcConnection& connection, const LocalVideoStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /** Reports the statistics of the video stream from each remote user/host.
+   *
+   * The SDK triggers this callback once every two seconds for each remote user. If a channel has
+   * multiple users/hosts sending video streams, the SDK triggers this callback as many times.
+   *
+   * @param connection The RtcConnection object.
+   * @param stats Statistics of the remote video stream. See
+   * RemoteVideoStats.
+   */
+  virtual void onRemoteVideoStats(const RtcConnection& connection, const RemoteVideoStats& stats) {
+    (void)connection;
+    (void)stats;
+  }
+
+  /**
+   * Occurs when the SDK cannot reconnect to the server 10 seconds after its connection to the server is
+   * interrupted.
+   *
+   * The SDK triggers this callback when it cannot connect to the server 10 seconds after calling
+   * `joinChannel`, regardless of whether it is in the channel or not. If the SDK fails to rejoin
+   * the channel 20 minutes after being disconnected from Agora's edge server, the SDK stops rejoining the channel.
+   *
+   * @param connection The RtcConnection object.
+   */
+  virtual void onConnectionLost(const RtcConnection& connection) {
+    (void)connection;
+  }
+
+  /** Occurs when the connection between the SDK and the server is interrupted.
+   * @deprecated Use `onConnectionStateChanged` instead.
+
+  The SDK triggers this callback when it loses connection with the serer for more
+  than 4 seconds after the connection is established. After triggering this
+  callback, the SDK tries to reconnect to the server. If the reconnection fails
+  within a certain period (10 seconds by default), the onConnectionLost()
+  callback is triggered. If the SDK fails to rejoin the channel 20 minutes after
+  being disconnected from Agora's edge server, the SDK stops rejoining the channel.
+
+  @param connection The RtcConnection object.
+
+  */
+  virtual void onConnectionInterrupted(const RtcConnection& connection) __deprecated {
+    (void)connection;
+  }
+
+  /** Occurs when your connection is banned by the Agora Server.
+   *
+   * @param connection The RtcConnection object.
+   */
+  virtual void onConnectionBanned(const RtcConnection& connection) {
+    (void)connection;
+  }
+
+  /** Occurs when the local user receives the data stream from the remote user.
+   *
+   * The SDK triggers this callback when the user receives the data stream that another user sends
+   * by calling the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid ID of the user who sends the data stream.
+   * @param streamId The ID of the stream data.
+   * @param data The data stream.
+   * @param length The length (byte) of the data stream.
+   * @param sentTs The time when the data stream sent.
+   */
+  virtual void onStreamMessage(const RtcConnection& connection, uid_t remoteUid, int streamId, const char* data, size_t length, uint64_t sentTs) {
+    (void)connection;
+    (void)remoteUid;
+    (void)streamId;
+    (void)data;
+    (void)length;
+    (void)sentTs;
+  }
+
+  /** Occurs when the local user does not receive the data stream from the remote user.
+   *
+   * The SDK triggers this callback when the user fails to receive the data stream that another user sends
+   * by calling the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid ID of the user who sends the data stream.
+   * @param streamId The ID of the stream data.
+   * @param code The error code.
+   * @param missed The number of lost messages.
+   * @param cached The number of incoming cached messages when the data stream is
+   * interrupted.
+   */
+  virtual void onStreamMessageError(const RtcConnection& connection, uid_t remoteUid, int streamId, int code, int missed, int cached) {
+    (void)connection;
+    (void)remoteUid;
+    (void)streamId;
+    (void)code;
+    (void)missed;
+    (void)cached;
+  }
+
+  /**
+   * Occurs when the token expires.
+   *
+   * When the token expires during a call, the SDK triggers this callback to remind the app to renew the token.
+   *
+   * Upon receiving this callback, generate a new token at your app server and call
+   * `joinChannel` to pass the new token to the SDK.
+   *
+   * @param connection The RtcConnection object.
+   */
+  virtual void onRequestToken(const RtcConnection& connection) {
+    (void)connection;
+  }
+
+  /**
+   * Occurs when connection license verification fails.
+   *
+   * You can know the reason accordding to error code
+   */
+  virtual void onLicenseValidationFailure(const RtcConnection& connection, LICENSE_ERROR_TYPE reason) {
+    (void)connection;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the token will expire in 30 seconds.
+   *
+   * When the token is about to expire in 30 seconds, the SDK triggers this callback to remind the app to renew the token.
+
+   * Upon receiving this callback, generate a new token at your app server and call
+   * \ref IRtcEngine::renewToken "renewToken" to pass the new Token to the SDK.
+   *
+   * @param connection The RtcConnection object.
+   * @param token The token that will expire in 30 seconds.
+   */
+  virtual void onTokenPrivilegeWillExpire(const RtcConnection& connection, const char* token) {
+    (void)connection;
+    (void)token;
+  }
+
+  /** Occurs when the first local audio frame is published.
+   *
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The local client enables the audio module and calls `joinChannel` successfully.
+   * - The local client calls `muteLocalAudioStream(true)` and `muteLocalAudioStream(false)` in sequence.
+   * - The local client calls `disableAudio` and `enableAudio` in sequence.
+   * - The local client calls `pushAudioFrame` to successfully push the audio frame to the SDK.
+   *
+   * @param connection The RtcConnection object.
+   * @param elapsed The time elapsed (ms) from the local user calling `joinChannel` to the SDK triggers this callback.
+   */
+  virtual void onFirstLocalAudioFramePublished(const RtcConnection& connection, int elapsed) {
+    (void)connection;
+    (void)elapsed;
+  }
+
+  /** Occurs when the SDK receives the first audio frame from a specific remote user.
+   * @deprecated Use `onRemoteAudioStateChanged` instead.
+   *
+   * @param connection The RtcConnection object.
+   * @param userId ID of the remote user.
+   * @param elapsed The time elapsed (ms) from the loca user calling `joinChannel`
+   * until this callback is triggered.
+   */
+  virtual void onFirstRemoteAudioFrame(const RtcConnection& connection, uid_t userId, int elapsed) __deprecated {
+    (void)connection;
+    (void)userId;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when the SDK decodes the first remote audio frame for playback.
+   *
+   * @deprecated Use `onRemoteAudioStateChanged` instead.
+   * The SDK triggers this callback under one of the following circumstances:
+   * - The remote user joins the channel and sends the audio stream for the first time.
+   * - The remote user's audio is offline and then goes online to re-send audio. It means the local user cannot
+   * receive audio in 15 seconds. Reasons for such an interruption include:
+   *   - The remote user leaves channel.
+   *   - The remote user drops offline.
+   *   - The remote user calls muteLocalAudioStream to stop sending the audio stream.
+   *   - The remote user calls disableAudio to disable audio.
+   * @param connection The RtcConnection object.
+   * @param uid User ID of the remote user sending the audio stream.
+   * @param elapsed The time elapsed (ms) from the loca user calling `joinChannel`
+   * until this callback is triggered.
+   */
+  virtual void onFirstRemoteAudioDecoded(const RtcConnection& connection, uid_t uid, int elapsed) __deprecated {
+    (void)connection;
+    (void)uid;
+    (void)elapsed;
+  }
+
+  /** Occurs when the local audio state changes.
+   *
+   * When the state of the local audio stream changes (including the state of the audio capture and encoding), the SDK
+   * triggers this callback to report the current state. This callback indicates the state of the local audio stream,
+   * and allows you to troubleshoot issues when audio exceptions occur.
+   *
+   * @note
+   * When the state is `LOCAL_AUDIO_STREAM_STATE_FAILED(3)`, see the `error`
+   * parameter for details.
+   *
+   * @param connection The RtcConnection object.
+   * @param state State of the local audio. See #LOCAL_AUDIO_STREAM_STATE.
+   * @param reason The reason information of the local audio.
+   * See #LOCAL_AUDIO_STREAM_REASON.
+   */
+  virtual void onLocalAudioStateChanged(const RtcConnection& connection, LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_REASON reason) {
+    (void)connection;
+    (void)state;
+    (void)reason;
+  }
+
+  /** Occurs when the remote audio state changes.
+   *
+   * When the audio state of a remote user (in the voice/video call channel) or host (in the live streaming channel)
+   * changes, the SDK triggers this callback to report the current state of the remote audio stream.
+   *
+   * @note This callback does not work properly when the number of users (in the voice/video call channel) or hosts
+   * (in the live streaming channel) in the channel exceeds 17.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid ID of the remote user whose audio state changes.
+   * @param state State of the remote audio. See #REMOTE_AUDIO_STATE.
+   * @param reason The reason of the remote audio state change.
+   * See #REMOTE_AUDIO_STATE_REASON.
+   * @param elapsed Time elapsed (ms) from the local user calling the
+   * `joinChannel` method until the SDK
+   * triggers this callback.
+   */
+  virtual void onRemoteAudioStateChanged(const RtcConnection& connection, uid_t remoteUid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed) {
+    (void)connection;
+    (void)remoteUid;
+    (void)state;
+    (void)reason;
+    (void)elapsed;
+  }
+
+  /**
+   * Occurs when an active speaker is detected.
+   *
+   * After a successful call of `enableAudioVolumeIndication`, the SDK continuously detects which remote user has the
+   * loudest volume. During the current period, the remote user, who is detected as the loudest for the most times,
+   * is the most active user.
+   *
+   * When the number of users is no less than two and an active remote speaker exists, the SDK triggers this callback and reports the uid of the most active remote speaker.
+   * - If the most active remote speaker is always the same user, the SDK triggers the `onActiveSpeaker` callback only once.
+   * - If the most active remote speaker changes to another user, the SDK triggers this callback again and reports the uid of the new active remote speaker.
+   *
+   * @param connection The RtcConnection object.
+   * @param uid The ID of the active speaker. A `uid` of 0 means the local user.
+   */
+  virtual void onActiveSpeaker(const RtcConnection& connection, uid_t uid) {
+    (void)connection;
+    (void)uid;
+  }
+
+  /**
+   * Occurs when the user role switches in the interactive live streaming.
+   *
+   * @param connection The RtcConnection of the local user: #RtcConnection
+   * @param oldRole The old role of the user: #CLIENT_ROLE_TYPE
+   * @param newRole The new role of the user: #CLIENT_ROLE_TYPE
+   * @param newRoleOptions The client role options of the new role: #ClientRoleOptions.
+   */
+  virtual void onClientRoleChanged(const RtcConnection& connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole, const ClientRoleOptions& newRoleOptions) {
+    (void)connection;
+    (void)oldRole;
+    (void)newRole;
+    (void)newRoleOptions;
+  }
+
+  /**
+   * Occurs when the user role in a Live-Broadcast channel fails to switch, for example, from a broadcaster
+   * to an audience or vice versa.
+   *
+   * @param connection The RtcConnection object.
+   * @param reason The reason for failing to change the client role: #CLIENT_ROLE_CHANGE_FAILED_REASON.
+   * @param currentRole The current role of the user: #CLIENT_ROLE_TYPE.
+   */
+  virtual void onClientRoleChangeFailed(const RtcConnection& connection, CLIENT_ROLE_CHANGE_FAILED_REASON reason, CLIENT_ROLE_TYPE currentRole) {
+    (void)connection;
+    (void)reason;
+    (void)currentRole;
+  }
+
+  /** Reports the transport-layer statistics of each remote audio stream.
+   * @deprecated Use `onRemoteAudioStats` instead.
+
+  This callback reports the transport-layer statistics, such as the packet loss rate and network time delay, once every
+  two seconds after the local user receives an audio packet from a remote user. During a call, when the user receives
+  the audio packet sent by the remote user/host, the callback is triggered every 2 seconds.
+
+  @param connection The RtcConnection object.
+  @param remoteUid ID of the remote user whose audio data packet is received.
+  @param delay The network time delay (ms) from the sender to the receiver.
+  @param lost The Packet loss rate (%) of the audio packet sent from the remote
+  user.
+  @param rxKBitRate Received bitrate (Kbps) of the audio packet sent from the
+  remote user.
+  */
+  virtual void onRemoteAudioTransportStats(const RtcConnection& connection, uid_t remoteUid, unsigned short delay, unsigned short lost,
+                                           unsigned short rxKBitRate) __deprecated {
+    (void)connection;
+    (void)remoteUid;
+    (void)delay;
+    (void)lost;
+    (void)rxKBitRate;
+  }
+
+  /** Reports the transport-layer statistics of each remote video stream.
+   * @deprecated Use `onRemoteVideoStats` instead.
+
+  This callback reports the transport-layer statistics, such as the packet loss rate and network time
+  delay, once every two seconds after the local user receives a video packet from a remote user.
+
+  During a call, when the user receives the video packet sent by the remote user/host, the callback is
+  triggered every 2 seconds.
+
+  @param connection The RtcConnection object.
+  @param remoteUid ID of the remote user whose video packet is received.
+  @param delay The network time delay (ms) from the remote user sending the
+  video packet to the local user.
+  @param lost The packet loss rate (%) of the video packet sent from the remote
+  user.
+  @param rxKBitRate The bitrate (Kbps) of the video packet sent from
+  the remote user.
+  */
+  virtual void onRemoteVideoTransportStats(const RtcConnection& connection, uid_t remoteUid, unsigned short delay, unsigned short lost,
+                                           unsigned short rxKBitRate) __deprecated {
+    (void)connection;
+    (void)remoteUid;
+    (void)delay;
+    (void)lost;
+    (void)rxKBitRate;
+  }
+
+  /** Occurs when the network connection state changes.
+   *
+   * When the network connection state changes, the SDK triggers this callback and reports the current
+   * connection state and the reason for the change.
+   *
+   * @param connection The RtcConnection object.
+   * @param state The current connection state. See #CONNECTION_STATE_TYPE.
+   * @param reason The reason for a connection state change. See #CONNECTION_CHANGED_REASON_TYPE.
+   */
+  virtual void onConnectionStateChanged(const RtcConnection& connection,
+                                        CONNECTION_STATE_TYPE state,
+                                        CONNECTION_CHANGED_REASON_TYPE reason) {
+    (void)connection;
+    (void)state;
+    (void)reason;
+  }
+
+  /** Occurs when the WIFI message need be sent to the user.
+   *
+   * @param connection The RtcConnection object.
+   * @param reason The reason of notifying the user of a message.
+   * @param action Suggest an action for the user.
+   * @param wlAccMsg The message content of notifying the user.
+   */
+  virtual void onWlAccMessage(const RtcConnection& connection, WLACC_MESSAGE_REASON reason, WLACC_SUGGEST_ACTION action, const char* wlAccMsg) {
+    (void)connection;
+    (void)reason;
+    (void)action;
+    (void)wlAccMsg;
+  }
+
+  /** Occurs when SDK statistics wifi acceleration optimization effect.
+   *
+   * @param connection The RtcConnection object.
+   * @param currentStats Instantaneous value of optimization effect.
+   * @param averageStats Average value of cumulative optimization effect.
+   */
+  virtual void onWlAccStats(const RtcConnection& connection, WlAccStats currentStats, WlAccStats averageStats) {
+    (void)connection;
+    (void)currentStats;
+    (void)averageStats;
+  }
+
+  /** Occurs when the local network type changes.
+   *
+   * This callback occurs when the connection state of the local user changes. You can get the
+   * connection state and reason for the state change in this callback. When the network connection
+   * is interrupted, this callback indicates whether the interruption is caused by a network type
+   * change or poor network conditions.
+   * @param connection The RtcConnection object.
+   * @param type The type of the local network connection. See #NETWORK_TYPE.
+   */
+  virtual void onNetworkTypeChanged(const RtcConnection& connection, NETWORK_TYPE type) {
+    (void)connection;
+    (void)type;
+  }
+
+  /** Reports the built-in encryption errors.
+   *
+   * When encryption is enabled by calling `enableEncryption`, the SDK triggers this callback if an
+   * error occurs in encryption or decryption on the sender or the receiver side.
+   * @param connection The RtcConnection object.
+   * @param errorType The error type. See #ENCRYPTION_ERROR_TYPE.
+   */
+  virtual void onEncryptionError(const RtcConnection& connection, ENCRYPTION_ERROR_TYPE errorType) {
+    (void)connection;
+    (void)errorType;
+  }
+  /**
+   * Reports the user log upload result
+   * @param connection The RtcConnection object.
+   * @param requestId RequestId of the upload
+   * @param success Is upload success
+   * @param reason Reason of the upload, 0: OK, 1 Network Error, 2 Server Error.
+   */
+  virtual void onUploadLogResult(const RtcConnection& connection, const char* requestId, bool success, UPLOAD_ERROR_REASON reason) {
+    (void)connection;
+    (void)requestId;
+    (void)success;
+    (void)reason;
+  }
+
+  /**
+   * Occurs when the user account is updated.
+   *
+   * @param connection The RtcConnection object.
+   * @param remoteUid The user ID.
+   * @param userAccount The user account.
+   */
+  virtual void onUserAccountUpdated(const RtcConnection& connection, uid_t remoteUid, const char* remoteUserAccount){
+    (void)connection;
+    (void)remoteUid;
+    (void)remoteUserAccount;
+  }
+
+  /** Reports the result of taking a video snapshot.
+   *
+   * After a successful `takeSnapshot` method call, the SDK triggers this callback to report whether the snapshot is
+   * successfully taken, as well as the details for that snapshot.
+   * @param connection The RtcConnection object.
+   * @param uid The user ID. A `uid` of 0 indicates the local user.
+   * @param filePath The local path of the snapshot.
+   * @param width The width (px) of the snapshot.
+   * @param height The height (px) of the snapshot.
+   * @param errCode The message that confirms success or gives the reason why the snapshot is not successfully taken:
+   * - 0: Success.
+   * - &lt; 0: Failure.
+   *   - -1: The SDK fails to write data to a file or encode a JPEG image.
+   *   - -2: The SDK does not find the video stream of the specified user within one second after the `takeSnapshot` method call succeeds.
+   *   - -3: Calling the `takeSnapshot` method too frequently. Call the `takeSnapshot` method after receiving the `onSnapshotTaken`
+   * callback from the previous call.
+   */
+  virtual void onSnapshotTaken(const RtcConnection& connection, uid_t uid, const char* filePath, int width, int height, int errCode) {
+    (void)uid;
+    (void)filePath;
+    (void)width;
+    (void)height;
+    (void)errCode;
+  }
+
+  /**
+   * Reports the tracing result of video rendering event of the user.
+   * 
+   * @param connection The RtcConnection object.
+   * @param uid The user ID.
+   * @param currentEvent The current event of the tracing result: #MEDIA_TRACE_EVENT.
+   * @param tracingInfo The tracing result: #VideoRenderingTracingInfo.
+   */
+  virtual void onVideoRenderingTracingResult(const RtcConnection& connection, uid_t uid, MEDIA_TRACE_EVENT currentEvent, VideoRenderingTracingInfo tracingInfo) {
+    (void)uid;
+    (void)currentEvent;
+    (void)tracingInfo;
+  }
+
+  /**
+   * Occurs when receive use rtm response.
+   *
+   * @param connection The RtcConnection object.
+   * @param code The error code:
+   */
+  virtual void onSetRtmFlagResult(const RtcConnection& connection, int code) {
+    (void)connection;
+    (void)code;
+  }
+  /**
+   * Occurs when receive a video transcoder stream which has video layout info.
+   *
+   * @param connection The RtcConnection object.
+   * @param uid user id of the transcoded stream.
+   * @param width width of the transcoded stream.
+   * @param height height of the transcoded stream.
+   * @param layoutCount count of layout info in the transcoded stream.
+   * @param layoutlist video layout info list of the transcoded stream.
+   */
+  virtual void onTranscodedStreamLayoutInfo(const RtcConnection& connection, uid_t uid, int width, int height, int layoutCount,const VideoLayout* layoutlist) {
+    (void)uid;
+    (void)width;
+    (void)height;
+    (void)layoutCount;
+    (void)layoutlist;
+  }
+
+  /**
+   * The audio metadata received.
+   *
+   * @param connection The RtcConnection object.
+   * @param uid ID of the remote user.
+   * @param metadata The pointer of metadata
+   * @param length Size of metadata
+   * @technical preview 
+   */
+  virtual void onAudioMetadataReceived(const RtcConnection& connection, uid_t uid, const char* metadata, size_t length) {
+    (void)metadata;
+    (void)length;
+  }
+};
+
+class IRtcEngineEx : public IRtcEngine {
+public:
+   /**
+    * Joins a channel with media options.
+    *
+    * This method enables users to join a channel. Users in the same channel can talk to each other,
+    * and multiple users in the same channel can start a group chat. Users with different App IDs
+    * cannot call each other.
+    *
+    * A successful call of this method triggers the following callbacks:
+    * - The local client: The `onJoinChannelSuccess` and `onConnectionStateChanged` callbacks.
+    * - The remote client: `onUserJoined`, if the user joining the channel is in the Communication
+    * profile or is a host in the Live-broadcasting profile.
+    *
+    * When the connection between the client and Agora's server is interrupted due to poor network
+    * conditions, the SDK tries reconnecting to the server. When the local client successfully rejoins
+    * the channel, the SDK triggers the `onRejoinChannelSuccess` callback on the local client.
+    *
+    * Compared to `joinChannel`, this method adds the options parameter to configure whether to
+    * automatically subscribe to all remote audio and video streams in the channel when the user
+    * joins the channel. By default, the user subscribes to the audio and video streams of all
+    * the other users in the channel, giving rise to usage and billings. To unsubscribe, set the
+    * `options` parameter or call the `mute` methods accordingly.
+    *
+    * @note
+    * - This method allows users to join only one channel at a time.
+    * - Ensure that the app ID you use to generate the token is the same app ID that you pass in the
+    * `initialize` method; otherwise, you may fail to join the channel by token.
+    *
+    * @param connection The RtcConnection object.
+    * @param token The token generated on your server for authentication.
+    * @param options The channel media options: ChannelMediaOptions.
+    * @param eventHandler The event handler: IRtcEngineEventHandler.
+    *
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    *   - -2: The parameter is invalid. For example, the token is invalid, the uid parameter is not set
+    * to an integer, or the value of a member in the `ChannelMediaOptions` structure is invalid. You need
+    * to pass in a valid parameter and join the channel again.
+    *   - -3: Failes to initialize the `IRtcEngine` object. You need to reinitialize the IRtcEngine object.
+    *   - -7: The IRtcEngine object has not been initialized. You need to initialize the IRtcEngine
+    * object before calling this method.
+    *   - -8: The internal state of the IRtcEngine object is wrong. The typical cause is that you call
+    * this method to join the channel without calling `stopEchoTest` to stop the test after calling
+    * `startEchoTest` to start a call loop test. You need to call `stopEchoTest` before calling this method.
+    *   - -17: The request to join the channel is rejected. The typical cause is that the user is in the
+    * channel. Agora recommends using the `onConnectionStateChanged` callback to get whether the user is
+    * in the channel. Do not call this method to join the channel unless you receive the
+    * `CONNECTION_STATE_DISCONNECTED(1)` state.
+    *   - -102: The channel name is invalid. You need to pass in a valid channel name in channelId to
+    * rejoin the channel.
+    *   - -121: The user ID is invalid. You need to pass in a valid user ID in uid to rejoin the channel.
+    */
+    virtual int joinChannelEx(const char* token, const RtcConnection& connection,
+                              const ChannelMediaOptions& options,
+                              IRtcEngineEventHandler* eventHandler) = 0;
+
+  /**
+   * Leaves the channel.
+   *
+   * This method allows a user to leave the channel, for example, by hanging up or exiting a call.
+   *
+   * This method is an asynchronous call, which means that the result of this method returns even before
+   * the user has not actually left the channel. Once the user successfully leaves the channel, the
+   * SDK triggers the \ref IRtcEngineEventHandler::onLeaveChannel "onLeaveChannel" callback.
+   *
+   * @param connection The RtcConnection object.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+    virtual int leaveChannelEx(const RtcConnection& connection) = 0;
+
+    /**
+     * Leaves the channel with the connection ID.
+     *
+     * @param connection connection.
+     * @param options The options for leaving the channel. See #LeaveChannelOptions.
+     * @return int
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int leaveChannelEx(const RtcConnection& connection, const LeaveChannelOptions& options) = 0;
+
+  /**
+    * Leaves a channel with the channel ID and user account.
+    *
+    * This method allows a user to leave the channel, for example, by hanging up or exiting a call.
+    *
+    * This method is an asynchronous call, which means that the result of this method returns even before
+    * the user has not actually left the channel. Once the user successfully leaves the channel, the
+    * SDK triggers the \ref IRtcEngineEventHandler::onLeaveChannel "onLeaveChannel" callback.
+    *
+    * @param channelId The channel name. The maximum length of this parameter is 64 bytes. Supported character scopes are:
+    * - All lowercase English letters: a to z.
+    * - All uppercase English letters: A to Z.
+    * - All numeric characters: 0 to 9.
+    * - The space character.
+    * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+    * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+    * - All lowercase English letters: a to z.
+    * - All uppercase English letters: A to Z.
+    * - All numeric characters: 0 to 9.
+    * - The space character.
+    * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int leaveChannelWithUserAccountEx(const char* channelId, const char* userAccount) = 0;
+
+  /**
+    * Leaves a channel with the channel ID and user account and sets the options for leaving.
+    *
+    * @param channelId The channel name. The maximum length of this parameter is 64 bytes. Supported character scopes are:
+    * - All lowercase English letters: a to z.
+    * - All uppercase English letters: A to Z.
+    * - All numeric characters: 0 to 9.
+    * - The space character.
+    * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+    * @param userAccount The user account. The maximum length of this parameter is 255 bytes. Ensure that you set this parameter and do not set it as null. Supported character scopes are:
+    * - All lowercase English letters: a to z.
+    * - All uppercase English letters: A to Z.
+    * - All numeric characters: 0 to 9.
+    * - The space character.
+    * - Punctuation characters and other symbols, including: "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+    * @param options The options for leaving the channel. See #LeaveChannelOptions.
+    * @return int
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int leaveChannelWithUserAccountEx(const char* channelId, const char* userAccount, const LeaveChannelOptions& options) = 0;
+
+    /**
+     *  Updates the channel media options after joining the channel.
+     *
+     * @param options The channel media options: ChannelMediaOptions.
+     * @param connection The RtcConnection object.
+     * @return int
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int updateChannelMediaOptionsEx(const ChannelMediaOptions& options, const RtcConnection& connection) = 0;
+    /**
+     * Sets the video encoder configuration.
+     *
+     * Each configuration profile corresponds to a set of video parameters, including
+     * the resolution, frame rate, and bitrate.
+     *
+     * The parameters specified in this method are the maximum values under ideal network conditions.
+     * If the video engine cannot render the video using the specified parameters due
+     * to poor network conditions, the parameters further down the list are considered
+     * until a successful configuration is found.
+     *
+     * @param config The local video encoder configuration: VideoEncoderConfiguration.
+     * @param connection The RtcConnection object.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setVideoEncoderConfigurationEx(const VideoEncoderConfiguration& config, const RtcConnection& connection) = 0;
+    /**
+     * Initializes the video view of a remote user.
+     *
+     * This method initializes the video view of a remote stream on the local device. It affects only the
+     * video view that the local user sees.
+     *
+     * Usually the app should specify the `uid` of the remote video in the method call before the
+     * remote user joins the channel. If the remote `uid` is unknown to the app, set it later when the
+     * app receives the \ref IRtcEngineEventHandler::onUserJoined "onUserJoined" callback.
+     *
+     * To unbind the remote user from the view, set `view` in VideoCanvas as `null`.
+     *
+     * @note
+     * Ensure that you call this method in the UI thread.
+     *
+     * @param canvas The remote video view settings: VideoCanvas.
+     * @param connection The RtcConnection object.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setupRemoteVideoEx(const VideoCanvas& canvas, const RtcConnection& connection) = 0;
+    /**
+     * Stops or resumes receiving the audio stream of a specified user.
+     *
+     * @note
+     * You can call this method before or after joining a channel. If a user
+     * leaves a channel, the settings in this method become invalid.
+     *
+     * @param uid The ID of the specified user.
+     * @param mute Whether to stop receiving the audio stream of the specified user:
+     * - true: Stop receiving the audio stream of the specified user.
+     * - false: (Default) Resume receiving the audio stream of the specified user.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int muteRemoteAudioStreamEx(uid_t uid, bool mute, const RtcConnection& connection) = 0;
+    /**
+     * Stops or resumes receiving the video stream of a specified user.
+     *
+     * @note
+     * You can call this method before or after joining a channel. If a user
+     * leaves a channel, the settings in this method become invalid.
+     *
+     * @param uid The ID of the specified user.
+     * @param mute Whether to stop receiving the video stream of the specified user:
+     * - true: Stop receiving the video stream of the specified user.
+     * - false: (Default) Resume receiving the video stream of the specified user.
+     * @param connection The RtcConnetion object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int muteRemoteVideoStreamEx(uid_t uid, bool mute, const RtcConnection& connection) = 0;
+    /**
+     * Sets the remote video stream type.
+     *
+     * If the remote user has enabled the dual-stream mode, by default the SDK receives the high-stream video by
+     * Call this method to switch to the low-stream video.
+     *
+     * @note
+     * This method applies to scenarios where the remote user has enabled the dual-stream mode using
+     * \ref enableDualStreamMode "enableDualStreamMode"(true) before joining the channel.
+     *
+     * @param uid ID of the remote user sending the video stream.
+     * @param streamType Sets the video stream type: #VIDEO_STREAM_TYPE.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setRemoteVideoStreamTypeEx(uid_t uid, VIDEO_STREAM_TYPE streamType, const RtcConnection& connection) = 0;
+    /**
+     *Stops or resumes sending the local audio stream with connection.
+     *
+     *@param mute Determines whether to send or stop sending the local audio stream:
+     *- true: Stop sending the local audio stream.
+     *- false: Send the local audio stream.
+     *
+     *@param connection The connection of the user ID.
+     *
+     *@return
+     *- 0: Success.
+     *- < 0: Failure.
+     */
+    virtual int muteLocalAudioStreamEx(bool mute, const RtcConnection& connection) = 0;
+  
+    /**
+     *Stops or resumes sending the local video stream with connection.
+     *
+     *@param mute Determines whether to send or stop sending the local video stream:
+     *- true: Stop sending the local video stream.
+     *- false: Send the local video stream.
+     *
+     *@param connection The connection of the user ID.
+     *
+     *@return
+     *- 0: Success.
+     *- < 0: Failure.
+     */
+    virtual int muteLocalVideoStreamEx(bool mute, const RtcConnection& connection) = 0;
+    
+    /**
+     *Stops or resumes receiving all remote audio stream with connection.
+     *
+     *@param mute Whether to stop receiving remote audio streams:
+     *- true: Stop receiving any remote audio stream.
+     *- false: Resume receiving all remote audio streams.
+     *
+     *@param connection The connection of the user ID.
+     *
+     *@return
+     *- 0: Success.
+     *- < 0: Failure.
+     */
+    virtual int muteAllRemoteAudioStreamsEx(bool mute, const RtcConnection& connection) = 0;
+  
+    /**
+     *Stops or resumes receiving all remote video stream with connection.
+     *
+     *@param mute Whether to stop receiving remote audio streams:
+     *- true: Stop receiving any remote audio stream.
+     *- false: Resume receiving all remote audio streams.
+     *
+     *@param connection The connection of the user ID.
+     *
+     *@return
+     *- 0: Success.
+     *- < 0: Failure.
+     */
+    virtual int muteAllRemoteVideoStreamsEx(bool mute, const RtcConnection& connection) = 0;
+
+
+    /**
+     * Sets the blocklist of subscribe remote stream audio.
+     *
+     * @note
+     * If uid is in uidList, the remote user's audio will not be subscribed,
+     * even if muteRemoteAudioStream(uid, false) and muteAllRemoteAudioStreams(false) are operated.
+     *
+     * @param uidList The id list of users who do not subscribe to audio.
+     * @param uidNumber The number of uid in uidList.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setSubscribeAudioBlocklistEx(uid_t* uidList, int uidNumber, const RtcConnection& connection) = 0;
+
+    /**
+     * Sets the allowlist of subscribe remote stream audio.
+     *
+     * @note
+     * - If uid is in uidList, the remote user's audio will be subscribed,
+     * even if muteRemoteAudioStream(uid, true) and muteAllRemoteAudioStreams(true) are operated.
+     * - If a user is in the blacklist and whitelist at the same time, the user will not subscribe to audio.
+     *
+     * @param uidList The id list of users who do subscribe to audio.
+     * @param uidNumber The number of uid in uidList.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setSubscribeAudioAllowlistEx(uid_t* uidList, int uidNumber, const RtcConnection& connection) = 0;
+
+    /**
+     * Sets the blocklist of subscribe remote stream video.
+     *
+     * @note
+     * If uid is in uidList, the remote user's video will not be subscribed,
+     * even if muteRemoteVideoStream(uid, false) and muteAllRemoteVideoStreams(false) are operated.
+     *
+     * @param uidList The id list of users who do not subscribe to video.
+     * @param uidNumber The number of uid in uidList.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setSubscribeVideoBlocklistEx(uid_t* uidList, int uidNumber, const RtcConnection& connection) = 0;
+
+    /**
+     * Sets the allowlist of subscribe remote stream video.
+     *
+     * @note
+     * - If uid is in uidList, the remote user's video will be subscribed,
+     * even if muteRemoteVideoStream(uid, true) and muteAllRemoteVideoStreams(true) are operated.
+     * - If a user is in the blacklist and whitelist at the same time, the user will not subscribe to video.
+     *
+     * @param uidList The id list of users who do subscribe to video.
+     * @param uidNumber The number of uid in uidList.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setSubscribeVideoAllowlistEx(uid_t* uidList, int uidNumber, const RtcConnection& connection) = 0;
+    /**
+     * Sets the remote video subscription options
+     *
+     *
+     * @param uid ID of the remote user sending the video stream.
+     * @param options Sets the video subscription options: VideoSubscriptionOptions.
+     * @param connection The RtcConnection object.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setRemoteVideoSubscriptionOptionsEx(uid_t uid, const VideoSubscriptionOptions& options, const RtcConnection& connection) = 0;
+    /** Sets the sound position and gain of a remote user.
+
+    When the local user calls this method to set the sound position of a remote user, the sound difference between the left and right channels allows the local user to track the real-time position of the remote user, creating a real sense of space. This method applies to massively multiplayer online games, such as Battle Royale games.
+
+    @note
+    - For this method to work, enable stereo panning for remote users by calling the \ref agora::rtc::IRtcEngine::enableSoundPositionIndication "enableSoundPositionIndication" method before joining a channel.
+    - This method requires hardware support. For the best sound positioning, we recommend using a wired headset.
+    - Ensure that you call this method after joining a channel.
+
+    @param uid The ID of the remote user.
+    @param pan The sound position of the remote user. The value ranges from -1.0 to 1.0:
+    - 0.0: the remote sound comes from the front.
+    - -1.0: the remote sound comes from the left.
+    - 1.0: the remote sound comes from the right.
+    @param gain Gain of the remote user. The value ranges from 0.0 to 100.0. The default value is 100.0 (the original gain of the remote user). The smaller the value, the less the gain.
+    @param connection The RtcConnection object.
+
+    @return
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int setRemoteVoicePositionEx(uid_t uid, double pan, double gain, const RtcConnection& connection) = 0;
+    /** Sets remote user parameters for spatial audio
+
+    @param uid The ID of the remote user.
+    @param param Spatial audio parameters. See SpatialAudioParams.
+    @param connection The RtcConnection object.
+
+    @return int
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int setRemoteUserSpatialAudioParamsEx(uid_t uid, const agora::SpatialAudioParams& params, const RtcConnection& connection) = 0;
+    /**
+     * Updates the display mode of the video view of a remote user.
+     *
+     * After initializing the video view of a remote user, you can call this method to update its
+     * rendering and mirror modes. This method affects only the video view that the local user sees.
+     *
+     * @note
+     * - Ensure that you have called \ref setupRemoteVideo "setupRemoteVideo" to initialize the remote video
+     * view before calling this method.
+     * - During a call, you can call this method as many times as necessary to update the display mode
+     * of the video view of a remote user.
+     *
+     * @param uid ID of the remote user.
+     * @param renderMode Sets the remote display mode. See #RENDER_MODE_TYPE.
+     * @param mirrorMode Sets the mirror type. See #VIDEO_MIRROR_MODE_TYPE.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int setRemoteRenderModeEx(uid_t uid, media::base::RENDER_MODE_TYPE renderMode,
+                                      VIDEO_MIRROR_MODE_TYPE mirrorMode, const RtcConnection& connection) = 0;
+    /** Enables loopback recording.
+     *
+     * If you enable loopback recording, the output of the default sound card is mixed into
+     * the audio stream sent to the other end.
+     *
+     * @note This method is for Windows only.
+     *
+     * @param connection The RtcConnection object.
+     * @param enabled Sets whether to enable/disable loopback recording.
+     * - true: Enable loopback recording.
+     * - false: (Default) Disable loopback recording.
+     * @param deviceName Pointer to the device name of the sound card. The default value is NULL (the default sound card).
+     * - This method is for macOS and Windows only.
+     * - macOS does not support loopback capturing of the default sound card. If you need to use this method,
+     * please use a virtual sound card and pass its name to the deviceName parameter. Agora has tested and recommends using soundflower.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int enableLoopbackRecordingEx(const RtcConnection& connection, bool enabled, const char* deviceName = NULL) = 0;
+    
+    /**
+     * Adjusts the recording volume.
+     *
+     * @param volume The recording volume, which ranges from 0 to 400:
+     * - 0  : Mute the recording volume.
+     * - 100: The original volume.
+     * - 400: (Maximum) Four times the original volume with signal clipping protection.
+     *
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0  : Success.
+     * - < 0: Failure.
+     */
+    virtual int adjustRecordingSignalVolumeEx(int volume, const RtcConnection& connection) = 0;
+    
+    /**
+     * Mute or resume recording signal volume.
+     *
+     * @param mute Determines whether to mute or resume the recording signal volume.
+     * -  true: Mute the recording signal volume.
+     * - false: (Default) Resume the recording signal volume.
+     *
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0  : Success.
+     * - < 0: Failure.
+     */
+    virtual int muteRecordingSignalEx(bool mute, const RtcConnection& connection) = 0;
+
+    /**
+     * Adjust the playback signal volume of a specified remote user.
+     * You can call this method as many times as necessary to adjust the playback volume of different remote users, or to repeatedly adjust the playback volume of the same remote user.
+     * 
+     * @note
+     * The playback volume here refers to the mixed volume of a specified remote user.
+     * This method can only adjust the playback volume of one specified remote user at a time. To adjust the playback volume of different remote users, call the method as many times, once for each remote user.
+     * 
+     * @param uid The ID of the remote user.
+     * @param volume The playback volume of the specified remote user. The value ranges between 0 and 400, including the following:
+     * 
+     * - 0: Mute.
+     * - 100: (Default) Original volume.
+     * @param connection  RtcConnection
+     * 
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */    
+    virtual int adjustUserPlaybackSignalVolumeEx(uid_t uid, int volume, const RtcConnection& connection) = 0;
+
+    /** Gets the current connection state of the SDK.
+     @param connection The RtcConnection object.
+     @return #CONNECTION_STATE_TYPE.
+     */
+    virtual CONNECTION_STATE_TYPE getConnectionStateEx(const RtcConnection& connection) = 0;
+    /** Enables/Disables the built-in encryption.
+     *
+     * In scenarios requiring high security, Agora recommends calling this method to enable the built-in encryption before joining a channel.
+     *
+     * All users in the same channel must use the same encryption mode and encryption key. Once all users leave the channel, the encryption key of this channel is automatically cleared.
+     *
+     * @note
+     * - If you enable the built-in encryption, you cannot use the RTMP streaming function.
+     *
+     * @param connection The RtcConnection object.
+     * @param enabled Whether to enable the built-in encryption:
+     * - true: Enable the built-in encryption.
+     * - false: Disable the built-in encryption.
+     * @param config Configurations of built-in encryption schemas. See EncryptionConfig.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     *  - -2(ERR_INVALID_ARGUMENT): An invalid parameter is used. Set the parameter with a valid value.
+     *  - -4(ERR_NOT_SUPPORTED): The encryption mode is incorrect or the SDK fails to load the external encryption library. Check the enumeration or reload the external encryption library.
+     *  - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. Initialize the `IRtcEngine` instance before calling this method.
+     */
+    virtual int enableEncryptionEx(const RtcConnection& connection, bool enabled, const EncryptionConfig& config) = 0;
+    /** Creates a data stream.
+     *
+     * You can call this method to create a data stream and improve the
+     * reliability and ordering of data tranmission.
+     *
+     * @note
+     * - Ensure that you set the same value for `reliable` and `ordered`.
+     * - Each user can only create a maximum of 5 data streams during a RtcEngine
+     * lifecycle.
+     * - The data channel allows a data delay of up to 5 seconds. If the receiver
+     * does not receive the data stream within 5 seconds, the data channel reports
+     * an error.
+     *
+     * @param[out] streamId The ID of the stream data.
+     * @param reliable Sets whether the recipients are guaranteed to receive
+     * the data stream from the sender within five seconds:
+     * - true: The recipients receive the data stream from the sender within
+     * five seconds. If the recipient does not receive the data stream within
+     * five seconds, an error is reported to the application.
+     * - false: There is no guarantee that the recipients receive the data stream
+     * within five seconds and no error message is reported for any delay or
+     * missing data stream.
+     * @param ordered Sets whether the recipients receive the data stream
+     * in the sent order:
+     * - true: The recipients receive the data stream in the sent order.
+     * - false: The recipients do not receive the data stream in the sent order.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int createDataStreamEx(int* streamId, bool reliable, bool ordered, const RtcConnection& connection) = 0;
+    /** Creates a data stream.
+     *
+     * Each user can create up to five data streams during the lifecycle of the IChannel.
+     * @param streamId The ID of the created data stream.
+     * @param config  The config of data stream.
+     * @param connection The RtcConnection object.
+     * @return int
+     * - Returns 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int createDataStreamEx(int* streamId, const DataStreamConfig& config, const RtcConnection& connection) = 0;
+    /** Sends a data stream.
+     *
+     * After calling \ref IRtcEngine::createDataStream "createDataStream", you can call
+     * this method to send a data stream to all users in the channel.
+     *
+     * The SDK has the following restrictions on this method:
+     * - Up to 60 packets can be sent per second in a channel with each packet having a maximum size of 1 KB.
+     * - Each client can send up to 30 KB of data per second.
+     * - Each user can have up to five data streams simultaneously.
+     *
+     * After the remote user receives the data stream within 5 seconds, the SDK triggers the
+     * \ref IRtcEngineEventHandler::onStreamMessage "onStreamMessage" callback on
+     * the remote client. After the remote user does not receive the data stream within 5 seconds,
+     * the SDK triggers the \ref IRtcEngineEventHandler::onStreamMessageError "onStreamMessageError"
+     * callback on the remote client.
+     *
+     * @note
+     * - Call this method after calling \ref IRtcEngine::createDataStream "createDataStream".
+     * - This method applies only to the `COMMUNICATION` profile or to
+     * the hosts in the `LIVE_BROADCASTING` profile. If an audience in the
+     * `LIVE_BROADCASTING` profile calls this method, the audience may be switched to a host.
+     *
+     * @param streamId The ID of the stream data.
+     * @param data The data stream.
+     * @param length The length (byte) of the data stream.
+     * @param connection The RtcConnection object.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int sendStreamMessageEx(int streamId, const char* data, size_t length, const RtcConnection& connection) = 0;
+    /** Adds a watermark image to the local video.
+
+    This method adds a PNG watermark image to the local video in a live broadcast. Once the watermark image is added, all the audience in the channel (CDN audience included),
+    and the recording device can see and capture it. Agora supports adding only one watermark image onto the local video, and the newly watermark image replaces the previous one.
+
+    The watermark position depends on the settings in the \ref IRtcEngine::setVideoEncoderConfiguration "setVideoEncoderConfiguration" method:
+    - If the orientation mode of the encoding video is #ORIENTATION_MODE_FIXED_LANDSCAPE, or the landscape mode in #ORIENTATION_MODE_ADAPTIVE, the watermark uses the landscape orientation.
+    - If the orientation mode of the encoding video is #ORIENTATION_MODE_FIXED_PORTRAIT, or the portrait mode in #ORIENTATION_MODE_ADAPTIVE, the watermark uses the portrait orientation.
+    - When setting the watermark position, the region must be less than the dimensions set in the `setVideoEncoderConfiguration` method. Otherwise, the watermark image will be cropped.
+
+    @note
+    - Ensure that you have called the \ref agora::rtc::IRtcEngine::enableVideo "enableVideo" method to enable the video module before calling this method.
+    - If you only want to add a watermark image to the local video for the audience in the CDN live broadcast channel to see and capture, you can call this method or the \ref agora::rtc::IRtcEngine::setLiveTranscoding "setLiveTranscoding" method.
+    - This method supports adding a watermark image in the PNG file format only. Supported pixel formats of the PNG image are RGBA, RGB, Palette, Gray, and Alpha_gray.
+    - If the dimensions of the PNG image differ from your settings in this method, the image will be cropped or zoomed to conform to your settings.
+    - If you have enabled the local video preview by calling the \ref agora::rtc::IRtcEngine::startPreview "startPreview" method, you can use the `visibleInPreview` member in the WatermarkOptions class to set whether or not the watermark is visible in preview.
+    - If you have enabled the mirror mode for the local video, the watermark on the local video is also mirrored. To avoid mirroring the watermark, Agora recommends that you do not use the mirror and watermark functions for the local video at the same time. You can implement the watermark function in your application layer.
+
+    @param watermarkUrl The local file path of the watermark image to be added. This method supports adding a watermark image from the local absolute or relative file path.
+    @param options Pointer to the watermark's options to be added. See WatermarkOptions for more infomation.
+    @param connection The RtcConnection object.
+
+    @return int
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int addVideoWatermarkEx(const char* watermarkUrl, const WatermarkOptions& options, const RtcConnection& connection) = 0;
+    /** Removes the watermark image on the video stream added by
+    addVideoWatermark().
+
+    @param connection The RtcConnection object.
+    @return
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int clearVideoWatermarkEx(const RtcConnection& connection) = 0;
+    /** Agora supports reporting and analyzing customized messages.
+     *
+     * This function is in the beta stage with a free trial. The ability provided
+     * in its beta test version is reporting a maximum of 10 message pieces within
+     * 6 seconds, with each message piece not exceeding 256 bytes.
+     *
+     * To try out this function, contact [support@agora.io](mailto:support@agora.io)
+     * and discuss the format of customized messages with us.
+     */
+    virtual int sendCustomReportMessageEx(const char* id, const char* category, const char* event, const char* label,
+                                          int value, const RtcConnection& connection) = 0;
+
+    /**
+     * Enables the `onAudioVolumeIndication` callback to report on which users are speaking
+     * and the speakers' volume.
+     *
+     * Once the \ref IRtcEngineEventHandler::onAudioVolumeIndication "onAudioVolumeIndication"
+     * callback is enabled, the SDK returns the volume indication in the at the time interval set
+     * in `enableAudioVolumeIndication`, regardless of whether any user is speaking in the channel.
+     *
+     * @param interval Sets the time interval between two consecutive volume indications:
+     * - <= 0: Disables the volume indication.
+     * - > 0: Time interval (ms) between two consecutive volume indications,
+     * and should be integral multiple of 200 (less than 200 will be set to 200).
+     * @param smooth The smoothing factor that sets the sensitivity of the audio volume
+     * indicator. The value range is [0, 10]. The greater the value, the more sensitive the
+     * indicator. The recommended value is 3.
+     * @param reportVad
+     * - `true`: Enable the voice activity detection of the local user. Once it is enabled, the `vad` parameter of the
+     * `onAudioVolumeIndication` callback reports the voice activity status of the local user.
+     * - `false`: (Default) Disable the voice activity detection of the local user. Once it is disabled, the `vad` parameter
+     * of the `onAudioVolumeIndication` callback does not report the voice activity status of the local user, except for
+     * the scenario where the engine automatically detects the voice activity of the local user.
+     * @param connection The RtcConnection object.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     */
+    virtual int enableAudioVolumeIndicationEx(int interval, int smooth, bool reportVad, const RtcConnection& connection) = 0;
+  
+    /** Publishes the local stream without transcoding to a specified CDN live RTMP address.  (CDN live only.)
+      *
+      * @param url The CDN streaming URL in the RTMP format. The maximum length of this parameter is 1024 bytes.
+      * @param connection RtcConnection.
+      *
+      * @return
+      * - 0: Success.
+      * - < 0: Failure.
+      */
+    virtual int startRtmpStreamWithoutTranscodingEx(const char* url, const RtcConnection& connection) = 0;
+  
+    /** Publishes the local stream with transcoding to a specified CDN live RTMP address.  (CDN live only.)
+      *
+      * @param url The CDN streaming URL in the RTMP format. The maximum length of this parameter is 1024 bytes.
+      * @param transcoding Sets the CDN live audio/video transcoding settings.  See LiveTranscoding.
+      * @param connection RtcConnection.
+      *
+      * @return
+      * - 0: Success.
+      * - < 0: Failure.
+      */
+    virtual int startRtmpStreamWithTranscodingEx(const char* url, const LiveTranscoding& transcoding, const RtcConnection& connection) = 0;
+  
+    /** Update the video layout and audio settings for CDN live. (CDN live only.)
+      * @note This method applies to Live Broadcast only.
+      *
+      * @param transcoding Sets the CDN live audio/video transcoding settings. See LiveTranscoding.
+      * @param connection RtcConnection.
+      *
+      * @return
+      * - 0: Success.
+      * - < 0: Failure.
+      */
+    virtual int updateRtmpTranscodingEx(const LiveTranscoding& transcoding, const RtcConnection& connection) = 0;
+  
+    /** Stop an RTMP stream with transcoding or without transcoding from the CDN. (CDN live only.)
+      * @param url The RTMP URL address to be removed. The maximum length of this parameter is 1024 bytes.
+      * @param connection RtcConnection.
+      * @return
+      * - 0: Success.
+      * - < 0: Failure.
+      */
+    virtual int stopRtmpStreamEx(const char* url, const RtcConnection& connection) = 0;
+  
+    /** Starts relaying media streams across channels or updates the channels for media relay.
+     *
+     * @since v4.2.0
+     * @param configuration The configuration of the media stream relay:ChannelMediaRelayConfiguration.
+     * @param connection RtcConnection.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -8(ERR_INVALID_STATE): The current status is invalid, only allowed to be called when the role is the broadcaster.
+     */
+    virtual int startOrUpdateChannelMediaRelayEx(const ChannelMediaRelayConfiguration& configuration, const RtcConnection& connection) = 0;
+  
+    /** Stops the media stream relay.
+     *
+     * Once the relay stops, the host quits all the destination
+     * channels.
+     *
+     * @param connection RtcConnection.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+     */
+    virtual int stopChannelMediaRelayEx(const RtcConnection& connection) = 0;
+  
+    /** pause the channels for media stream relay.
+     *
+     * @param connection RtcConnection.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+     */
+    virtual int pauseAllChannelMediaRelayEx(const RtcConnection& connection) = 0;
+
+    /** resume the channels for media stream relay.
+     *
+     * @param connection RtcConnection.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     *   - -1(ERR_FAILED): A general error occurs (no specified reason).
+     *   - -2(ERR_INVALID_ARGUMENT): The argument is invalid.
+     *   - -5(ERR_REFUSED): The request is rejected.
+     *   - -7(ERR_NOT_INITIALIZED): cross channel media streams are not relayed.
+     */
+    virtual int resumeAllChannelMediaRelayEx(const RtcConnection& connection) = 0;
+
+   /** Gets the user information by passing in the user account.
+    *  It is same as agora::rtc::IRtcEngine::getUserInfoByUserAccount.
+    *
+    * @param userAccount The user account of the user. Ensure that you set this parameter.
+    * @param [in,out] userInfo  A userInfo object that identifies the user:
+    * - Input: A userInfo object.
+    * - Output: A userInfo object that contains the user account and user ID of the user.
+    * @param connection The RtcConnection object.
+    *
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int getUserInfoByUserAccountEx(const char* userAccount, rtc::UserInfo* userInfo, const RtcConnection& connection) = 0;
+
+    /** Gets the user information by passing in the user ID.
+    *  It is same as agora::rtc::IRtcEngine::getUserInfoByUid.
+    *
+    * @param uid The user ID of the remote user. Ensure that you set this parameter.
+    * @param[in,out] userInfo A userInfo object that identifies the user:
+    * - Input: A userInfo object.
+    * - Output: A userInfo object that contains the user account and user ID of the user.
+    * @param connection The RtcConnection object.
+    *
+    * @return
+    * - 0: Success.
+    * - < 0: Failure.
+    */
+    virtual int getUserInfoByUidEx(uid_t uid, rtc::UserInfo* userInfo, const RtcConnection& connection) = 0;
+
+     /**
+     * Enables or disables the dual video stream mode.
+     *
+     * @deprecated v4.2.0. This method is deprecated. Use setDualStreamModeEx instead
+     *
+     * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+     * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video
+     * stream) video using {@link setRemoteVideoStreamType setRemoteVideoStreamType}.
+     *
+     * @param enabled
+     * - true: Enable the dual-stream mode.
+     * - false: (default) Disable the dual-stream mode.
+     * @param streamConfig The minor stream config
+     * @param connection The RtcConnection object.
+     */
+    virtual int enableDualStreamModeEx(bool enabled, const SimulcastStreamConfig& streamConfig,
+                                       const RtcConnection& connection) = 0;
+    /**
+     * Enables, disables or auto enable the dual video stream mode.
+     *
+     * If dual-stream mode is enabled, the subscriber can choose to receive the high-stream
+     * (high-resolution high-bitrate video stream) or low-stream (low-resolution low-bitrate video
+     * stream) video using {@link setRemoteVideoStreamType setRemoteVideoStreamType}.
+     *
+     * @param mode The dual stream mode: #SIMULCAST_STREAM_MODE.
+     * @param streamConfig The configuration of the low stream: SimulcastStreamConfig.
+     * @param connection The RtcConnection object.
+     */
+    virtual int setDualStreamModeEx(SIMULCAST_STREAM_MODE mode,
+                                   const SimulcastStreamConfig& streamConfig,
+                                   const RtcConnection& connection) = 0;
+
+    /**
+     * Set the multi-layer video stream configuration.
+     *
+     * If multi-layer is configed, the subscriber can choose to receive the coresponding layer
+     * of video stream using {@link setRemoteVideoStreamType setRemoteVideoStreamType}.
+     *
+     * @param simulcastConfig
+     * - The configuration for multi-layer video stream. It includes seven layers, ranging from
+     *   STREAM_LAYER_1 to STREAM_LOW. A maximum of 3 layers can be enabled simultaneously.
+     * @param connection The RtcConnection object.
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+     * @technical preview
+     */
+    virtual int setSimulcastConfigEx(const SimulcastConfig& simulcastConfig,
+                                     const RtcConnection& connection) = 0;
+    
+  /**
+    * Set the high priority user list and their fallback level in weak network condition.
+    *
+    * @note
+    * - This method can be called before and after joining a channel.
+    * - If a subscriber is set to high priority, this stream only fallback to lower stream after all normal priority users fallback to their fallback level on weak network condition if needed.
+    *
+    * @param uidList The high priority user list.
+    * @param uidNum The size of uidList.
+    * @param option The fallback level of high priority users.
+    * @param connection An output parameter which is used to control different connection instances.
+    *
+    * @return int
+    * - 0 : Success.
+    * - <0 : Failure.
+    */
+    virtual int setHighPriorityUserListEx(uid_t* uidList, int uidNum,
+                                          STREAM_FALLBACK_OPTIONS option,
+                                          const RtcConnection& connection) = 0;
+
+  /**
+   * Takes a snapshot of a video stream.
+   *
+   * This method takes a snapshot of a video stream from the specified user, generates a JPG
+   * image, and saves it to the specified path.
+   *
+   * The method is asynchronous, and the SDK has not taken the snapshot when the method call
+   * returns. After a successful method call, the SDK triggers the `onSnapshotTaken` callback
+   * to report whether the snapshot is successfully taken, as well as the details for that
+   * snapshot.
+   *
+   * @note
+   * - Call this method after joining a channel.
+   * - This method takes a snapshot of the published video stream specified in `ChannelMediaOptions`.
+   * - If the user's video has been preprocessed, for example, watermarked or beautified, the resulting
+   * snapshot includes the pre-processing effect.
+   * @param connection The RtcConnection object.
+   * @param uid The user ID. Set uid as 0 if you want to take a snapshot of the local user's video.
+   * @param filePath The local path (including filename extensions) of the snapshot. For example:
+   * - Windows: `C:\Users\<user_name>\AppData\Local\Agora\<process_name>\example.jpg`
+   * - iOS: `/App Sandbox/Library/Caches/example.jpg`
+   * - macOS: `~/Library/Logs/example.jpg`
+   * - Android: `/storage/emulated/0/Android/data/<package name>/files/example.jpg`
+   *
+   * Ensure that the path you specify exists and is writable.
+   * @return
+   * - 0 : Success.
+   * - < 0 : Failure.
+   */
+    virtual int takeSnapshotEx(const RtcConnection& connection, uid_t uid, const char* filePath)  = 0;
+
+  /**
+   * Takes a snapshot of a video stream.
+   *
+   * This method takes a snapshot of a video stream from the specified user, generates a JPG
+   * image, and saves it to the specified path.
+   *
+   * The method is asynchronous, and the SDK has not taken the snapshot when the method call
+   * returns. After a successful method call, the SDK triggers the `onSnapshotTaken` callback
+   * to report whether the snapshot is successfully taken, as well as the details for that
+   * snapshot.
+   *
+   * @note
+   * - Call this method after joining a channel.
+   * - This method takes a snapshot of the published video stream specified in `ChannelMediaOptions`.
+   *
+   * @param connection The RtcConnection object.
+   * @param uid The user ID. Set uid as 0 if you want to take a snapshot of the local user's video.
+   * @param config The configuration for the take snapshot. See SnapshotConfig.
+   *
+   * Ensure that the path you specify exists and is writable.
+   * @return
+   * - 0 : Success.
+   * - &lt; 0: Failure.
+   *   - -4: Incorrect observation position. Modify the input observation position according to the reqiurements specified in SnapshotConfig.
+   */
+    virtual int takeSnapshotEx(const RtcConnection& connection, uid_t uid, const media::SnapshotConfig& config)  = 0;
+    
+    /** Enables video screenshot and upload with the connection ID.
+    @param enabled Whether to enable video screenshot and upload:
+    - `true`: Yes.
+    - `false`: No.
+    @param config The configuration for video screenshot and upload.
+    @param connection The connection information. See RtcConnection.
+    @return
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int enableContentInspectEx(bool enabled, const media::ContentInspectConfig &config, const RtcConnection& connection) = 0;
+
+    /**
+     @brief Start tracing media rendering events.
+     @since v4.1.1
+     @discussion
+     - SDK will trace media rendering events when this API is called.
+     - The tracing result can be obtained through callback `IRtcEngineEventHandler(Ex)::onVideoRenderingTracingResult`
+     @param connection The RtcConnection object.
+     @note
+     - By default, SDK will trace media rendering events when `IRtcEngineEx::joinChannelEx` is called.
+     - The start point of event tracing will be reset after leaving channel.
+     @return
+     - 0: Success.
+     - < 0: Failure.
+      - -2(ERR_INVALID_ARGUMENT): The parameter is invalid. Check the channel ID and local uid set by parameter `connection`.
+      - -7(ERR_NOT_INITIALIZED): The SDK is not initialized. Initialize the `IRtcEngine` instance before calling this method.
+     */
+    virtual int startMediaRenderingTracingEx(const RtcConnection& connection) = 0;
+
+    /** Provides the technical preview functionalities or special customizations by configuring the SDK with JSON options.
+    @since v4.3.0
+    @param connection The connection information. See RtcConnection.
+    @param parameters Pointer to the set parameters in a JSON string.
+    @return
+    - 0: Success.
+    - < 0: Failure.
+    */
+    virtual int setParametersEx(const RtcConnection& connection, const char* parameters) = 0;
+
+    /**
+     * Gets the current call ID.
+     *
+     * When a user joins a channel on a client, a `callId` is generated to identify
+     * the call.
+     *
+     * After a call ends, you can call `rate` or `complain` to gather feedback from the customer.
+     * These methods require a `callId` parameter. To use these feedback methods, call the this
+     * method first to retrieve the `callId` during the call, and then pass the value as an
+     * argument in the `rate` or `complain` method after the call ends.
+     *
+     * @param callId The reference to the call ID.
+     * @param connection The RtcConnection object.
+     * @return
+     * - The call ID if the method call is successful.
+     * - < 0: Failure.
+    */
+    virtual int getCallIdEx(agora::util::AString& callId, const RtcConnection& connection) = 0;
+
+    /**
+     * send audio metadata
+     * @since v4.3.1
+     * @param connection The RtcConnection object.
+     * @param metadata The pointer of metadata
+     * @param length Size of metadata
+     * @return
+     * - 0: success
+     * - <0: failure
+     * @technical preview
+    */
+    virtual int sendAudioMetadataEx(const RtcConnection& connection, const char* metadata, size_t length) = 0;
+
+    /** Preloads a specified audio effect.
+     *
+     * This method preloads only one specified audio effect into the memory each time
+     * it is called. To preload multiple audio effects, call this method multiple times.
+     *
+     * After preloading, you can call \ref IRtcEngine::playEffect "playEffect"
+     * to play the preloaded audio effect or call
+     * \ref IRtcEngine::playAllEffects "playAllEffects" to play all the preloaded
+     * audio effects.
+     *
+     * @note
+     * - To ensure smooth communication, limit the size of the audio effect file.
+     * - Agora recommends calling this method before joining the channel.
+     *
+     * @param connection The RtcConnection object.
+     * @param soundId The ID of the audio effect.
+     * @param filePath The absolute path of the local audio effect file or the URL
+     * of the online audio effect file. Supported audio formats: mp3, mp4, m4a, aac,
+     * 3gp, mkv, and wav.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+    */
+    virtual int preloadEffectEx(const RtcConnection& connection, int soundId, const char* filePath, int startPos = 0) = 0;
+
+    /** Plays a specified audio effect.
+     *
+     *
+     * This method plays only one specified audio effect each time it is called.
+     * To play multiple audio effects, call this method multiple times.
+     *
+     * @note
+     * - Agora recommends playing no more than three audio effects at the same time.
+     * - The ID and file path of the audio effect in this method must be the same
+     * as that in the \ref IRtcEngine::preloadEffect "preloadEffect" method.
+     *
+     * @param connection The RtcConnection object.
+     * @param soundId The ID of the audio effect.
+     * @param filePath The absolute path of the local audio effect file or the URL
+     * of the online audio effect file. Supported audio formats: mp3, mp4, m4a, aac,
+     * 3gp, mkv, and wav.
+     * @param loopCount The number of times the audio effect loops:
+     * - `-1`: Play the audio effect in an indefinite loop until
+     * \ref IRtcEngine::stopEffect "stopEffect" or
+     * \ref IRtcEngine::stopAllEffects "stopAllEffects"
+     * - `0`: Play the audio effect once.
+     * - `1`: Play the audio effect twice.
+     * @param pitch The pitch of the audio effect. The value ranges between 0.5 and 2.0.
+     * The default value is `1.0` (original pitch). The lower the value, the lower the pitch.
+     * @param pan The spatial position of the audio effect. The value ranges between -1.0 and 1.0:
+     * - `-1.0`: The audio effect displays to the left.
+     * - `0.0`: The audio effect displays ahead.
+     * - `1.0`: The audio effect displays to the right.
+     * @param gain The volume of the audio effect. The value ranges between 0 and 100.
+     * The default value is `100` (original volume). The lower the value, the lower
+     * the volume of the audio effect.
+     * @param publish Sets whether to publish the audio effect to the remote:
+     * - true: Publish the audio effect to the remote.
+     * - false: (Default) Do not publish the audio effect to the remote.
+     *
+     * @return
+     * - 0: Success.
+     * - < 0: Failure.
+    */
+    virtual int playEffectEx(const RtcConnection& connection, int soundId, const char* filePath, int loopCount, double pitch, double pan, int gain, bool publish = false, int startPos = 0) = 0;
+};
+
+}  // namespace rtc
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAgoraSpatialAudio.h b/headers/rtc_4.3.2.11/include/IAgoraSpatialAudio.h
new file mode 100644
index 0000000..1c96632
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAgoraSpatialAudio.h
@@ -0,0 +1,302 @@
+//
+//  AgoraRtcEngine SDK
+//
+//  Copyright (c) 2019 Agora.io. All rights reserved.
+//
+
+#pragma once
+
+#include <stdint.h>
+#include "AgoraBase.h"
+#include "AgoraMediaBase.h"
+#include "AgoraRefPtr.h"
+#include "IAgoraRtcEngineEx.h"
+
+namespace agora {
+namespace rtc {
+
+// The information of remote voice position
+struct RemoteVoicePositionInfo {
+  // The coordnate of remote voice source, (x, y, z)
+  float position[3];
+  // The forward vector of remote voice, (x, y, z). When it's not set, the vector is forward to listner.
+  float forward[3];
+};
+
+struct SpatialAudioZone {
+  //the zone id
+  int zoneSetId;
+  //zone center point
+  float position[3];
+  //forward direction 
+  float forward[3];
+  //right direction
+  float right[3];
+  //up direction
+  float up[3];
+  //the forward side length of the zone
+  float forwardLength;
+  //tehe right side length of the zone
+  float rightLength;
+  //the up side length of the zone
+  float upLength;
+  //the audio attenuation of zone
+  float audioAttenuation;
+};
+
+/** The definition of LocalSpatialAudioConfig
+ */
+struct LocalSpatialAudioConfig {
+  /*The reference to \ref IRtcEngine, which is the base interface class of the Agora RTC SDK and provides
+   * the real-time audio and video communication functionality.
+   */
+  agora::rtc::IRtcEngine* rtcEngine;
+
+  LocalSpatialAudioConfig() : rtcEngine(NULL) {}
+};
+
+/** The IBaseSpatialAudioEngine class provides the common methods of ICloudSpatialAudioEngine and ILocalSpatialAudioEngine.
+ */
+class ILocalSpatialAudioEngine: public RefCountInterface {
+ protected:
+  virtual ~ILocalSpatialAudioEngine() {}
+
+ public:
+  /**
+   * Releases all the resources occupied by spatial audio engine.
+   */
+  virtual void release() = 0;
+
+  /**
+   * Initializes the ILocalSpatialAudioEngine object and allocates the internal resources.
+   *
+   * @note Ensure that you call IRtcEngine::queryInterface and initialize before calling any other ILocalSpatialAudioEngine APIs.
+   *
+   * @param config The pointer to the LocalSpatialAudioConfig. See #LocalSpatialAudioConfig.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int initialize(const LocalSpatialAudioConfig& config) = 0;
+  /**
+   * Updates the position information of remote user. You should call it when remote user whose role is broadcaster moves.
+   *
+   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
+   * @param posInfo The position information of remote user. See #RemoteVoicePositionInfo.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int updateRemotePosition(uid_t uid, const RemoteVoicePositionInfo &posInfo) = 0;
+  /**
+   * Updates the position of remote user. It's supposed to use with IRtcEngineEx::joinChannelEx.
+   *
+   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
+   * @param posInfo The position information of remote user. See #RemoteVoicePositionInfo.
+   * @param connection The RTC connection whose spatial audio effect you want to update. See #RtcConnection.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int updateRemotePositionEx(uid_t uid, const RemoteVoicePositionInfo &posInfo, const RtcConnection& connection) = 0;
+  /**
+   * Remove the position information of remote user. You should call it when remote user called IRtcEngine::leaveChannel.
+   *
+   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int removeRemotePosition(uid_t uid) = 0;
+  /**
+   * Remove the position information of remote user. It's supposed to use with IRtcEngineEx::joinChannelEx.
+   *
+   * @param uid The remote user ID. It should be the same as RTC channel remote user id.
+   * @param connection The RTC connection whose spatial audio effect you want to update. See #RtcConnection.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int removeRemotePositionEx(uid_t uid, const RtcConnection& connection) = 0;
+  /**
+   * Clear the position informations of remote users. It's supposed to use with IRtcEngineEx::joinChannelEx.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int clearRemotePositionsEx(const RtcConnection& connection) = 0;
+  /**
+   * Updates the position of local user. This method is used in scene with multi RtcConnection.
+   *
+   * @note
+   * - This method is only effective in ILocalSpatialAudioEngine currently.
+   *
+   * @param position The sound position of the user. The coordinate order is forward, right, and up.
+   * @param axisForward The vector in the direction of the forward axis in the coordinate system.
+   * @param axisRight The vector in the direction of the right axis in the coordinate system.
+   * @param axisUp The vector in the direction of the up axis in the coordinate system.
+   * @param connection The RTC connection whose spatial audio effect you want to update.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int updateSelfPositionEx(const float position[3], const float axisForward[3], const float axisRight[3], const float axisUp[3], const RtcConnection& connection) = 0;
+
+  /**
+   * This method sets the maximum number of streams that a player can receive in a
+   * specified audio reception range.
+   *
+   * @note You can call this method either before or after calling enterRoom:
+   * - Calling this method before enterRoom affects the maximum number of received streams
+   *   the next time the player enters a room.
+   * - Calling this method after entering a room changes the current maximum number of
+   *   received streams of the player.
+   *
+   * @param maxCount The maximum number of streams that a player can receive within
+   * a specified audio reception range. If the number of receivable streams exceeds
+   * the set value, the SDK receives the set number of streams closest to the player.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setMaxAudioRecvCount(int maxCount) = 0;
+  /**
+   * This method sets the audio reception range. The unit of the audio reception range
+   * is the same as the unit of distance in the game engine.
+   *
+   * @note You can call this method either before or after calling enterRoom.
+   * During the game, you can call it multiple times to update the audio reception range.
+   *
+   * @param range The maximum audio reception range, in the unit of game engine distance.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setAudioRecvRange(float range) = 0;
+
+  /**
+   * This method sets distance unit of game engine. The smaller the unit is, the sound fades slower
+   * with distance.
+   *
+   * @note You can call this method either before or after calling enterRoom.
+   * During the game, you can call it multiple times to update the distance unit.
+   *
+   * @param unit The number of meters that the game engine distance per unit is equal to. For example, setting unit as 2 means the game engine distance per unit equals 2 meters.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setDistanceUnit(float unit) = 0;
+  /**
+   * Updates the position of local user.
+   * When calling it in ICloudSpatialAudioEngine, it triggers the SDK to update the user position to the Agora spatial audio server. The Agora spatial audio server uses the users' world coordinates and audio reception range to determine whether they are within each other's specified audio reception range.
+   * When calling it in ILocalSpatialAudioEngine, it triggers the SDK to calculate the relative position between the local and remote users and updates spatial audio parameters.
+   *
+   * when calling it in ICloudSpatialAudioEngine, you should notice:
+   * @note
+   * - Call the method after calling enterRoom.
+   * - The call frequency is determined by the app. Agora recommends calling this method every
+   *   120 to 7000 ms. Otherwise, the SDK may lose synchronization with the server.
+   *
+   * @param position The sound position of the user. The coordinate order is forward, right, and up.
+   * @param axisForward The vector in the direction of the forward axis in the coordinate system.
+   * @param axisRight The vector in the direction of the right axis in the coordinate system.
+   * @param axisUp The vector in the direction of the up axis in the coordinate system.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int updateSelfPosition(const float position[3], const float axisForward[3], const float axisRight[3], const float axisUp[3]) = 0;
+  /**
+   * Updates the position of a media player in scene. This method has same behavior both in ICloudSpatialAudioEngine and ILocalSpatialAudioEngine.
+   *
+   * @note
+   * - This method is suggested to be called once if you don't move media player in the virtual space.
+   *
+   * @param playerId The ID of the media player. You can get it by IMediaPlayer::getMediaPlayerId.
+   * @param positionInfo The position information of media player in the virtual space. For details inforamtion, see the declaration of RemoteVoicePositionInfo.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int updatePlayerPositionInfo(int playerId, const RemoteVoicePositionInfo& positionInfo) = 0;
+
+  /**
+   * Set parameters for spatial audio engine. It's deprecated for using.
+   *
+   * @param params The parameter string.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setParameters(const char* params) = 0;
+
+  /**
+   * Mute or unmute local audio stream.
+   *
+   * @param mute When it's false, it will send local audio stream, otherwise it will not send local audio stream.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int muteLocalAudioStream(bool mute) = 0;
+  /**
+   * Mute all remote audio streams. It determines wether SDK receves remote audio streams or not.
+   *
+   * @param mute When it's false, SDK will receive remote audio streams, otherwise SDK will not receive remote audio streams.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int muteAllRemoteAudioStreams(bool mute) = 0;
+  
+  /**
+   * Mute or unmute remote user audio stream.
+   *
+   * @param uid  The ID of the remote user.
+   * @param mute When it's false, SDK will receive remote user audio streams, otherwise SDK will not receive remote user audio streams.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int muteRemoteAudioStream(uid_t uid, bool mute) = 0;
+  
+  virtual int setRemoteAudioAttenuation(uid_t uid, double attenuation, bool forceSet) = 0;
+    
+  /**
+   * Setting up sound Space
+   *
+   * @param zones The Sound space array
+   * @param zoneCount the sound Space count of  array
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setZones(const SpatialAudioZone *zones, unsigned int zoneCount) = 0;
+  /**
+   * Set the audio attenuation coefficient of the player
+   * @param playerId The ID of the media player. You can get it by IMediaPlayer::getMediaPlayerId.
+   * @param attenuation The audio attenuation of the  media player.
+   * @param forceSet Whether to force the setting of audio attenuation coefficient.
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int setPlayerAttenuation(int playerId, double attenuation, bool forceSet) = 0;
+  /**
+   * Clear the position informations of remote users.
+   *
+   * @return
+   * - 0: Success.
+   * - <0: Failure.
+   */
+  virtual int clearRemotePositions() = 0;  
+};
+
+}  // namespace rtc
+}  // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/IAudioDeviceManager.h b/headers/rtc_4.3.2.11/include/IAudioDeviceManager.h
new file mode 100644
index 0000000..ab3cfe5
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/IAudioDeviceManager.h
@@ -0,0 +1,532 @@
+//
+//  Agora SDK
+//
+//  Copyright (c) 2021 Agora.io. All rights reserved.
+//
+#pragma once // NOLINT(build/header_guard)
+
+namespace agora {
+namespace rtc {
+
+/**
+ * The maximum device ID length.
+ */
+enum MAX_DEVICE_ID_LENGTH_TYPE {
+  /**
+   * The maximum device ID length is 512.
+   */
+  MAX_DEVICE_ID_LENGTH = 512
+};
+
+/**
+ * The IAudioDeviceCollection class.
+ */
+class IAudioDeviceCollection {
+public:
+  virtual ~IAudioDeviceCollection() {}
+
+  /**
+   * Gets the total number of the playback or recording devices.
+   *
+   * Call \ref IAudioDeviceManager::enumeratePlaybackDevices
+   * "enumeratePlaybackDevices" first, and then call this method to return the
+   * number of the audio playback devices.
+   *
+   * @return
+   * - The number of the audio devices, if the method call succeeds.
+   * - < 0, if the method call fails.
+   */
+  virtual int getCount() = 0;
+
+  /**
+   * Gets the information of a specified audio device.
+   * @param index An input parameter that specifies the audio device.
+   * @param deviceName An output parameter that indicates the device name.
+   * @param deviceId An output parameter that indicates the device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH],
+                        char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the information of a specified audio device.
+   * @note 
+   * @param index An input parameter that specifies the audio device.
+   * @param deviceName An output parameter that indicates the device name.
+   * @param deviceTypeName An output parameter that indicates the device type name. such as Built-in, USB, HDMI, etc. (MacOS only)
+   * @param deviceId An output parameter that indicates the device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDevice(int index, char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH],
+                        char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;                      
+
+  /**
+   * Specifies a device with the device ID.
+   * @param deviceId The device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setDevice(const char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the default audio device of the system (for macOS and Windows only).
+   *
+   * @param deviceName The name of the system default audio device.
+   * @param deviceId The device ID of the the system default audio device.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+   /**
+   * Gets the default audio device of the system (for macOS and Windows only).
+   *
+   * @param deviceName The name of the system default audio device.
+   * @param deviceTypeName The device type name of the the system default audio device, such as Built-in, USB, HDMI, etc. (MacOS only)
+   * @param deviceId The device ID of the the system default audio device.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getDefaultDevice(char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH], char deviceId[MAX_DEVICE_ID_LENGTH]) = 0; 
+
+  /**
+   * Sets the volume of the app.
+   *
+   * @param volume The volume of the app. The value range is [0, 255].
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setApplicationVolume(int volume) = 0;
+
+  /**
+   * Gets the volume of the app.
+   *
+   * @param volume The volume of the app. The value range is [0, 255]
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getApplicationVolume(int &volume) = 0;
+
+  /** Mutes or unmutes the app.
+   *
+   * @param mute Determines whether to mute the app:
+   * - true: Mute the app.
+   * - false: Unmute the app.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setApplicationMute(bool mute) = 0;
+
+  /**
+   * Gets the mute state of the app.
+   *
+   * @param mute A reference to the mute state of the app:
+   * - true: The app is muted.
+   * - false: The app is not muted.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int isApplicationMute(bool &mute) = 0;
+
+  /**
+   * Releases all IAudioDeviceCollection resources.
+   */
+  virtual void release() = 0;
+};
+
+/**
+ * The IAudioDeviceManager class.
+ */
+class IAudioDeviceManager : public RefCountInterface {
+public:
+  virtual ~IAudioDeviceManager() {}
+
+  /**
+   * Enumerates the audio playback devices.
+   *
+   * This method returns an IAudioDeviceCollection object that includes all the
+   * audio playback devices in the system. With the IAudioDeviceCollection
+   * object, the app can enumerate the audio playback devices. The app must call
+   * the \ref IAudioDeviceCollection::release "IAudioDeviceCollection::release"
+   * method to release the returned object after using it.
+   *
+   * @return
+   * - A pointer to the IAudioDeviceCollection object that includes all the
+   * audio playback devices in the system, if the method call succeeds.
+   * - The empty pointer NULL, if the method call fails.
+   */
+  virtual IAudioDeviceCollection *enumeratePlaybackDevices() = 0;
+
+  /**
+   * Enumerates the audio recording devices.
+   *
+   * This method returns an IAudioDeviceCollection object that includes all the
+   * audio recording devices in the system. With the IAudioDeviceCollection
+   * object, the app can enumerate the audio recording devices. The app needs to
+   * call the \ref IAudioDeviceCollection::release
+   * "IAudioDeviceCollection::release" method to release the returned object
+   * after using it.
+   *
+   * @return
+   * - A pointer to the IAudioDeviceCollection object that includes all the
+   * audio recording devices in the system, if the method call succeeds.
+   * - The empty pointer NULL, if the method call fails.
+   */
+  virtual IAudioDeviceCollection *enumerateRecordingDevices() = 0;
+
+  /**
+   * Specifies an audio playback device with the device ID.
+   *
+   * @param deviceId ID of the audio playback device. It can be retrieved by the
+   * \ref enumeratePlaybackDevices "enumeratePlaybackDevices" method. Plugging
+   * or unplugging the audio device does not change the device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackDevice(const char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the ID of the audio playback device.
+   * @param deviceId An output parameter that specifies the ID of the audio
+   * playback device.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlaybackDevice(char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the device ID and device name of the audio playback device.
+   * @param deviceId An output parameter that specifies the ID of the audio
+   * playback device.
+   * @param deviceName An output parameter that specifies the name of the audio
+   * playback device.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlaybackDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH],
+                                    char deviceName[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the device ID and device name and device type name of the audio playback device.
+   * @param deviceId An output parameter that specifies the ID of the audio playback device.
+   * @param deviceName An output parameter that specifies the name of the audio playback device.
+   * @param deviceTypeName An output parameter that specifies the device type name. such as Built-in, USB, HDMI, etc. (MacOS only)
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlaybackDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Sets the volume of the audio playback device.
+   * @param volume The volume of the audio playing device. The value range is
+   * [0, 255].
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackDeviceVolume(int volume) = 0;
+
+  /**
+   * Gets the volume of the audio playback device.
+   * @param volume The volume of the audio playback device. The value range is
+   * [0, 255].
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlaybackDeviceVolume(int *volume) = 0;
+
+  /**
+   * Specifies an audio recording device with the device ID.
+   *
+   * @param deviceId ID of the audio recording device. It can be retrieved by
+   * the \ref enumerateRecordingDevices "enumerateRecordingDevices" method.
+   * Plugging or unplugging the audio device does not change the device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRecordingDevice(const char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the audio recording device by the device ID.
+   *
+   * @param deviceId ID of the audio recording device.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getRecordingDevice(char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the information of the audio recording device by the device ID and
+   * device name.
+   *
+   * @param deviceId ID of the audio recording device.
+   * @param deviceName The name of the audio recording device.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getRecordingDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH],
+                                     char deviceName[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the device ID and device name and device type name of the audio recording device.
+   *
+   * @param deviceId An output parameter that indicates the device id.
+   * @param deviceName An output parameter that indicates the device name.
+   * @param deviceTypeName An output parameter that indicates the device type name. such as Built-in, USB, HDMI, etc. (MacOS only)
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getRecordingDeviceInfo(char deviceId[MAX_DEVICE_ID_LENGTH], char deviceName[MAX_DEVICE_ID_LENGTH], char deviceTypeName[MAX_DEVICE_ID_LENGTH]) = 0;    
+                               
+  /**
+   * Sets the volume of the recording device.
+   * @param volume The volume of the recording device. The value range is [0,
+   * 255].
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRecordingDeviceVolume(int volume) = 0;
+
+  /**
+   * Gets the volume of the recording device.
+   * @param volume The volume of the microphone, ranging from 0 to 255.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getRecordingDeviceVolume(int *volume) = 0;
+
+  /**
+   * Specifies an audio loopback recording device with the device ID.
+   *
+   * @param deviceId ID of the audio loopback recording device. It can be retrieved by
+   * the \ref enumeratePlaybackDevices "enumeratePlaybackDevices" method.
+   * Plugging or unplugging the audio device does not change the device ID.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setLoopbackDevice(const char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Gets the audio loopback recording device by the device ID.
+   *
+   * @param deviceId ID of the audio loopback recording device.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getLoopbackDevice(char deviceId[MAX_DEVICE_ID_LENGTH]) = 0;
+
+  /**
+   * Mutes or unmutes the audio playback device.
+   *
+   * @param mute Determines whether to mute the audio playback device.
+   * - true: Mute the device.
+   * - false: Unmute the device.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setPlaybackDeviceMute(bool mute) = 0;
+
+  /**
+   * Gets the mute state of the playback device.
+   *
+   * @param mute A pointer to the mute state of the playback device.
+   * - true: The playback device is muted.
+   * - false: The playback device is unmuted.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getPlaybackDeviceMute(bool *mute) = 0;
+
+  /**
+   * Mutes or unmutes the audio recording device.
+   *
+   * @param mute Determines whether to mute the recording device.
+   * - true: Mute the microphone.
+   * - false: Unmute the microphone.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int setRecordingDeviceMute(bool mute) = 0;
+
+  /**
+   * Gets the mute state of the audio recording device.
+   *
+   * @param mute A pointer to the mute state of the recording device.
+   * - true: The microphone is muted.
+   * - false: The microphone is unmuted.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int getRecordingDeviceMute(bool *mute) = 0;
+
+  /**
+   * Starts the audio playback device test.
+   *
+   * This method tests if the playback device works properly. In the test, the
+   * SDK plays an audio file specified by the user. If the user hears the audio,
+   * the playback device works properly.
+   *
+   * @param testAudioFilePath The file path of the audio file for the test,
+   * which is an absolute path in UTF8:
+   * - Supported file format: wav, mp3, m4a, and aac.
+   * - Supported file sampling rate: 8000, 16000, 32000, 44100, and 48000.
+   *
+   * @return
+   * - 0, if the method call succeeds and you can hear the sound of the
+   * specified audio file.
+   * - An error code, if the method call fails.
+   */
+  virtual int startPlaybackDeviceTest(const char *testAudioFilePath) = 0;
+
+  /**
+   * Stops the audio playback device test.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopPlaybackDeviceTest() = 0;
+
+  /**
+   * Starts the recording device test.
+   *
+   * This method tests whether the recording device works properly. Once the
+   * test starts, the SDK uses the \ref
+   * IRtcEngineEventHandler::onAudioVolumeIndication "onAudioVolumeIndication"
+   * callback to notify the app on the volume information.
+   *
+   * @param indicationInterval The time interval (ms) between which the SDK
+   * triggers the `onAudioVolumeIndication` callback.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startRecordingDeviceTest(int indicationInterval) = 0;
+
+  /**
+   * Stops the recording device test.
+   *
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopRecordingDeviceTest() = 0;
+
+  /**
+   * Starts the audio device loopback test.
+   *
+   * This method tests whether the local audio devices are working properly.
+   * After calling this method, the microphone captures the local audio and
+   * plays it through the speaker, and the \ref
+   * IRtcEngineEventHandler::onAudioVolumeIndication "onAudioVolumeIndication"
+   * callback returns the local audio volume information at the set interval.
+   *
+   * @note This method tests the local audio devices and does not report the
+   * network conditions.
+   * @param indicationInterval The time interval (ms) at which the \ref
+   * IRtcEngineEventHandler::onAudioVolumeIndication "onAudioVolumeIndication"
+   * callback returns.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int startAudioDeviceLoopbackTest(int indicationInterval) = 0;
+
+  /**
+   * Stops the audio device loopback test.
+   *
+   * @note Ensure that you call this method to stop the loopback test after
+   * calling the \ref IAudioDeviceManager::startAudioDeviceLoopbackTest
+   * "startAudioDeviceLoopbackTest" method.
+   * @return
+   * - 0: Success.
+   * - < 0: Failure.
+   */
+  virtual int stopAudioDeviceLoopbackTest() = 0;
+
+  /** The status of following system default playback device.
+
+   @note The status of following system default playback device.
+
+   @param enable Variable to whether the current device follow system default playback device or not.
+   - true: The current device will change when the system default playback device changed.
+   - false: The current device will change only current device is removed.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int followSystemPlaybackDevice(bool enable) = 0;
+
+  /** The status of following system default recording device.
+
+   @note The status of following system default recording device.
+
+   @param enable Variable to whether the current device follow system default recording device or not.
+   - true: The current device will change when the system default recording device changed.
+   - false: The current device will change only current device is removed.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int followSystemRecordingDevice(bool enable) = 0;
+
+  /** The status of following system default loopback device.
+
+   @note The status of following system default loopback device.
+
+   @param enable Variable to whether the current device follow system default loopback device or not.
+   - true: The current device will change when the system default loopback device changed.
+   - false: The current device will change only current device is removed.
+   @return
+   - 0: Success.
+   - < 0: Failure.
+   */
+  virtual int followSystemLoopbackDevice(bool enable) = 0;
+
+  /**
+   * Releases all IAudioDeviceManager resources.
+   */
+  virtual void release() = 0;
+};
+
+} // namespace rtc
+} // namespace agora
diff --git a/headers/rtc_4.3.2.11/include/time_utils.h b/headers/rtc_4.3.2.11/include/time_utils.h
new file mode 100644
index 0000000..f6d1fd9
--- /dev/null
+++ b/headers/rtc_4.3.2.11/include/time_utils.h
@@ -0,0 +1,85 @@
+//
+//  Agora Media SDK
+//
+//  Copyright (c) 2021 Agora IO. All rights reserved.
+//
+
+#pragma once
+#include <stdint.h>
+
+namespace agora {
+namespace base {
+
+class NtpTime {
+ public:
+  static const uint64_t ntpFracPerSecond = 4294967296;
+
+  NtpTime() : ms_(0) {}
+
+  NtpTime(uint64_t ms) : ms_(ms) {}
+
+  NtpTime(uint32_t seconds, uint32_t fractions) {
+    const double fracMs = fractions * 1000.0 / static_cast<double>(ntpFracPerSecond);
+    ms_ = static_cast<uint64_t>(seconds) * 1000 + static_cast<uint64_t>(0.5 + fracMs);
+  }
+
+  operator uint64_t() const { return ms_; }
+
+  /** Gets the NTP time.
+   *
+   * @return
+   * - The wallclock time which is in milliseconds relative to 0h UTC on 1 January 1900.
+   */
+  uint64_t Ms() const {
+    return ms_;
+  }
+
+   /** Check that whether the NtpTime object is valid
+   *
+   * - `true`: the NtpTime object is valid.
+   * - `false`: the NtpTime object is invalid.
+   */
+  bool Valid() const { return ms_ != 0; }
+
+   /** Gets the integer part of the NTP timestamp.
+   *
+   * @return
+   * - An uint32_t value.
+   */
+  uint32_t ToSeconds() const {
+    return static_cast<uint32_t>(ms_ / 1000);
+  }
+
+   /** Gets the fractional part of the NTP timestamp.
+   *
+   * @return
+   * - An uint32_t value.
+   */
+  uint32_t ToFractions() const {
+    return static_cast<uint32_t>((ms_ % 1000) * static_cast<double>(ntpFracPerSecond) / 1000.0);
+  }
+
+   /** Gets the NTP timestamp.
+   *
+   * @note
+   * - The full resolution NTP timestamp is a 64-bit unsigned fixed-point number with the integer part in the first 32 bits and the fractional part in the last 32 bits.
+   *
+   * @return
+   * - An uint64_t value.
+   */
+  uint64_t ToTimestamp() const {
+    return ToSeconds() * ntpFracPerSecond + ToFractions();
+  }
+
+ private:
+  uint64_t ms_;
+};
+
+inline bool operator==(const NtpTime& n1, const NtpTime& n2) {
+  return static_cast<uint64_t>(n1) == static_cast<uint64_t>(n2);
+}
+
+inline bool operator!=(const NtpTime& n1, const NtpTime& n2) { return !(n1 == n2); }
+
+}  // namespace base
+}  // namespace agora