Setting the file. One moment.
Subchapter 149.5
concepts/singleton-hierarchy.mdMarkdown19 KBView on GitHub
The Zoom Video SDK for Web uses a service locator pattern - a tree of objects where you navigate from the root ZoomVideo module to specific features. You don’t construct objects; you traverse to them.
You want to... You navigate to...
─────────────────────────────────────────────────────
Start your camera client.getMediaStream() → stream.startVideo()
Mute audio client.getMediaStream() → stream.muteAudio()
Attach video client.getMediaStream() → stream.attachVideo(userId, quality)
Send chat message client.getChatClient() → chatClient.send(message)
Start screen share client.getMediaStream() → stream.startShareScreen(canvas)
Start recording client.getRecordingClient() → recordingClient.startCloudRecording()Level 0: Global Module
│
└─► ZoomVideo ──────────────────────────────────────────────────► Default Export
│
├─► Static Methods (Before Session)
│ ├── createClient() → VideoClient (singleton)
│ ├── destroyClient() → Promise<void>
│ ├── checkSystemRequirements() → MediaCompatibility
│ ├── checkFeatureRequirements() → SupportFeatures
│ ├── getDevices(skip?) → Promise<MediaDeviceInfo[]>
│ ├── preloadDependentAssets(path) → void
│ ├── createLocalVideoTrack(id?) → LocalVideoTrack
│ ├── createLocalAudioTrack(id?) → LocalAudioTrack
│ └── VERSION → string
│
└─► Level 1: VideoClient (The Root Singleton)
│
├─► Session Lifecycle
│ ├── init(language, assets, options) → ExecutedResult
│ ├── join(topic, token, name, pass) → ExecutedResult
│ ├── leave(end?) → ExecutedResult
│ ├── on(event, callback) → void
│ └── off(event, callback) → void
│
├─► Participant Info
│ ├── getAllUser() → Participant[]
│ ├── getCurrentUserInfo() → Participant
│ ├── getUser(userId) → Participant | undefined
│ ├── getSessionHost() → Participant | undefined
| Aspect | Windows SDK | Web SDK |
|---|---|---|
| Root Object | IZoomVideoSDK | ZoomVideo.createClient() → VideoClient |
| Feature Access | Helpers (getVideoHelper()) | Stream + Clients (getMediaStream(), getChatClient()) |
| Video Rendering | Canvas API / Raw Data Pipe | attachVideo() returns VideoPlayer |
| Events | Delegate callbacks | client.on('event', handler) |
| Depth | 5 levels max | 4 levels max |
| Level | When | Example |
|---|---|---|
| Level 0 | Before SDK init, check compatibility | ZoomVideo.checkSystemRequirements() |
| Level 1 | Session lifecycle, get clients | client.join(...), client.getChatClient() |
| Level 2 | Media operations (Stream) | stream.startVideo(), stream.attachVideo() |
| Level 2 | Feature-specific operations (Clients) | chatClient.send(...), recordingClient.start...() |
| Level 3 | Participant info | user.bVideoOn, user.displayName |
| Feature | Navigation Path |
|---|---|
| Start camera | client.getMediaStream().startVideo() |
| Stop camera | client.getMediaStream().stopVideo() |
| Attach video | client.getMediaStream().attachVideo(userId, quality) |
| Detach video | client.getMediaStream().detachVideo(userId) |
| Switch camera | client.getMediaStream().switchCamera(deviceId) |
| Start audio | client.getMediaStream().startAudio() |
| Mute audio | client.getMediaStream().muteAudio() |
| Unmute audio | client.getMediaStream().unmuteAudio() |
| Start share | client.getMediaStream().startShareScreen(canvas) |
| Stop share | client.getMediaStream().stopShareScreen() |
| View share | client.getMediaStream().startShareView(canvas, userId) |
| Send chat | client.getChatClient().send(message) |
| Start recording | client.getRecordingClient().startCloudRecording() |
| Start transcription | client.getLiveTranscriptionClient().startLiveTranscription() |
| Send command | client.getCommandClient().send(text) |
| Get participants | client.getAllUser() |
| Get myself | client.getCurrentUserInfo() |
| Leave session | client.leave() |
| End session | client.leave(true) |
All events are subscribed via client.on():
// Session events
client.on('connection-change', (payload) => { ... });
client.on('user-added', (payload) => { ... });
client.on('user-removed', (payload) => { ... });
client.on('user-updated', (payload) => { ... });
// Video events
client.on('peer-video-state-change', (payload) => { ... });
client.on('video-active-change', (payload) => { ... });
client.on('video-capturing-change', (payload) => { ... });
// Audio events
client.on('current-audio-change', (payload) => { ... });
client.on('active-speaker', (payload) => { ... });
client.on('host-ask-unmute-audio', (payload) => { ... });
// Share events
client.on('active-share-change', (payload) => { ... });
client.on('peer-share-state-change', (payload) => { ... });
client.on('passively-stop-share', (payload) => { ... });
// Chat events
client.on('chat-on-message', (payload) => { ... });
// Recording events
client.on('recording-change', (payload) => { ... });
// Transcription events
client.on('caption-message', (payload) => { ... });// WRONG - stream is undefined
const stream = client.getMediaStream();
await client.join(...);
// CORRECT - get stream after join
await client.join(...);
const stream = client.getMediaStream();// Listen for video changes
client.on('peer-video-state-change', async (payload) => {
const { action, userId } = payload;
if (action === 'Start') {
const element = await stream.attachVideo(userId, VideoQuality.Video_360P);
container.appendChild(element);
} else {
await stream.detachVideo(userId);
}
});// HD Video
if (stream.isSupportHDVideo()) {
await stream.startVideo({ hd: true });
}
// Virtual Background
if (stream.isSupportVirtualBackground()) {
await stream.updateVirtualBackgroundImage('blur');
}
// Video Processor
if (stream.isSupportVideoProcessor()) {
const processor = await stream.createProcessor(params);
await stream.addProcessor(processor);
}// WRONG
const stream = client.getMediaStream(); // undefined!
await client.join(...);
// CORRECT
await client.join(...);
const stream = client.getMediaStream(); // Works!// Check which element to use
if (stream.isStartShareScreenWithVideoElement()) {
// Use HTMLVideoElement
await stream.startShareScreen(videoElement);
} else {
// Use HTMLCanvasElement
await stream.startShareScreen(canvasElement);
}// After joining, render existing participants
const users = client.getAllUser();
const currentUserId = client.getCurrentUserInfo().userId;
for (const user of users) {
if (user.bVideoOn && user.userId !== currentUserId) {
const element = await stream.attachVideo(user.userId, VideoQuality.Video_360P);
container.appendChild(element);
}
}TL;DR: Start at ZoomVideo.createClient(), get stream AFTER join, use stream. for media and client.getXXXClient() for features. Events via client.on().