Unreal C++ SDK Guide
Unreal C++ install, join, render and event handling.
The Unreal Engine (C++) SDK has the IRtcEngine + IRtcEngineEventHandler shape with lower-case method names, the same as the C++ surface on Android/Windows.
Prerequisites
- Unreal Engine 5.2+ (a C++ project; the plugin must be enabled in
.uproject). - Target-platform permissions: the system camera/microphone permission on Windows/macOS; for Android/iOS see each platform's requirements.
- An
appIdand a token service (see Enable Services and Get Credentials).
Installation
# planned shape: put the plugin in the project directory
YourGame/Plugins/NimeetRtc/…
# after regenerating the project files, enable the NimeetRtc module in .uprojectGet a token
The game backend issues the token; the UE client only receives a string. In production a short-lived token is handed out at login and renewed in the onTokenPrivilegeWillExpire callback; do not compile the App Certificate into the client.
Join a channel
#include "NimeetRtc/IRtcEngine.h"
class FMeetingHandler final : public IRtcEngineEventHandler {
public:
void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) override {}
void onUserJoined(uid_t uid, int elapsed) override {}
void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) override {}
void onTokenPrivilegeWillExpire(const char* token) override {}
void onError(int err, const char* msg) override {}
};
void UMeetingSubsystem::Start(const FString& AppId, const FString& Token)
{
Handler = MakeShared<FMeetingHandler>();
Engine = createAgoraRtcEngine();
RtcEngineContext Context;
Context.appId = TCHAR_TO_UTF8(*AppId);
Context.eventHandler = Handler.Get();
Engine->initialize(Context);
Engine->setChannelProfile(CHANNEL_PROFILE_LIVE_BROADCASTING);
Engine->enableVideo();
Engine->startPreview();
Engine->joinChannel(TCHAR_TO_UTF8(*Token), "weekly-sync", "", 10001);
}Publishing and subscribing
Engine->muteLocalAudioStream(true);
Engine->muteLocalVideoStream(true);
Engine->enableLocalVideo(false);
Engine->updateChannelMediaOptions(Options);Screen sharing: getScreenCaptureSources → startScreenCaptureByDisplayId / startScreenCaptureByWindowId; render the remote picture with the VideoSurface component the plugin provides (or a custom texture callback).
Events
IRtcEngineEventHandler's C++ virtual functions: onJoinChannelSuccess, onUserJoined, onUserOffline, onFirstRemoteVideoDecoded, onTokenPrivilegeWillExpire, onError, onConnectionStateChanged.
Error handling
- Synchronous methods return
int;0means success and a negative value is an Agora error number; - Asynchronous errors arrive through
onError; - Unimplemented capabilities return
-4(ERR_NOT_SUPPORTED).
Troubleshooting
| Symptom | Cause | Handling |
|---|---|---|
| The plugin module is missing after packaging | It is not enabled in .uproject, or the project was not regenerated | Enable the module and Rebuild |
| Runtime crash inside a callback | Cross-thread access to a UObject | Switch back to the GameThread with AsyncTask |
| No camera picture | The platform permission was not granted | Grant it in the system settings and restart the process |
| The remote picture does not show | VideoSurface is not bound | Bind the surface to the target material/widget |
| Audio is mono | Audio device configuration | Check the system default device and sample rate |
Next steps
- Full interfaces and overloads: API reference for this platform
- Error codes and troubleshooting: Error code table, FAQ
- Issue tokens on your server: Token authentication
Platform information
| Item | value |
|---|---|
| Platform | Unreal Engine(C++) |
| Language | C++ |
| SDK package | NimeetRtc |
| Interface version | 4.5.1 |
| Compatible Agora API | Agora unreal-cpp API |
The public interfaces are aligned with the Agora unreal-cpp API; installation and runtime requirements are listed under Prerequisites above.
Availability
This platform's SDK: Generally available. The examples use the public API; runtime capability follows the published SDK package.
join-channel flow
The method names, parameter types and return values match the API reference for the same platform. Where a method has several overloads, the most common one is shown here; see the API reference for all overloads.
| Step | Interface | Method | Overload count |
|---|---|---|---|
| subscribe to remote media | IBaseSpatialAudioEngine | muteRemoteAudioStream | 1 |
| Create instance / initialize | ILocalSpatialAudioEngine | initialize | 1 |
| Renew the Token | IMusicContentCenter | renewToken | 1 |
| enable video and local preview | IRtcEngine | enableVideo | 1 |
| Join a channel | IRtcEngine | joinChannel | 2 |
| Leave a channel | IRtcEngine | leaveChannel | 2 |
| Channel scenarios | IRtcEngine | setChannelProfile | 1 |
| switch roles | IRtcEngine | setClientRole | 2 |
| bind the local video | IRtcEngine | setupLocalVideo | 1 |
muteRemoteAudioStream(uid: uid_t, mute: bool): int
initialize(config: const LocalSpatialAudioConfig&): int
renewToken(token: const char*): int
enableVideo(): int
joinChannel(token: const char*, channelId: const char*, info: const char*, uid: uid_t): int
leaveChannel(): int
setChannelProfile(profile: CHANNEL_PROFILE_TYPE): int
setClientRole(role: CLIENT_ROLE_TYPE): int
setupLocalVideo(canvas: const VideoCanvas&): intEvents and callbacks
Callbacks are registered on IRtcEngineEventHandler; naming differs by platform, and the full list is in the API reference for the same platform.
| Callbacks | Signature | Purpose |
|---|---|---|
onConnectionStateChanged | onConnectionStateChanged(state: CONNECTION_STATE_TYPE, reason: CONNECTION_CHANGED_REASON_TYPE): void | join result / connection-state changes:update the UI accordingly and decide whether to reconnect. |
onUserJoined | onUserJoined(uid: uid_t, elapsed: int): void | Remote user joined:allocate the frame container. |
onFirstRemoteVideoDecoded | onFirstRemoteVideoDecoded(uid: uid_t, width: int, height: int, elapsed: int): void | Remote video status:it can render once the first frame is decoded. |
onUserOffline | onUserOffline(uid: uid_t, reason: USER_OFFLINE_REASON_TYPE): void | Remote user left:release the container and subscription. |
onRequestToken | onRequestToken(): void | Token expiring soon:call the server to renew, then `renewToken`. |
onError | onError(err: int, msg: const char*): void | Runtime errors:`err` is an Agora error number, route by the error-code table. |
onNetworkQuality | onNetworkQuality(uid: uid_t, txQuality: int, rxQuality: int): void | Network quality:report to Analytics or prompt the user. |
Error codes
| Constant | value |
|---|---|
ERR_ABORTED | 20 |
ERR_ADM_GENERAL_ERROR | 1005 |
ERR_ADM_INIT_PLAYOUT | 1008 |
ERR_ADM_INIT_RECORDING | 1011 |
ERR_ADM_START_PLAYOUT | 1009 |
ERR_ADM_START_RECORDING | 1012 |
ERR_ADM_STOP_PLAYOUT | 1010 |
ERR_ADM_STOP_RECORDING | 1013 |
ERR_ALREADY_IN_USE | 19 |
ERR_BITRATE_LIMIT | 115 |
ERR_BUFFER_TOO_SMALL | 6 |
ERR_CLIENT_IS_BANNED_BY_SERVER | 123 |
ERR_CONNECTION_INTERRUPTED | 111 |
ERR_CONNECTION_LOST | 112 |
ERR_DATASTREAM_DECRYPTION_FAILED | 122 |
ERR_DECRYPTION_FAILED | 120 |
ERR_ENCRYPTED_STREAM_NOT_ALLOWED_PUBLISH | 130 |
ERR_FAILED | 1 |
ERR_INIT_NET_ENGINE | 21 |
ERR_INVALID_APP_ID | 101 |
ERR_INVALID_ARGUMENT | 2 |
ERR_INVALID_CHANNEL_NAME | 102 |
ERR_INVALID_STATE | 8 |
ERR_INVALID_TOKEN | 110 |
ERR_INVALID_USER_ACCOUNT | 134 |
ERR_INVALID_USER_ID | 121 |
ERR_JOIN_CHANNEL_REJECTED | 17 |
ERR_LEAVE_CHANNEL_REJECTED | 18 |
ERR_LOAD_MEDIA_ENGINE | 1001 |
ERR_NOT_INITIALIZED | 7 |
ERR_NOT_IN_CHANNEL | 113 |
ERR_NOT_READY | 3 |
ERR_NOT_SUPPORTED | 4 |
ERR_NO_PERMISSION | 9 |
ERR_NO_SERVER_RESOURCES | 103 |
ERR_OK | 0 |
ERR_REFUSED | 5 |
ERR_RESOURCE_LIMITED | 22 |
ERR_SET_CLIENT_ROLE_NOT_AUTHORIZED | 119 |
ERR_SIZE_TOO_LARGE | 114 |
ERR_STREAM_MESSAGE_TIMEOUT | 117 |
ERR_TIMEDOUT | 10 |
ERR_TOKEN_EXPIRED | 109 |
ERR_TOO_MANY_DATA_STREAMS | 116 |
ERR_VDM_CAMERA_NOT_AUTHORIZED | 1501 |
IRtcEngine | null |
the platform REST layer uses REST error codes;the SDK layer maps connection/authentication failures to Agora error numbers, for the mapping table see SDK error identifiers per platform。