All Blog

iGaming API Integration: Voice, Video, Chat & Streaming

12 min read
May 28, 2026

iGaming API Integration

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
  • enterRoom is the universal entry point: use scene: '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
  • enterRoom is the universal entry point: use scene: '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:

RequirementTRTC 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 coverageWeb, iOS, Android, Flutter, Unity, Unreal
AI noise suppressionBuilt-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:

  1. TRTC Account: Register at trtc.io and create an application
  2. Credentials: Obtain your SDKAppID and SecretKey from the TRTC Console
  3. Environment: Node.js 18+ (for server-side UserSig generation), HTTPS-enabled development environment (required for WebRTC)
  4. 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 --save

Or 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 rooms

Part 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:

  1. Generate integration code — Ask "Create a live dealer room with TRTC Web SDK" and get working code with correct API calls
  2. Troubleshoot issues — Describe an error and get context-aware solutions
  3. Generate UserSig — For testing purposes (not production)
  4. 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 patterns

Skills Integration for Enhanced MCP

# Install TRTC-specific skills for better AI assistance
npx skills add Tencent-RTC/tencent-rtc-skills

This 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

ComponentConfigurationPurpose
TRTC SDKProduction SDKAppID + SecretKeyCore real-time communication
Chat SDKSame SDKAppID, production log levelIn-game messaging
Cloud RecordingVOD or COS storage configuredCompliance & disputes
CDN RelayStream relay for large audiencesScale beyond RTC limits
WebhooksEvent callbacks to your serverGame state sync
MonitoringTRTC Console dashboardQuality 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

MethodPurposeScene
trtc.enterRoom()Join a roomAll
trtc.exitRoom()Leave a roomAll
trtc.startLocalVideo()Publish cameraPoker, Live Dealer
trtc.stopLocalVideo()Stop cameraAll
trtc.startLocalAudio()Publish microphoneVoice Chat, Poker
trtc.stopLocalAudio()Stop microphoneAll
trtc.startRemoteVideo()Subscribe to remote videoAll
trtc.startScreenShare()Share screen/gameCommentary, Analysis
trtc.switchRole()Switch anchor/audienceLive Streaming
trtc.enableAudioVolumeEvaluation()Volume indicatorsVoice Rooms
chat.createGroup()Create chat roomAll
chat.joinGroup()Join chat roomAll
chat.sendMessage()Send messageAll
chat.createCustomMessage()Custom game eventsGame 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:

  1. enterRoom — The universal entry point for all real-time features, with scene/role configuration for different game types
  2. startLocalVideo — Publish camera with game-specific encoding profiles (poker face cam vs. live dealer HD)
  3. Chat SDK — In-game messaging with custom message types for game actions, moderation, and community features
  4. Cloud Recording — Automated compliance recording with programmatic lifecycle management
  5. Live Streaming — RTMP+RTC hybrid or full RTC chain for ultra-low latency dealer/sports streams
  6. MCP Tooling — AI-assisted development via @tencentcloud/sdk-mcp for faster integration

For implementation details on specific use cases, refer to:

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.