
The global iGaming market reached $107.7 billion in 2025 and is projected to grow at an 11.9% CAGR through 2030, according to Mordor Intelligence. With 70–80% of users gambling via mobile devices and live dealer games driving the fastest-growing segment, modern iGaming platforms compete on experience, not just game selection. Players expect real-time voice chat at poker tables, live dealer video streams with sub-300ms latency, in-game messaging, and interactive community features. Building this infrastructure from scratch takes 12-18 months and millions in engineering costs. The alternative: integrate a purpose-built iGaming API that handles the real-time communication layer while you focus on game logic and player experience.
This guide is a developer-focused walkthrough for integrating Tencent RTC (TRTC) APIs into an iGaming platform. You'll get working code examples for Web, iOS, and Android — covering room entry, audio/video publishing, chat initialization, cloud recording, live streaming, and MCP-powered development tooling.
TL;DR
- TRTC provides voice, video, chat, live streaming, and cloud recording under a single SDK — eliminating multi-vendor complexity
enterRoomis the universal entry point: usescene: 'rtc'for poker tables,scene: 'live'for dealer streams- Cloud recording handles regulatory compliance with automatic lifecycle management and webhook-based file delivery
- The same SDKAppID works across Web, iOS, Android, Flutter, and Unity — unified identity for chat and RTC
- MCP integration (
@tencentcloud/sdk-mcp) lets AI assistants generate correct TRTC code with current API signatures
TL;DR
- TRTC provides voice, video, chat, live streaming, and cloud recording under a single SDK — eliminating multi-vendor complexity
enterRoomis the universal entry point: usescene: 'rtc'for poker tables,scene: 'live'for dealer streams- Cloud recording handles regulatory compliance with automatic lifecycle management and webhook-based file delivery
- The same SDKAppID works across Web, iOS, Android, Flutter, and Unity — unified identity for chat and RTC
- MCP integration (
@tencentcloud/sdk-mcp) lets AI assistants generate correct TRTC code with current API signatures
Why TRTC for iGaming
Before diving into code, here's why TRTC fits iGaming specifically:
| Requirement | TRTC Capability |
|---|---|
| Ultra-low latency (live dealer, in-play betting) | End-to-end < 300ms via full RTC chain |
| Packet loss resilience (mobile players) | 80% packet loss resistance |
| Global coverage (multi-jurisdiction) | 200+ countries, optimized for SEA/ME/NA |
| Scale (tournament events) | 50 concurrent speakers per room, unlimited audience |
| Compliance (recording) | Cloud recording with COS/VOD storage |
| Platform coverage | Web, iOS, Android, Flutter, Unity, Unreal |
| AI noise suppression | Built-in 3A algorithms (AEC, ANS, AGC) |
Competitors like Agora and ODIN cover subsets of these requirements. TRTC covers the full stack — voice, video, chat, live streaming, and recording — under a single SDK and billing model, eliminating multi-vendor complexity for iGaming platforms.
Prerequisites
Before starting integration:
- TRTC Account: Register at trtc.io and create an application
- Credentials: Obtain your
SDKAppIDandSecretKeyfrom the TRTC Console - Environment: Node.js 18+ (for server-side UserSig generation), HTTPS-enabled development environment (required for WebRTC)
- Documentation reference: TRTC Web SDK Guide and Interactive Game Console Architecture
Part 1: Core SDK Setup (Web)
Installation
# Install TRTC Web SDK v5
npm install trtc-sdk-v5 --save
# Install Chat SDK
npm install @tencentcloud/chat tim-upload-plugin --saveOr load via CDN for quick prototyping:
<script src="https://web.sdk.qcloud.com/trtc/webrtc/v5/dist/trtc.js"></script>Initialize TRTC Instance
import TRTC from 'trtc-sdk-v5';
// Create a TRTC instance — one per audio/video session
const trtc = TRTC.create();
// Check browser compatibility before proceeding
const isSupported = await TRTC.isSupported();
if (!isSupported) {
console.error('Current browser does not support TRTC WebRTC');
showFallbackUI(); // Redirect to native app or show browser upgrade prompt
}Server-Side Authentication: UserSig Generation
Never generate UserSig on the client. Your server issues short-lived tokens:
// server/auth.js — Node.js backend
const TLSSigAPIv2 = require('tls-sig-api-v2');
const SDK_APP_ID = parseInt(process.env.TRTC_SDK_APP_ID);
const SECRET_KEY = process.env.TRTC_SECRET_KEY;
/**
* Generate a UserSig for TRTC authentication
* @param {string} userId - Unique user identifier
* @param {number} expireSeconds - Token validity period (default: 7 days)
* @returns {string} UserSig token
*/
function generateUserSig(userId, expireSeconds = 604800) {
const api = new TLSSigAPIv2.Api(SDK_APP_ID, SECRET_KEY);
return api.genUserSig(userId, expireSeconds);
}
// Express endpoint
app.post('/api/auth/rtc-token', authenticateMiddleware, (req, res) => {
const userId = req.user.id;
const userSig = generateUserSig(userId);
res.json({
sdkAppId: SDK_APP_ID,
userId,
userSig,
expiresAt: Date.now() + 604800 * 1000
});
});Part 2: Entering a Room (enterRoom)
The enterRoom method is the gateway to all real-time features. Every voice chat, video call, or live stream starts here.
Basic Room Entry
/**
* Enter a TRTC room for iGaming interaction
* @param {Object} config - Room configuration
* @param {number} config.roomId - Numeric room ID (1-4294967294)
* @param {string} config.scene - 'rtc' for calls, 'live' for streaming
* @param {string} config.role - 'anchor' (publisher) or 'audience' (subscriber)
*/
async function enterGameRoom(config) {
const { roomId, scene = 'rtc', role = 'anchor' } = config;
try {
await trtc.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: currentUser.id,
userSig: currentUser.sig,
roomId: roomId,
scene: scene, // 'rtc' = real-time communication, 'live' = live streaming
role: role // Only relevant in 'live' scene
});
console.log(`Entered room ${roomId} as ${role} in ${scene} mode`);
return true;
} catch (error) {
handleRoomEntryError(error);
return false;
}
}
function handleRoomEntryError(error) {
switch (error.code) {
case -8:
console.error('Room entry failed: Invalid userSig (expired or malformed)');
refreshToken(); // Re-fetch from server
break;
case -10:
console.error('Room entry failed: Network unavailable');
showReconnectionUI();
break;
default:
console.error('Room entry failed:', error.message);
}
}Room Entry Patterns for iGaming Scenarios
Different game types require different room configurations:
// Pattern 1: Poker Table (real-time voice + video, small group)
async function joinPokerTable(tableId) {
await trtc.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: currentUser.id,
userSig: currentUser.sig,
roomId: tableId,
scene: 'rtc', // Low-latency bidirectional communication
role: 'anchor' // All players publish and subscribe
});
// Start video (show player's face)
await trtc.startLocalVideo({
view: document.getElementById('local-video'),
option: {
profile: '480p' // Sufficient for face cam
}
});
// Start audio (voice chat at the table)
await trtc.startLocalAudio({
option: { profile: 'speech' }
});
}
// Pattern 2: Live Dealer Game (one broadcaster, many viewers)
async function joinLiveDealerTable(tableId, isDealer = false) {
await trtc.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: currentUser.id,
userSig: currentUser.sig,
roomId: tableId,
scene: 'live',
role: isDealer ? 'anchor' : 'audience'
});
if (isDealer) {
// Dealer publishes high-quality video
await trtc.startLocalVideo({
view: document.getElementById('dealer-cam'),
option: {
profile: '1080p', // High quality dealer stream
mirror: false // Cards should not be mirrored
}
});
await trtc.startLocalAudio({ option: { profile: 'speech' } });
}
// Viewers automatically receive remote streams via event listeners
}
// Pattern 3: Sports Prediction Room (live stream + interaction)
async function joinPredictionRoom(eventId) {
await trtc.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: currentUser.id,
userSig: currentUser.sig,
strRoomId: `prediction_${eventId}`, // String room ID
scene: 'live',
role: 'audience'
});
// Subscribe to live event stream — handled by REMOTE_VIDEO_AVAILABLE event
}String Room IDs vs Numeric Room IDs
// Numeric room ID: 1 to 4294967294
await trtc.enterRoom({ roomId: 123456, ... });
// String room ID: alphanumeric, max 64 characters
await trtc.enterRoom({ strRoomId: 'poker_table_vip_007', ... });
// IMPORTANT: Numeric and string room IDs are NOT interchangeable
// Users in roomId: 123 and strRoomId: "123" are in DIFFERENT roomsPart 3: Publishing Local Video (startLocalVideo)
Basic Video Publishing
/**
* Start publishing local video to the room
* @param {HTMLElement} viewElement - DOM element for local preview
* @param {Object} options - Video configuration
*/
async function startVideo(viewElement, options = {}) {
const defaultOptions = {
profile: '720p', // Resolution preset
mirror: true, // Mirror local preview
facingMode: 'user' // Front camera on mobile
};
await trtc.startLocalVideo({
view: viewElement,
option: { ...defaultOptions, ...options }
});
}Video Profiles for iGaming
// Video resolution presets for different scenarios
const iGamingVideoProfiles = {
// Poker face cam: needs to show expressions clearly
pokerPlayer: {
profile: '480p',
frameRate: 20,
bitrate: 600
},
// Live dealer: high quality, smooth motion for card dealing
liveDealer: {
profile: '1080p',
frameRate: 30,
bitrate: 2500
},
// Sports stream: fast motion requires higher framerate
sportsStream: {
profile: '720p',
frameRate: 30,
bitrate: 1800
},
// Screen share (for showing game interfaces)
screenShare: {
profile: '1080p',
frameRate: 15,
bitrate: 1500
}
};
// Apply video profile
async function startGameVideo(scenario) {
const profile = iGamingVideoProfiles[scenario];
await trtc.startLocalVideo({
view: document.getElementById('local-video'),
option: {
profile: profile.profile,
frameRate: profile.frameRate,
bitrate: profile.bitrate
}
});
}Custom Video Source (Game Capture)
For iGaming platforms that need to stream game interfaces:
// Capture and stream a canvas-based game
async function streamGameCanvas(canvasElement) {
// Get video track from canvas
const canvasStream = canvasElement.captureStream(30); // 30fps
const videoTrack = canvasStream.getVideoTracks()[0];
// Publish canvas as video source
await trtc.startLocalVideo({
view: null, // No local preview needed for game stream
option: {
videoTrack: videoTrack // Custom video track
}
});
}
// Screen sharing for live commentary/analysis
async function startScreenShare() {
await trtc.startScreenShare({
view: document.getElementById('screen-share-preview'),
option: {
profile: '1080p',
systemAudio: true // Include game audio
}
});
}Subscribing to Remote Video Streams
// Handle remote player video streams
trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, async (event) => {
const { userId, streamType } = event;
console.log(`Remote video available from ${userId}, type: ${streamType}`);
// Determine view element based on user role
const viewElement = getPlayerViewElement(userId);
if (viewElement) {
await trtc.startRemoteVideo({
userId: userId,
streamType: streamType, // MAIN or SUB (screen share)
view: viewElement
});
}
});
trtc.on(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, (event) => {
const { userId, streamType } = event;
console.log(`Remote video ended from ${userId}`);
clearPlayerView(userId);
});
// First frame rendered (good for loading states)
trtc.on(TRTC.EVENT.FIRST_VIDEO_FRAME, (event) => {
const { userId } = event;
hideLoadingSpinner(userId);
});Part 4: Chat SDK Integration
In-game chat is essential for iGaming — from poker table banter to live dealer interactions to community discussions.
Initialize Chat SDK
import TencentCloudChat from '@tencentcloud/chat';
import TIMUploadPlugin from 'tim-upload-plugin';
// Initialize Chat (uses same SDKAppID as TRTC)
const chatOptions = { SDKAppID: YOUR_SDK_APP_ID };
const chat = TencentCloudChat.create(chatOptions);
chat.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin });
chat.setLogLevel(1); // Production mode
// Login (same userId as TRTC for unified identity)
async function initializeChat(userId, userSig) {
try {
await chat.login({ userID: userId, userSig: userSig });
console.log('Chat SDK initialized and logged in');
setupChatEventListeners();
return chat;
} catch (error) {
console.error('Chat initialization failed:', error);
throw error;
}
}Chat Event Listeners
function setupChatEventListeners() {
// Core message handler
chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
const messages = event.data;
messages.forEach(processIncomingMessage);
});
// SDK ready state
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
console.log('Chat SDK ready — can now send messages');
});
// Conversation list update
chat.on(TencentCloudChat.EVENT.CONVERSATION_LIST_UPDATED, (event) => {
updateConversationUI(event.data);
});
// Group system notifications (join, leave, mute)
chat.on(TencentCloudChat.EVENT.GROUP_SYSTEM_NOTICE_RECEIVED, (event) => {
handleGroupNotification(event.data);
});
// Network status changes
chat.on(TencentCloudChat.EVENT.NET_STATE_CHANGE, (event) => {
handleNetworkChange(event.data.state);
});
}
function processIncomingMessage(message) {
switch (message.type) {
case TencentCloudChat.TYPES.MSG_TEXT:
renderTextMessage(message);
break;
case TencentCloudChat.TYPES.MSG_IMAGE:
renderImageMessage(message);
break;
case TencentCloudChat.TYPES.MSG_CUSTOM:
handleCustomMessage(message);
break;
}
}Create Game Room Chat Groups
// Create a chat group for a game table
async function createGameChat(gameType, tableId, maxPlayers) {
const groupConfig = {
type: TencentCloudChat.TYPES.GRP_PUBLIC, // Public group with approval
name: `${gameType} Table #${tableId}`,
groupID: `game_${gameType}_${tableId}`,
maxMemberNum: maxPlayers + 5, // Players + moderators
joinOption: TencentCloudChat.TYPES.JOIN_OPTIONS_FREE_ACCESS
};
const result = await chat.createGroup(groupConfig);
return result.data.group;
}
// Create a live chat room (unlimited members, for live events)
async function createLiveEventChat(eventId, eventName) {
const result = await chat.createGroup({
type: TencentCloudChat.TYPES.GRP_AVCHATROOM,
name: eventName,
groupID: `live_${eventId}`
});
return result.data.group;
}
// Join a game chat
async function joinGameChat(gameGroupId) {
await chat.joinGroup({
groupID: gameGroupId,
type: TencentCloudChat.TYPES.GRP_AVCHATROOM
});
}Send Messages
// Text message
async function sendTextMessage(groupId, text) {
const message = chat.createTextMessage({
to: groupId,
conversationType: TencentCloudChat.TYPES.CONV_GROUP,
payload: { text: text }
});
return await chat.sendMessage(message);
}
// Custom game event message (bet placed, card dealt, etc.)
async function sendGameEvent(groupId, eventData) {
const message = chat.createCustomMessage({
to: groupId,
conversationType: TencentCloudChat.TYPES.CONV_GROUP,
payload: {
data: JSON.stringify(eventData),
description: eventData.description || '',
extension: eventData.type // e.g., 'bet_placed', 'card_dealt'
}
});
return await chat.sendMessage(message);
}
// Example: Announce a bet
await sendGameEvent('game_poker_table_5', {
type: 'bet_placed',
userId: 'player_42',
amount: 500,
action: 'raise',
description: 'Player42 raises to $500'
});
// Example: Deal cards (only visible to specific player via C2C)
async function dealCards(playerId, cards) {
const message = chat.createCustomMessage({
to: playerId,
conversationType: TencentCloudChat.TYPES.CONV_C2C,
payload: {
data: JSON.stringify({
type: 'cards_dealt',
cards: cards // ['Ah', 'Kd']
}),
description: 'Your cards have been dealt',
extension: 'game_action'
}
});
return await chat.sendMessage(message);
}Chat Moderation for iGaming
// Set up moderation rules (configured in TRTC Console + client enforcement)
async function moderateGameChat(groupId) {
// Mute a disruptive player
await chat.setGroupMemberMuteTime({
groupID: groupId,
userID: 'toxic_player_99',
muteTime: 300 // Muted for 5 minutes (0 = unmute)
});
// Kick from game chat
await chat.deleteGroupMember({
groupID: groupId,
userIDList: ['banned_user_1'],
reason: 'Violation of chat guidelines'
});
}
// Rate limiting on client side
class MessageRateLimiter {
constructor(maxPerSecond = 5) {
this.maxPerSecond = maxPerSecond;
this.timestamps = [];
}
canSend() {
const now = Date.now();
this.timestamps = this.timestamps.filter(t => now - t < 1000);
if (this.timestamps.length >= this.maxPerSecond) return false;
this.timestamps.push(now);
return true;
}
}
const rateLimiter = new MessageRateLimiter(5);
async function sendRateLimitedMessage(groupId, text) {
if (!rateLimiter.canSend()) {
showWarning('Slow down! You are sending messages too fast.');
return;
}
return await sendTextMessage(groupId, text);
}Part 5: Cloud Recording Configuration
iGaming platforms require recording for regulatory compliance, dispute resolution, and content moderation.
Enable Cloud Recording
// Server-side: Start cloud recording for a game session
const tencentcloud = require('tencentcloud-sdk-nodejs-trtc');
const TrtcClient = tencentcloud.trtc.v20190722.Client;
const client = new TrtcClient({
credential: {
secretId: process.env.TENCENT_SECRET_ID,
secretKey: process.env.TENCENT_SECRET_KEY
},
region: 'ap-singapore',
profile: { httpProfile: { endpoint: 'trtc.tencentcloudapi.com' } }
});
/**
* Start cloud recording for a game room
* @param {number} roomId - The room to record
* @param {string} recordMode - 'audio' | 'video' | 'both'
*/
async function startCloudRecording(roomId, recordMode = 'both') {
const params = {
SdkAppId: YOUR_SDK_APP_ID,
RoomId: roomId,
UserId: `recorder_${roomId}`, // Dedicated recording user
UserSig: generateUserSig(`recorder_${roomId}`),
RecordParams: {
RecordMode: recordMode === 'audio' ? 2 : recordMode === 'video' ? 1 : 0,
MaxIdleTime: 60, // Stop after 60s of silence
StreamType: 0, // Mixed stream (all users combined)
OutputFormat: 1 // HLS for easy playback
},
StorageParams: {
CloudVod: {
TencentVod: {
SubAppId: YOUR_VOD_SUB_APP_ID,
UserDefineRecordId: `game_${roomId}_${Date.now()}`
}
}
}
};
try {
const response = await client.CreateCloudRecording(params);
console.log('Recording started, TaskId:', response.TaskId);
return response.TaskId; // Save for stopping later
} catch (error) {
console.error('Failed to start recording:', error);
throw error;
}
}
// Stop recording
async function stopCloudRecording(taskId) {
const params = { TaskId: taskId };
await client.DeleteCloudRecording(params);
console.log('Recording stopped');
}Recording Lifecycle for Game Sessions
// Automatic recording management tied to game state
class GameRecordingManager {
constructor() {
this.activeRecordings = new Map();
}
async onGameStart(gameId, roomId) {
const taskId = await startCloudRecording(roomId, 'both');
this.activeRecordings.set(gameId, { taskId, roomId, startTime: Date.now() });
}
async onGameEnd(gameId) {
const recording = this.activeRecordings.get(gameId);
if (!recording) return;
await stopCloudRecording(recording.taskId);
// Store recording metadata for compliance
await db.gameRecordings.create({
gameId,
roomId: recording.roomId,
taskId: recording.taskId,
startTime: recording.startTime,
endTime: Date.now(),
retentionDays: 90 // Regulatory requirement varies by jurisdiction
});
this.activeRecordings.delete(gameId);
}
// Record specific events within a game (e.g., disputes)
async flagMoment(gameId, reason) {
const recording = this.activeRecordings.get(gameId);
if (!recording) return;
await db.recordingFlags.create({
taskId: recording.taskId,
timestamp: Date.now() - recording.startTime, // Offset in ms
reason: reason
});
}
}Recording Webhook Handler
// Handle recording completion callbacks
app.post('/webhooks/trtc/recording', (req, res) => {
const { EventType, EventData } = req.body;
switch (EventType) {
case 'RecordComplete':
handleRecordingComplete(EventData);
break;
case 'RecordError':
handleRecordingError(EventData);
break;
}
res.status(200).send('OK');
});
async function handleRecordingComplete(data) {
const { TaskId, FileList } = data;
// Store file URLs for later retrieval
for (const file of FileList) {
await db.recordingFiles.create({
taskId: TaskId,
fileUrl: file.FileUrl,
fileSize: file.FileSize,
duration: file.Duration,
format: file.FileFormat
});
}
// Notify compliance team if game was flagged
const flags = await db.recordingFlags.findAll({ where: { taskId: TaskId } });
if (flags.length > 0) {
notifyComplianceTeam(TaskId, flags);
}
}Part 6: Live Streaming Integration
Live streaming in iGaming covers live dealer games, sports broadcasts, and eSports event streaming. TRTC's Interactive Game Console architecture supports two approaches documented in the implementation guide.
Approach 1: RTMP Push + RTC Pull (Hybrid)
Best for: Integrating existing RTMP streaming infrastructure with low-latency viewer interaction.
// RTMP stream URL generation for existing broadcast equipment
function generateRtmpPushUrl(roomId, userId) {
const streamId = `${YOUR_SDK_APP_ID}_${roomId}_${userId}_main`;
return `rtmp://rtmp.rtc.qq.com/push/${streamId}?sdkappid=${YOUR_SDK_APP_ID}&userid=${userId}&usersig=${generateUserSig(userId)}`;
}
// Viewers pull via RTC for ultra-low latency
async function watchLiveStream(streamRoomId) {
const viewer = TRTC.create();
await viewer.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: `viewer_${currentUser.id}`,
userSig: currentUser.sig,
roomId: streamRoomId,
scene: 'live',
role: 'audience'
});
viewer.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, async (event) => {
await viewer.startRemoteVideo({
userId: event.userId,
streamType: event.streamType,
view: document.getElementById('live-stream-player')
});
});
return viewer;
}Approach 2: Full RTC Chain (Ultra-Low Latency)
Best for: Live dealer games and in-play betting where every millisecond matters.
// Full RTC streaming — broadcaster side
async function startLiveBroadcast(roomId, videoConfig) {
const broadcaster = TRTC.create();
await broadcaster.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: 'dealer_001',
userSig: dealerUserSig,
roomId: roomId,
scene: 'live',
role: 'anchor'
});
// High-quality video for live dealer
await broadcaster.startLocalVideo({
view: document.getElementById('dealer-preview'),
option: {
profile: '1080p',
frameRate: 30,
bitrate: 2500,
mirror: false // Cards should not be mirrored
}
});
// Crystal clear audio
await broadcaster.startLocalAudio({
option: { profile: 'standard' }
});
return broadcaster;
}
// Multiple camera setup (e.g., overhead card view + dealer face)
async function startMultiCameraBroadcast(roomId) {
const broadcaster = TRTC.create();
await broadcaster.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: 'dealer_multicam',
userSig: dealerUserSig,
roomId: roomId,
scene: 'live',
role: 'anchor'
});
// Main camera: dealer face
await broadcaster.startLocalVideo({
view: document.getElementById('cam-face'),
option: { profile: '720p', cameraId: FACE_CAMERA_ID }
});
// Sub stream: overhead card view
await broadcaster.startLocalVideo({
view: document.getElementById('cam-cards'),
option: {
profile: '720p',
cameraId: OVERHEAD_CAMERA_ID
},
publish: true,
publishMode: 'sub' // Publish as sub stream
});
return broadcaster;
}Low-Latency Configuration
// Optimized settings for live dealer (minimize latency)
async function configureLowLatency(trtcInstance) {
// Set video encoding parameters
await trtcInstance.setVideoEncoderParam({
videoResolution: TRTC.VIDEO_RESOLUTION.VIDEO_RESOLUTION_1080_1920,
videoFps: 30,
videoBitrate: 2500,
minVideoBitrate: 1000 // Don't drop below this even on poor networks
});
// Enable dual-stream mode (big + small)
await trtcInstance.enableSmallVideoStream({
smallVideoEncParam: {
videoResolution: TRTC.VIDEO_RESOLUTION.VIDEO_RESOLUTION_480_640,
videoFps: 15,
videoBitrate: 400
}
});
}CDN Relay for Scale
When audience exceeds RTC capacity, relay to CDN:
// Start CDN relay streaming
async function startCdnRelay(roomId, streamId) {
const params = {
SdkAppId: YOUR_SDK_APP_ID,
RoomId: roomId,
OutputParams: {
StreamId: streamId, // CDN stream identifier
PureAudioStream: 0 // Include video
},
EncodeParams: {
VideoWidth: 1920,
VideoHeight: 1080,
VideoBitrate: 2500,
VideoFramerate: 30,
VideoGop: 2,
AudioSampleRate: 48000,
AudioBitrate: 128,
AudioChannels: 2
}
};
await client.StartPublishCdnStream(params);
// CDN playback URLs
return {
flv: `https://your-cdn.com/live/${streamId}.flv`,
hls: `https://your-cdn.com/live/${streamId}.m3u8`,
rtmp: `rtmp://your-cdn.com/live/${streamId}`
};
}Part 7: iOS Integration
Swift Implementation
// iOS: GameRTCManager.swift
import TXLiteAVSDK_TRTC
class GameRTCManager: NSObject {
private let trtcCloud: TRTCCloud
private var currentRoomId: UInt32 = 0
override init() {
trtcCloud = TRTCCloud.sharedInstance()
super.init()
trtcCloud.delegate = self
}
/// Enter a game room
func enterRoom(roomId: UInt32, userId: String, userSig: String, scene: TRTCAppScene) {
let params = TRTCParams()
params.sdkAppId = YOUR_SDK_APP_ID
params.userId = userId
params.userSig = userSig
params.roomId = roomId
params.role = .anchor
currentRoomId = roomId
trtcCloud.enterRoom(params, appScene: scene)
}
/// Start local camera with game-optimized settings
func startCamera(view: UIView, isDealer: Bool = false) {
let encParams = TRTCVideoEncParam()
if isDealer {
encParams.videoResolution = ._1280_720
encParams.videoFps = 30
encParams.videoBitrate = 1800
} else {
encParams.videoResolution = ._640_480
encParams.videoFps = 20
encParams.videoBitrate = 600
}
trtcCloud.setVideoEncoderParam(encParams)
trtcCloud.startLocalPreview(true, view: view)
}
/// Start microphone for voice chat
func startAudio() {
trtcCloud.startLocalAudio(.speech)
}
/// Switch between speaker and audience
func switchRole(toAnchor: Bool) {
trtcCloud.switch(toAnchor ? .anchor : .audience)
}
func exitRoom() {
trtcCloud.stopLocalPreview()
trtcCloud.stopLocalAudio()
trtcCloud.exitRoom()
}
}
// MARK: - TRTCCloudDelegate
extension GameRTCManager: TRTCCloudDelegate {
func onEnterRoom(_ result: Int) {
if result > 0 {
print("Entered room successfully in \(result)ms")
} else {
print("Room entry failed with code: \(result)")
}
}
func onRemoteUserEnterRoom(_ userId: String) {
print("Player joined: \(userId)")
}
func onUserVideoAvailable(_ userId: String, available: Bool) {
if available {
// Start rendering remote user's video
let remoteView = getRemoteView(for: userId)
trtcCloud.startRemoteView(userId, streamType: .big, view: remoteView)
} else {
trtcCloud.stopRemoteView(userId, streamType: .big)
}
}
func onNetworkQuality(_ localQuality: TRTCQualityInfo,
remoteQuality: [TRTCQualityInfo]) {
// Adapt video quality based on network
if localQuality.quality.rawValue >= 4 {
// Poor network: reduce video quality
let param = TRTCVideoEncParam()
param.videoResolution = ._480_360
param.videoFps = 15
trtcCloud.setVideoEncoderParam(param)
}
}
}iOS Chat Integration
// iOS: GameChatManager.swift
import ImSDK_Plus
class GameChatManager {
static let shared = GameChatManager()
func initialize(sdkAppId: Int32) {
let config = V2TIMSDKConfig()
config.logLevel = .LOG_INFO
V2TIMManager.sharedInstance().initSDK(sdkAppId, config: config)
}
func login(userId: String, userSig: String, completion: @escaping (Bool) -> Void) {
V2TIMManager.sharedInstance().login(userId, userSig: userSig) {
completion(true)
} fail: { code, desc in
print("Chat login failed: \(code) - \(desc ?? "")")
completion(false)
}
}
func joinGameRoom(groupId: String) {
V2TIMManager.sharedInstance().joinGroup(groupId, msg: "") {
print("Joined game chat: \(groupId)")
} fail: { code, desc in
print("Join failed: \(desc ?? "")")
}
}
func sendGameAction(groupId: String, action: [String: Any]) {
guard let data = try? JSONSerialization.data(withJSONObject: action),
let jsonString = String(data: data, encoding: .utf8) else { return }
let customMessage = V2TIMManager.sharedInstance().createCustomMessage(jsonString.data(using: .utf8)!)
V2TIMManager.sharedInstance().send(customMessage!,
receiver: nil,
groupID: groupId,
priority: .PRIORITY_HIGH,
onlineUserOnly: false,
offlinePushInfo: nil,
progress: nil) { msg in
print("Game action sent")
} fail: { code, desc in
print("Send failed: \(desc ?? "")")
}
}
}Part 8: Android Integration
Kotlin Implementation
// Android: GameRTCManager.kt
import com.tencent.trtc.TRTCCloud
import com.tencent.trtc.TRTCCloudDef
import com.tencent.trtc.TRTCCloudListener
class GameRTCManager(private val context: Context) {
private val trtcCloud: TRTCCloud = TRTCCloud.sharedInstance(context)
init {
trtcCloud.setListener(trtcListener)
}
/**
* Enter a game room
*/
fun enterRoom(roomId: Int, userId: String, userSig: String, scene: Int) {
val params = TRTCCloudDef.TRTCParams().apply {
sdkAppId = YOUR_SDK_APP_ID
this.userId = userId
this.userSig = userSig
this.roomId = roomId
role = TRTCCloudDef.TRTCRoleAnchor
}
trtcCloud.enterRoom(params, scene)
}
/**
* Start local video with game-specific encoding
*/
fun startCamera(view: TXCloudVideoView, isDealer: Boolean = false) {
val encParam = TRTCCloudDef.TRTCVideoEncParam().apply {
if (isDealer) {
videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_1280_720
videoFps = 30
videoBitrate = 1800
minVideoBitrate = 1000
} else {
videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_640_480
videoFps = 20
videoBitrate = 600
minVideoBitrate = 300
}
videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT
}
trtcCloud.setVideoEncoderParam(encParam)
trtcCloud.startLocalPreview(true, view)
}
/**
* Start audio for voice chat
*/
fun startAudio() {
trtcCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH)
}
fun exitRoom() {
trtcCloud.stopLocalPreview()
trtcCloud.stopLocalAudio()
trtcCloud.exitRoom()
}
private val trtcListener = object : TRTCCloudListener() {
override fun onEnterRoom(result: Long) {
if (result > 0) {
Log.d("GameRTC", "Entered room in ${result}ms")
} else {
Log.e("GameRTC", "Room entry failed: $result")
}
}
override fun onRemoteUserEnterRoom(userId: String?) {
Log.d("GameRTC", "Player joined: $userId")
}
override fun onUserVideoAvailable(userId: String?, available: Boolean) {
userId ?: return
if (available) {
val remoteView = getRemoteView(userId)
trtcCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, remoteView)
} else {
trtcCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG)
}
}
override fun onNetworkQuality(
localQuality: TRTCCloudDef.TRTCQuality?,
remoteQuality: ArrayList<TRTCCloudDef.TRTCQuality>?
) {
localQuality?.let {
if (it.quality >= 4) {
// Degrade gracefully on poor network
degradeVideoQuality()
}
}
}
}
private fun degradeVideoQuality() {
val param = TRTCCloudDef.TRTCVideoEncParam().apply {
videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_480_360
videoFps = 15
videoBitrate = 400
}
trtcCloud.setVideoEncoderParam(param)
}
}Android Chat Integration
// Android: GameChatManager.kt
import com.tencent.imsdk.v2.*
class GameChatManager {
companion object {
val instance = GameChatManager()
}
fun initialize(context: Context, sdkAppId: Int) {
val config = V2TIMSDKConfig().apply {
logLevel = V2TIMSDKConfig.V2TIM_LOG_INFO
}
V2TIMManager.getInstance().initSDK(context, sdkAppId, config)
}
fun login(userId: String, userSig: String, callback: (Boolean) -> Unit) {
V2TIMManager.getInstance().login(userId, userSig, object : V2TIMCallback {
override fun onSuccess() { callback(true) }
override fun onError(code: Int, desc: String?) {
Log.e("GameChat", "Login failed: $code - $desc")
callback(false)
}
})
}
fun sendBetAction(groupId: String, betData: Map<String, Any>) {
val json = JSONObject(betData).toString()
val customMessage = V2TIMManager.getMessageManager()
.createCustomMessage(json.toByteArray())
V2TIMManager.getMessageManager().sendMessage(
customMessage, null, groupId,
V2TIMMessage.V2TIM_PRIORITY_HIGH,
false, null,
object : V2TIMSendCallback<V2TIMMessage> {
override fun onSuccess(msg: V2TIMMessage?) {
Log.d("GameChat", "Bet action sent")
}
override fun onError(code: Int, desc: String?) {
Log.e("GameChat", "Send failed: $desc")
}
override fun onProgress(progress: Int) {}
}
)
}
fun listenForMessages(handler: (V2TIMMessage) -> Unit) {
V2TIMManager.getMessageManager().addAdvancedMsgListener(
object : V2TIMAdvancedMsgListener() {
override fun onRecvNewMessage(msg: V2TIMMessage?) {
msg?.let { handler(it) }
}
}
)
}
}Part 9: MCP-Powered Development
TRTC provides Model Context Protocol (MCP) integration via @tencentcloud/sdk-mcp, enabling AI-assisted development of iGaming features.
Configure MCP in Your IDE
// .cursor/mcp.json (for Cursor IDE)
{
"mcpServers": {
"tencentcloud-sdk-mcp": {
"command": "npx",
"args": ["-y", "@tencentcloud/sdk-mcp"],
"env": {
"SDKAPPID": "YOUR_SDK_APP_ID",
"SECRETKEY": "YOUR_SECRET_KEY"
}
}
}
}What MCP Enables
With the TRTC MCP server configured, your AI coding assistant can:
- Generate integration code — Ask "Create a live dealer room with TRTC Web SDK" and get working code with correct API calls
- Troubleshoot issues — Describe an error and get context-aware solutions
- Generate UserSig — For testing purposes (not production)
- Look up API parameters — Get accurate method signatures without leaving your editor
MCP Usage Examples
// In your AI assistant, prompt:
"Create a poker table video chat component using TRTC Web SDK v5
that supports 8 players with video and voice."
// The MCP server provides:
// - Correct import paths
// - Current API method signatures
// - Best practices for multi-user rooms
// - Error handling patternsSkills Integration for Enhanced MCP
# Install TRTC-specific skills for better AI assistance
npx skills add Tencent-RTC/tencent-rtc-skillsThis adds domain knowledge about:
- TRTC architecture patterns for gaming
- Common integration pitfalls
- Performance optimization techniques
- Platform-specific best practices
Part 10: Complete Integration Example — Online Poker Platform
Putting it all together: a full-stack poker game integration.
Game Session Lifecycle
// PokerGameSession.js — Full lifecycle management
class PokerGameSession {
constructor(tableId, players) {
this.tableId = tableId;
this.players = players;
this.trtcInstances = new Map();
this.chatGroupId = `poker_${tableId}`;
this.recordingTaskId = null;
}
/**
* Initialize all communication channels for a poker game
*/
async start() {
// Step 1: Create chat group for the table
await this.createTableChat();
// Step 2: Start cloud recording (compliance)
this.recordingTaskId = await startCloudRecording(this.tableId, 'both');
// Step 3: Signal all players to join
for (const player of this.players) {
await this.notifyPlayerToJoin(player);
}
console.log(`Poker game started: Table ${this.tableId}, ${this.players.length} players`);
}
async createTableChat() {
await chat.createGroup({
type: TencentCloudChat.TYPES.GRP_PUBLIC,
name: `Poker Table #${this.tableId}`,
groupID: this.chatGroupId,
maxMemberNum: 12 // 8 players + 4 observers
});
}
/**
* Player joins the table — sets up video + audio + chat
*/
async playerJoin(player) {
const trtcInstance = TRTC.create();
// Enter room with video + audio
await trtcInstance.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId: player.id,
userSig: player.sig,
roomId: this.tableId,
scene: 'rtc',
role: 'anchor'
});
// Start player camera
await trtcInstance.startLocalVideo({
view: document.getElementById(`player-video-${player.seatIndex}`),
option: { profile: '480p', mirror: true }
});
// Start player microphone
await trtcInstance.startLocalAudio({
option: { profile: 'speech' }
});
// Subscribe to other players' streams
trtcInstance.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, async (event) => {
const seatIndex = this.getPlayerSeat(event.userId);
await trtcInstance.startRemoteVideo({
userId: event.userId,
streamType: event.streamType,
view: document.getElementById(`player-video-${seatIndex}`)
});
});
// Join table chat
await chat.joinGroup({ groupID: this.chatGroupId });
this.trtcInstances.set(player.id, trtcInstance);
}
/**
* Broadcast a game action to all players
*/
async broadcastAction(action) {
await sendGameEvent(this.chatGroupId, {
type: 'GAME_ACTION',
action: action.type, // 'bet', 'fold', 'raise', 'check', 'call'
player: action.playerId,
amount: action.amount,
description: `${action.playerName} ${action.type}s${action.amount ? ` $${action.amount}` : ''}`
});
}
/**
* Deal private cards (only visible to recipient)
*/
async dealPrivateCards(playerId, cards) {
await dealCards(playerId, cards);
}
/**
* Show community cards to all players
*/
async revealCommunityCards(cards, stage) {
await sendGameEvent(this.chatGroupId, {
type: 'COMMUNITY_CARDS',
stage: stage, // 'flop', 'turn', 'river'
cards: cards,
description: `${stage.toUpperCase()}: ${cards.join(' ')}`
});
}
/**
* End the game session and clean up
*/
async end(winner, potAmount) {
// Announce winner
await sendGameEvent(this.chatGroupId, {
type: 'GAME_END',
winner: winner.id,
winnerName: winner.name,
pot: potAmount,
description: `🏆 ${winner.name} wins $${potAmount}!`
});
// Stop recording
if (this.recordingTaskId) {
await stopCloudRecording(this.recordingTaskId);
}
// Disconnect all players
for (const [playerId, instance] of this.trtcInstances) {
await instance.exitRoom();
await instance.destroy();
}
// Destroy chat group (or keep for post-game discussion)
// await chat.dismissGroup({ groupID: this.chatGroupId });
console.log(`Game ended: Table ${this.tableId}`);
}
}
// Usage
const game = new PokerGameSession(55001, [
{ id: 'player_1', name: 'Alice', seatIndex: 0 },
{ id: 'player_2', name: 'Bob', seatIndex: 1 },
{ id: 'player_3', name: 'Carol', seatIndex: 2 },
{ id: 'player_4', name: 'Dave', seatIndex: 3 }
]);
await game.start();
await game.playerJoin(players[0]);
// ... game logic
await game.end(winner, 5000);Part 11: Error Handling & Resilience
Comprehensive Error Handling
// Error codes and recovery strategies for iGaming
const TRTC_ERROR_HANDLERS = {
// Authentication errors
'-8': {
description: 'Invalid UserSig',
recovery: async () => {
const newToken = await fetchFreshToken();
await reconnectWithToken(newToken);
}
},
// Network errors
'-10': {
description: 'Network disconnected',
recovery: async () => {
showReconnectingUI();
await waitForNetwork();
await rejoinRoom();
}
},
// Room errors
'-3301': {
description: 'Room does not exist',
recovery: async () => {
showError('This game table has been closed');
redirectToLobby();
}
},
// Capacity errors
'-3302': {
description: 'Room is full',
recovery: async () => {
showError('Table is full. You\'ve been added to the waitlist.');
addToWaitlist();
}
}
};
// Reconnection logic for game continuity
class GameReconnectionManager {
constructor(maxRetries = 5, baseDelay = 1000) {
this.maxRetries = maxRetries;
this.baseDelay = baseDelay;
this.retryCount = 0;
}
async attemptReconnect(roomId, userId, userSig) {
while (this.retryCount < this.maxRetries) {
try {
const delay = this.baseDelay * Math.pow(2, this.retryCount); // Exponential backoff
await sleep(delay);
await trtc.enterRoom({
sdkAppId: YOUR_SDK_APP_ID,
userId, userSig, roomId,
scene: 'rtc', role: 'anchor'
});
this.retryCount = 0;
hideReconnectingUI();
showNotification('Reconnected to game');
return true;
} catch (error) {
this.retryCount++;
console.warn(`Reconnect attempt ${this.retryCount} failed:`, error);
}
}
showError('Unable to reconnect. Please rejoin from the lobby.');
return false;
}
}Network Quality Monitoring
// Real-time network quality for game-critical decisions
trtc.on(TRTC.EVENT.NETWORK_QUALITY, (event) => {
const { uplinkNetworkQuality, downlinkNetworkQuality } = event;
// Quality levels: 0=Unknown, 1=Excellent, 2=Good, 3=Fair, 4=Poor, 5=VeryPoor, 6=Disconnected
if (uplinkNetworkQuality >= 4) {
// Player's upload is poor — reduce outgoing video
trtc.updateLocalVideo({
option: { profile: '360p', frameRate: 15 }
});
showNetworkWarning('Your connection is unstable. Video quality reduced.');
}
if (downlinkNetworkQuality >= 5) {
// Can't receive streams — critical for live dealer
showNetworkCritical('Connection lost. Game actions may be delayed.');
// Pause bet acceptance for this player (server-side)
notifyServer('PLAYER_NETWORK_CRITICAL', currentUser.id);
}
});Part 12: Performance Optimization for iGaming
Bandwidth Management
// Adaptive bandwidth allocation based on game state
class BandwidthManager {
constructor(trtcInstance) {
this.trtc = trtcInstance;
}
// During active play: prioritize audio (decisions happen via voice)
setActivePlayMode() {
this.trtc.updateLocalVideo({
option: { profile: '360p', frameRate: 15, bitrate: 300 }
});
// Audio remains at highest quality
}
// During showdown: prioritize video (seeing cards matters)
setShowdownMode() {
this.trtc.updateLocalVideo({
option: { profile: '720p', frameRate: 30, bitrate: 1200 }
});
}
// Spectator mode: receive-only, no upload
setSpectatorMode() {
this.trtc.stopLocalVideo();
this.trtc.stopLocalAudio();
// Only receiving streams — saves bandwidth
}
}Memory Management
// Proper cleanup to prevent memory leaks in long gaming sessions
class TRTCResourceManager {
constructor() {
this.instances = new Set();
}
createInstance() {
const instance = TRTC.create();
this.instances.add(instance);
return instance;
}
async destroyInstance(instance) {
try {
await instance.exitRoom();
} catch (e) {
// Room may already be exited
}
await instance.destroy();
this.instances.delete(instance);
}
// Call on app background/close
async destroyAll() {
for (const instance of this.instances) {
await this.destroyInstance(instance);
}
this.instances.clear();
}
}
// Handle page visibility changes (mobile users switching apps)
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// Pause video to save bandwidth, keep audio
trtc.stopLocalVideo();
} else {
// Resume video when user returns
trtc.startLocalVideo({
view: document.getElementById('local-video'),
option: { profile: currentProfile }
});
}
});Part 13: Security Considerations for iGaming
Preventing Stream Manipulation
// Server-side stream integrity verification
app.post('/webhooks/trtc/stream-event', (req, res) => {
const { eventType, streamInfo } = req.body;
// Verify stream source matches authenticated user
if (eventType === 'STREAM_PUBLISH') {
const expectedUserId = getExpectedStreamUser(streamInfo.roomId);
if (streamInfo.userId !== expectedUserId) {
// Unauthorized stream — kick immediately
kickUserFromRoom(streamInfo.roomId, streamInfo.userId);
logSecurityEvent('UNAUTHORIZED_STREAM', streamInfo);
}
}
res.sendStatus(200);
});Anti-Cheat: Preventing Information Leakage
// Ensure private game information (cards, etc.) cannot be intercepted
// Always use C2C (peer-to-peer) messages for private data, never group messages
async function securelyDealCards(playerId, cards) {
// Encrypt card data before sending
const encryptedCards = encrypt(JSON.stringify(cards), playerPublicKey);
const message = chat.createCustomMessage({
to: playerId,
conversationType: TencentCloudChat.TYPES.CONV_C2C, // Private, never group
payload: {
data: encryptedCards,
description: 'encrypted_cards',
extension: 'secure_game_data'
}
});
await chat.sendMessage(message);
}Part 14: Testing & Debugging
Device Testing Matrix
// Automated compatibility check before entering game
async function runCompatibilityCheck() {
const results = {
webrtcSupport: await TRTC.isSupported(),
camera: false,
microphone: false,
networkLatency: null
};
// Check camera access
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
results.camera = true;
stream.getTracks().forEach(t => t.stop());
} catch (e) {
results.camera = false;
}
// Check microphone access
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
results.microphone = true;
stream.getTracks().forEach(t => t.stop());
} catch (e) {
results.microphone = false;
}
// Network speed test
results.networkLatency = await measureLatency();
return results;
}
function generateCompatibilityReport(results) {
const issues = [];
if (!results.webrtcSupport) issues.push('Browser does not support WebRTC');
if (!results.camera) issues.push('Camera access denied or unavailable');
if (!results.microphone) issues.push('Microphone access denied or unavailable');
if (results.networkLatency > 500) issues.push('Network latency too high for real-time gaming');
return {
canPlay: issues.length === 0,
issues: issues,
recommendation: issues.length > 0
? 'Please resolve the above issues for the best gaming experience'
: 'All systems ready!'
};
}Debug Logging
// Enable detailed logging during development
if (process.env.NODE_ENV === 'development') {
// TRTC SDK logs
trtc.on(TRTC.EVENT.ERROR, (error) => {
console.error('[TRTC Error]', error);
});
trtc.on(TRTC.EVENT.WARNING, (warning) => {
console.warn('[TRTC Warning]', warning);
});
// Chat SDK logs
chat.setLogLevel(0); // Verbose logging in dev
// Network quality logging
trtc.on(TRTC.EVENT.NETWORK_QUALITY, (event) => {
console.debug('[Network]', {
uplink: event.uplinkNetworkQuality,
downlink: event.downlinkNetworkQuality,
rtt: event.rtt
});
});
}Part 15: Deployment & Scaling
Infrastructure Checklist
| Component | Configuration | Purpose |
|---|---|---|
| TRTC SDK | Production SDKAppID + SecretKey | Core real-time communication |
| Chat SDK | Same SDKAppID, production log level | In-game messaging |
| Cloud Recording | VOD or COS storage configured | Compliance & disputes |
| CDN Relay | Stream relay for large audiences | Scale beyond RTC limits |
| Webhooks | Event callbacks to your server | Game state sync |
| Monitoring | TRTC Console dashboard | Quality metrics |
Scaling Patterns
// Room sharding for large tournaments
class TournamentRoomManager {
constructor(tournamentId, totalPlayers) {
this.tournamentId = tournamentId;
this.tables = [];
this.playersPerTable = 8;
this.totalTables = Math.ceil(totalPlayers / this.playersPerTable);
}
// Generate room IDs for all tournament tables
generateRoomIds() {
const baseId = hashToNumeric(this.tournamentId); // Deterministic base
for (let i = 0; i < this.totalTables; i++) {
this.tables.push({
tableNumber: i + 1,
roomId: baseId + i, // Sequential room IDs
chatGroupId: `tournament_${this.tournamentId}_table_${i + 1}`,
players: [],
status: 'waiting'
});
}
return this.tables;
}
// Rebalance tables as players are eliminated
async rebalanceTables(eliminatedPlayerId, sourceTableIndex) {
// Find table with fewest players
const targetTable = this.tables
.filter(t => t.status === 'active' && t.players.length < this.playersPerTable)
.sort((a, b) => a.players.length - b.players.length)[0];
if (targetTable && this.tables[sourceTableIndex].players.length <= 3) {
// Move remaining players to other tables
await this.mergeTables(sourceTableIndex, targetTable);
}
}
}API Reference Quick Sheet
| Method | Purpose | Scene |
|---|---|---|
trtc.enterRoom() | Join a room | All |
trtc.exitRoom() | Leave a room | All |
trtc.startLocalVideo() | Publish camera | Poker, Live Dealer |
trtc.stopLocalVideo() | Stop camera | All |
trtc.startLocalAudio() | Publish microphone | Voice Chat, Poker |
trtc.stopLocalAudio() | Stop microphone | All |
trtc.startRemoteVideo() | Subscribe to remote video | All |
trtc.startScreenShare() | Share screen/game | Commentary, Analysis |
trtc.switchRole() | Switch anchor/audience | Live Streaming |
trtc.enableAudioVolumeEvaluation() | Volume indicators | Voice Rooms |
chat.createGroup() | Create chat room | All |
chat.joinGroup() | Join chat room | All |
chat.sendMessage() | Send message | All |
chat.createCustomMessage() | Custom game events | Game Actions |
Conclusion
Integrating real-time communication into an iGaming platform doesn't require building WebRTC infrastructure from scratch. With the online casino software market projected to reach $39.59 billion in 2025 at a 14.8% CAGR, operators need scalable solutions that work across jurisdictions. TRTC provides the complete stack — voice, video, chat, live streaming, and cloud recording — through a unified API that works across Web, iOS, Android, and game engines.
The key integration points covered:
enterRoom— The universal entry point for all real-time features, with scene/role configuration for different game typesstartLocalVideo— Publish camera with game-specific encoding profiles (poker face cam vs. live dealer HD)- Chat SDK — In-game messaging with custom message types for game actions, moderation, and community features
- Cloud Recording — Automated compliance recording with programmatic lifecycle management
- Live Streaming — RTMP+RTC hybrid or full RTC chain for ultra-low latency dealer/sports streams
- MCP Tooling — AI-assisted development via
@tencentcloud/sdk-mcpfor faster integration
For implementation details on specific use cases, refer to:
- Interactive Game Console Solution — Architecture patterns for gaming platforms
- TRTC Web SDK Documentation — Complete API reference
- RTMP + RTC Implementation Guide — Dual-mode streaming architecture
- Chat Product Overview — Full chat capabilities and UIKit options
Start with the free Chat API — free forever — 1,000 MAU, no concurrency limits, push notifications included.
Start with a single game type (poker table voice chat, or live dealer streaming), validate the integration, then expand to additional features. The modular architecture means each capability can be added incrementally without refactoring existing code.
Frequently Asked Questions
How long does TRTC integration take for an iGaming platform?
A single feature (e.g., poker table voice chat or live dealer streaming) takes 1-2 weeks for a developer familiar with WebRTC concepts. Full-stack integration (voice + video + chat + recording) takes 4-8 weeks. The MCP tool accelerates this further by generating correct integration code via AI assistants.
What's the difference between scene: 'rtc' and scene: 'live'?
rtc mode is for bidirectional communication (poker tables where all players publish and subscribe). live mode is for one-to-many broadcasting (live dealer streaming where one anchor publishes to many audience members). Live mode has optimized server-side mixing and higher scalability.
Does TRTC support both numeric and string room IDs?
Yes. Numeric IDs (1 to 4294967294) and string IDs (alphanumeric, max 64 characters) are supported. Important: users in roomId: 123 and strRoomId: "123" are in different rooms — they are not interchangeable.
How does cloud recording work for regulatory compliance?
You call CreateCloudRecording with a dedicated recording user that joins the room. It captures all audio/video streams (individual or mixed) and stores them in COS/VOD. Webhook callbacks notify your server when files are ready. Configure retention periods per jurisdiction (90 days for UKGC, 6 months for MGA).
What happens when a player loses network connection during a game?
TRTC's SDK handles reconnection automatically with exponential backoff. The recommended pattern is up to 5 retries. During disconnection, audio continues on the server side. For game-critical sessions, notify the server to pause bet acceptance for that player until reconnected.
Can I use TRTC for both Web and native mobile apps?
Yes. The same SDKAppID and authentication (UserSig) work across Web (trtc-sdk-v5), iOS (TXLiteAVSDK_TRTC), Android (com.tencent.trtc), Flutter, Unity, and Unreal Engine. Player identity is unified across all platforms.
How does TRTC handle poor mobile network conditions?
The SDK implements adaptive bitrate (automatically downgrades video quality), FEC for up to 80% packet loss recovery, audio priority (keeps voice even if video freezes), and network quality events that let you show warnings or switch to audio-only mode.
What's the pricing model for TRTC in iGaming?
Usage-based pricing with 10,000 free monthly minutes for development. Production costs scale with concurrent users and media type (audio-only is cheaper than video). No upfront setup fees or minimum commitments.
Frequently Asked Questions
How long does TRTC integration take for an iGaming platform?
A single feature (e.g., poker table voice chat or live dealer streaming) takes 1-2 weeks for a developer familiar with WebRTC concepts. Full-stack integration (voice + video + chat + recording) takes 4-8 weeks. The MCP tool accelerates this further by generating correct integration code via AI assistants.
What's the difference between scene: 'rtc' and scene: 'live'?
rtc mode is for bidirectional communication (poker tables where all players publish and subscribe). live mode is for one-to-many broadcasting (live dealer streaming where one anchor publishes to many audience members). Live mode has optimized server-side mixing and higher scalability.
Does TRTC support both numeric and string room IDs?
Yes. Numeric IDs (1 to 4294967294) and string IDs (alphanumeric, max 64 characters) are supported. Important: users in roomId: 123 and strRoomId: "123" are in different rooms — they are not interchangeable.
How does cloud recording work for regulatory compliance?
You call CreateCloudRecording with a dedicated recording user that joins the room. It captures all audio/video streams (individual or mixed) and stores them in COS/VOD. Webhook callbacks notify your server when files are ready. Configure retention periods per jurisdiction (90 days for UKGC, 6 months for MGA).
What happens when a player loses network connection during a game?
TRTC's SDK handles reconnection automatically with exponential backoff. The recommended pattern is up to 5 retries. During disconnection, audio continues on the server side. For game-critical sessions, notify the server to pause bet acceptance for that player until reconnected.
Can I use TRTC for both Web and native mobile apps?
Yes. The same SDKAppID and authentication (UserSig) work across Web (trtc-sdk-v5), iOS (TXLiteAVSDK_TRTC), Android (com.tencent.trtc), Flutter, Unity, and Unreal Engine. Player identity is unified across all platforms.
How does TRTC handle poor mobile network conditions?
The SDK implements adaptive bitrate (automatically downgrades video quality), FEC for up to 80% packet loss recovery, audio priority (keeps voice even if video freezes), and network quality events that let you show warnings or switch to audio-only mode.
What's the pricing model for TRTC in iGaming?
Usage-based pricing with 10,000 free monthly minutes for development. Production costs scale with concurrent users and media type (audio-only is cheaper than video). No upfront setup fees or minimum commitments.


