mediasfu-angular 2.1.6 β†’ 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/README.md CHANGED
@@ -4,22 +4,57 @@
4
4
 
5
5
  <p align="center">
6
6
  <a href="https://twitter.com/media_sfu">
7
- <img src="https://img.icons8.com/color/48/000000/twitter--v1.png" alt="Twitter" style="margin-right: 10px;">
7
+ <img src="https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white" alt="Twitter" />
8
8
  </a>
9
9
  <a href="https://www.mediasfu.com/forums">
10
- <img src="https://img.icons8.com/color/48/000000/communication--v1.png" alt="Community Forum" style="margin-right: 10px;">
10
+ <img src="https://img.shields.io/badge/Community-Forum-blue?style=for-the-badge&logo=discourse&logoColor=white" alt="Community Forum" />
11
11
  </a>
12
12
  <a href="https://github.com/MediaSFU">
13
- <img src="https://img.icons8.com/fluent/48/000000/github.png" alt="Github" style="margin-right: 10px;">
13
+ <img src="https://img.shields.io/badge/GitHub-181717?style=for-the-badge&logo=github&logoColor=white" alt="Github" />
14
14
  </a>
15
15
  <a href="https://www.mediasfu.com/">
16
- <img src="https://img.icons8.com/color/48/000000/domain--v1.png" alt="Website" style="margin-right: 10px;">
16
+ <img src="https://img.shields.io/badge/Website-4285F4?style=for-the-badge&logo=google-chrome&logoColor=white" alt="Website" />
17
17
  </a>
18
18
  <a href="https://www.youtube.com/channel/UCELghZRPKMgjih5qrmXLtqw">
19
- <img src="https://img.icons8.com/color/48/000000/youtube--v1.png" alt="Youtube" style="margin-right: 10px;">
19
+ <img src="https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white" alt="Youtube" />
20
20
  </a>
21
21
  </p>
22
22
 
23
+ <p align="center">
24
+ <a href="https://opensource.org/licenses/MIT">
25
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="License: MIT" />
26
+ </a>
27
+ <a href="https://mediasfu.com">
28
+ <img src="https://img.shields.io/badge/Built%20with-MediaSFU-blue?style=flat-square" alt="Built with MediaSFU" />
29
+ </a>
30
+ <a href="https://angular.io">
31
+ <img src="https://img.shields.io/badge/Angular-DD0031?style=flat-square&logo=angular&logoColor=white" alt="Angular" />
32
+ </a>
33
+ <a href="https://www.typescriptlang.org">
34
+ <img src="https://img.shields.io/badge/TypeScript-007ACC?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" />
35
+ </a>
36
+ </p>
37
+
38
+ ---
39
+
40
+ ## 🚨 **BREAKING: AI Phone Agents at $0.10 per 1,000 minutes**
41
+
42
+ πŸ“ž **Call our live AI demos right now:**
43
+ - πŸ‡ΊπŸ‡Έ **+1 (785) 369-1724** - Mixed Support Demo
44
+ - πŸ‡¬πŸ‡§ **+44 7445 146575** - AI Conversation Demo
45
+ - πŸ‡¨πŸ‡¦ **+1 (587) 407-1990** - Technical Support Demo
46
+ - πŸ‡¨πŸ‡¦ **+1 (647) 558-6650** - Friendly AI Chat Demo
47
+
48
+ **Traditional providers charge $0.05 per minute. We charge $0.10 per 1,000 minutes. That's 500x cheaper.**
49
+
50
+ βœ… **Deploy AI phone agents in 30 minutes**
51
+ βœ… **Works with ANY SIP provider** (Twilio, Telnyx, Zadarma, etc.)
52
+ βœ… **Seamless AI-to-human handoffs**
53
+ βœ… **Real-time call analytics & transcription**
54
+
55
+ πŸ“– **[Complete SIP/PSTN Documentation β†’](https://mediasfu.com/telephony)**
56
+
57
+ ---
23
58
 
24
59
  MediaSFU offers a cutting-edge streaming experience that empowers users to customize their recordings and engage their audience with high-quality streams. Whether you're a content creator, educator, or business professional, MediaSFU provides the tools you need to elevate your streaming game.
25
60
 
@@ -39,15 +74,22 @@ MediaSFU offers a cutting-edge streaming experience that empowers users to custo
39
74
 
40
75
  **[Get started now on GitHub!](https://github.com/MediaSFU/MediaSFUOpen)**
41
76
 
77
+ ### βœ… Angular SDK Setup Guide
78
+ Coming soon! Watch this space for our comprehensive video tutorial on setting up the Angular SDK.
79
+
42
80
  ---
43
81
 
44
82
  ## Table of Contents
45
83
 
46
84
  - [Features](#features)
47
85
  - [Getting Started](#getting-started)
48
- - [Basic Usage Guide](#basic-usage-guide)
49
- - [Intermediate Usage Guide](#intermediate-usage-guide)
50
- - [Advanced Usage Guide](#advanced-usage-guide)
86
+ - [πŸ“˜ Angular SDK Guide](#angular-sdk-guide)
87
+ - [Quick Start](#quick-start-5-minutes)
88
+ - [Understanding the Architecture](#understanding-mediasfu-architecture)
89
+ - [Core Concepts & Components](#core-concepts--components)
90
+ - [Working with Methods](#working-with-methods)
91
+ - [Media Streams & Participants](#media-streams--participants)
92
+ - [Customization & Styling](#customization--styling)
51
93
  - [API Reference](#api-reference)
52
94
  - [Troubleshooting](#troubleshooting)
53
95
  - [Contributing](#contributing)
@@ -67,6 +109,74 @@ MediaSFU's Angular SDK comes with a host of powerful features out of the box:
67
109
  9. **Cloud Recording (track-based)**: Customize recordings with track-based options, including watermarks, name tags, background colors, and more.
68
110
  10. **Managed Events**: Manage events with features to handle abandoned and inactive participants, as well as enforce time and capacity limits.
69
111
 
112
+ ## πŸ†• **New Advanced Media Access**
113
+
114
+ The Angular SDK now includes powerful utility methods for fine-grained control over media devices and participant streams:
115
+
116
+ ### **`getMediaDevicesList`** - Device Enumeration
117
+
118
+ Enumerate available cameras and microphones with automatic permission handling:
119
+
120
+ ```typescript
121
+ // Get all available cameras
122
+ const cameras = await sourceParameters.getMediaDevicesList('videoinput');
123
+ cameras.forEach(camera => {
124
+ console.log(`Camera: ${camera.label} (${camera.deviceId})`);
125
+ });
126
+
127
+ // Get all available microphones
128
+ const microphones = await sourceParameters.getMediaDevicesList('audioinput');
129
+ microphones.forEach(mic => {
130
+ console.log(`Microphone: ${mic.label} (${mic.deviceId})`);
131
+ });
132
+ ```
133
+
134
+ **Use Cases:**
135
+ - Build custom device selection interfaces
136
+ - Detect available media hardware
137
+ - Switch between multiple cameras/microphones
138
+ - Pre-flight device checks before joining
139
+
140
+ [See full documentation and examples β†’](#media-device-and-stream-utility-methods)
141
+
142
+ ---
143
+
144
+ ### **`getParticipantMedia`** - Stream Access
145
+
146
+ Retrieve specific participant's video or audio streams by ID or name:
147
+
148
+ ```typescript
149
+ // Get participant video stream by producer ID
150
+ const videoStream = await sourceParameters.getParticipantMedia({
151
+ id: 'producer-123',
152
+ kind: 'video'
153
+ });
154
+
155
+ // Get participant audio stream by name
156
+ const audioStream = await sourceParameters.getParticipantMedia({
157
+ name: 'John Doe',
158
+ kind: 'audio'
159
+ });
160
+
161
+ // Use the stream (e.g., attach to video element)
162
+ if (videoStream) {
163
+ videoElement.srcObject = videoStream;
164
+ }
165
+ ```
166
+
167
+ **Use Cases:**
168
+ - Monitor specific participant streams
169
+ - Create custom video layouts with individual control
170
+ - Build stream recording features
171
+ - Implement advanced audio/video processing
172
+ - Create picture-in-picture views for specific users
173
+
174
+ [See full documentation and examples β†’](#media-device-and-stream-utility-methods)
175
+
176
+ ---
177
+
178
+ These utilities enable advanced features like custom device selection interfaces, participant stream monitoring, and dynamic media routing. Both methods are fully integrated with Angular's reactive patterns using RxJS.
179
+
70
180
  # Getting Started <a name="getting-started"></a>
71
181
 
72
182
  This section will guide users through the initial setup and installation of the npm module.
@@ -159,42 +269,153 @@ If you plan to self-host MediaSFU or use it without MediaSFU Cloud services, you
159
269
  This setup allows full flexibility and customization while bypassing the need for cloud-dependent credentials.
160
270
 
161
271
 
162
- # Basic Usage Guide <a name="basic-usage-guide"></a>
272
+ # πŸ“˜ Angular SDK Guide <a name="angular-sdk-guide"></a>
163
273
 
164
- A basic guide on how to use the module for common tasks.
274
+ This comprehensive guide will walk you through everything you need to know about building real-time communication apps with MediaSFU's Angular SDK. Whether you're a beginner or an experienced developer, you'll find clear explanations, practical examples, and best practices.
165
275
 
166
- This section will guide users through the initial setup and installation of the npm module.
167
- ## Introduction
276
+ ---
168
277
 
169
- MediaSFU is a 2-page application consisting of a prejoin/welcome page and the main events room page. This guide will walk you through the basic usage of the module for setting up these pages.
278
+ ## Quick Start (5 Minutes) <a name="quick-start-5-minutes"></a>
170
279
 
171
- ### Documentation Reference
280
+ Get your first MediaSFU app running in just a few minutes.
172
281
 
173
- For comprehensive documentation on the available methods, components, and functions, please visit [mediasfu.com](https://www.mediasfu.com/angular/). This resource provides detailed information for this guide and additional documentation.
282
+ ### Step 1: Install the Package
174
283
 
175
- ## Prebuilt Event Rooms
284
+ ```bash
285
+ npm install mediasfu-angular
286
+ ```
176
287
 
177
- MediaSFU provides prebuilt event rooms for various purposes. These rooms are rendered as full pages and can be easily imported and used in your application. Here are the available prebuilt event rooms:
288
+ ### Step 2: Import and Use
178
289
 
179
- 1. **MediasfuGeneric**: A generic event room suitable for various types of events.
180
- 2. **MediasfuBroadcast**: A room optimized for broadcasting events.
181
- 3. **MediasfuWebinar**: Specifically designed for hosting webinars.
182
- 4. **MediasfuConference**: Ideal for hosting conferences.
183
- 5. **MediasfuChat**: A room tailored for interactive chat sessions.
290
+ ```typescript
291
+ // app.component.ts
292
+ import { Component } from '@angular/core';
293
+ import { MediasfuGeneric } from 'mediasfu-angular';
184
294
 
185
- Users can easily pick an interface and render it in their app.
295
+ @Component({
296
+ selector: 'app-root',
297
+ standalone: true,
298
+ imports: [MediasfuGeneric],
299
+ template: `<app-mediasfu-generic></app-mediasfu-generic>`,
300
+ })
301
+ export class AppComponent { }
302
+ ```
186
303
 
187
- If no API credentials are provided, a default home page will be displayed where users can scan or manually enter the event details.
304
+ **Alternative with Credentials:**
188
305
 
189
- To use these prebuilt event rooms, simply import them into your application:
306
+ ```typescript
307
+ import { Component } from '@angular/core';
308
+ import { MediasfuGeneric, PreJoinPage } from 'mediasfu-angular';
190
309
 
191
- ```javascript
192
- ## Simplest Usage
310
+ @Component({
311
+ selector: 'app-root',
312
+ standalone: true,
313
+ imports: [MediasfuGeneric],
314
+ template: `
315
+ <app-mediasfu-generic
316
+ [PrejoinPage]="PreJoinPage"
317
+ [credentials]="credentials">
318
+ </app-mediasfu-generic>
319
+ `,
320
+ })
321
+ export class AppComponent {
322
+ PreJoinPage = PreJoinPage;
323
+ credentials = {
324
+ apiUserName: 'your_username',
325
+ apiKey: 'your_api_key',
326
+ };
327
+ }
328
+ ```
329
+
330
+ ### Step 3: Run Your App
331
+
332
+ ```bash
333
+ ng serve
334
+ ```
335
+
336
+ **That's it!** You now have a fully functional video conferencing app with:
337
+ - βœ… Video and audio streaming
338
+ - βœ… Screen sharing
339
+ - βœ… Chat messaging
340
+ - βœ… Participant management
341
+ - βœ… Recording capabilities
342
+ - βœ… Breakout rooms
343
+ - βœ… Polls and whiteboards
344
+
345
+ ---
346
+
347
+ ## Understanding MediaSFU Architecture <a name="understanding-mediasfu-architecture"></a>
348
+
349
+ Before diving deeper, let's understand how MediaSFU is structured.
350
+
351
+ ### The Three-Layer Architecture
352
+
353
+ ```
354
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
355
+ β”‚ Your Angular Application β”‚
356
+ β”‚ (components, services, business logic) β”‚
357
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
358
+ ↓
359
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
360
+ β”‚ MediaSFU Components Layer β”‚
361
+ β”‚ (MediasfuGeneric, MediasfuBroadcast, etc.) β”‚
362
+ β”‚ - Pre-built UI components β”‚
363
+ β”‚ - Event handling β”‚
364
+ β”‚ - State management (RxJS) β”‚
365
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
366
+ ↓
367
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
368
+ β”‚ MediaSFU Core Methods Layer β”‚
369
+ β”‚ (Stream control, room management, β”‚
370
+ β”‚ WebRTC handling, socket communication) β”‚
371
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
372
+ ↓
373
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
374
+ β”‚ MediaSFU Backend Services β”‚
375
+ β”‚ (MediaSFU Cloud or Community Edition) β”‚
376
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
377
+ ```
378
+
379
+ ### Key Concepts
193
380
 
194
- The simplest way to use MediaSFU in your Angular application is by directly rendering the prebuilt event room component, such as `MediasfuGeneric`.
381
+ #### 1. **Event Room Types**
382
+
383
+ MediaSFU provides 5 specialized room types, each optimized for specific use cases:
384
+
385
+ | Room Type | Best For | Key Features |
386
+ |-----------|----------|--------------|
387
+ | **MediasfuGeneric** | General purpose meetings | Flexible layout, all features enabled |
388
+ | **MediasfuBroadcast** | Live streaming events | Optimized for one-to-many communication |
389
+ | **MediasfuWebinar** | Educational sessions | Presenter focus, Q&A features |
390
+ | **MediasfuConference** | Business meetings | Equal participant layout, collaboration tools |
391
+ | **MediasfuChat** | Interactive discussions | Chat-first interface, quick connections |
392
+
393
+ ```typescript
394
+ // Choose the right room type for your use case
395
+ import {
396
+ MediasfuWebinar,
397
+ MediasfuBroadcast,
398
+ MediasfuConference
399
+ } from 'mediasfu-angular';
400
+
401
+ @Component({
402
+ // For a webinar
403
+ template: `<app-mediasfu-webinar [credentials]="credentials"></app-mediasfu-webinar>`,
404
+ // For a broadcast
405
+ // template: `<app-mediasfu-broadcast [credentials]="credentials"></app-mediasfu-broadcast>`,
406
+ // For a conference
407
+ // template: `<app-mediasfu-conference [credentials]="credentials"></app-mediasfu-conference>`,
408
+ })
409
+ ```
410
+
411
+ #### 2. **The Three Usage Modes**
412
+
413
+ MediaSFU offers three progressive levels of customization:
414
+
415
+ ##### Mode 1: Default UI (Simplest)
416
+ Use MediaSFU's complete pre-built interface - perfect for rapid development.
195
417
 
196
418
  ```typescript
197
- // app.component.ts
198
419
  import { Component } from '@angular/core';
199
420
  import { MediasfuGeneric } from 'mediasfu-angular';
200
421
 
@@ -202,12 +423,1320 @@ import { MediasfuGeneric } from 'mediasfu-angular';
202
423
  selector: 'app-root',
203
424
  standalone: true,
204
425
  imports: [MediasfuGeneric],
426
+ template: `<app-mediasfu-generic [credentials]="credentials"></app-mediasfu-generic>`,
427
+ })
428
+ export class AppComponent {
429
+ credentials = { apiUserName: 'username', apiKey: 'key' };
430
+ }
431
+ ```
432
+
433
+ **When to use:**
434
+ - βœ… Prototyping or MVP development
435
+ - βœ… Need a production-ready UI quickly
436
+ - βœ… Standard video conferencing features are sufficient
437
+
438
+ ##### Mode 2: Custom UI with MediaSFU Backend (Most Flexible)
439
+ Build your own UI while using MediaSFU's powerful backend infrastructure.
440
+
441
+ ```typescript
442
+ import { Component, OnInit } from '@angular/core';
443
+ import { MediasfuGeneric } from 'mediasfu-angular';
444
+
445
+ @Component({
446
+ selector: 'app-root',
447
+ standalone: true,
448
+ imports: [MediasfuGeneric, CommonModule],
205
449
  template: `
206
- <app-mediasfu-generic></app-mediasfu-generic>
450
+ <app-mediasfu-generic
451
+ [returnUI]="false"
452
+ [sourceParameters]="sourceParameters"
453
+ [updateSourceParameters]="updateSourceParameters.bind(this)"
454
+ [credentials]="credentials"
455
+ [noUIPreJoinOptions]="preJoinOptions">
456
+ </app-mediasfu-generic>
457
+
458
+ <!-- Your custom UI -->
459
+ @if (sourceParameters) {
460
+ <div class="custom-controls">
461
+ <button (click)="toggleVideo()">
462
+ {{ sourceParameters.videoAlreadyOn ? 'Stop Video' : 'Start Video' }}
463
+ </button>
464
+ <button (click)="toggleAudio()">
465
+ {{ sourceParameters.audioAlreadyOn ? 'Mute' : 'Unmute' }}
466
+ </button>
467
+ <button (click)="toggleScreenShare()">
468
+ {{ sourceParameters.screenAlreadyOn ? 'Stop Sharing' : 'Share Screen' }}
469
+ </button>
470
+ </div>
471
+ }
207
472
  `,
208
473
  })
209
- export class AppComponent { }
474
+ export class AppComponent implements OnInit {
475
+ sourceParameters: any = null;
476
+ credentials = { apiUserName: 'username', apiKey: 'key' };
477
+ preJoinOptions = {
478
+ action: 'create',
479
+ userName: 'Your Name',
480
+ capacity: 50,
481
+ duration: 30,
482
+ eventType: 'conference'
483
+ };
484
+
485
+ updateSourceParameters(params: any) {
486
+ this.sourceParameters = params;
487
+ }
488
+
489
+ toggleVideo() {
490
+ this.sourceParameters?.clickVideo({ parameters: this.sourceParameters });
491
+ }
492
+
493
+ toggleAudio() {
494
+ this.sourceParameters?.clickAudio({ parameters: this.sourceParameters });
495
+ }
496
+
497
+ toggleScreenShare() {
498
+ this.sourceParameters?.clickScreenShare({ parameters: this.sourceParameters });
499
+ }
500
+ }
501
+ ```
502
+
503
+ **When to use:**
504
+ - βœ… Need complete control over UI/UX
505
+ - βœ… Building a custom branded experience
506
+ - βœ… Integrating into existing app design
507
+
508
+ ##### Mode 3: Component Replacement (Balanced)
509
+ Replace specific MediaSFU components while keeping the rest of the infrastructure.
510
+
511
+ ```typescript
512
+ import { Component } from '@angular/core';
513
+ import {
514
+ MediasfuGeneric,
515
+ FlexibleVideo,
516
+ FlexibleGrid
517
+ } from 'mediasfu-angular';
518
+
519
+ @Component({
520
+ selector: 'app-custom-main',
521
+ standalone: true,
522
+ imports: [FlexibleVideo, FlexibleGrid, CommonModule],
523
+ template: `
524
+ <div class="custom-layout">
525
+ <!-- Custom header -->
526
+ <div class="custom-header">
527
+ <h1>{{ parameters.roomName }}</h1>
528
+ <span>{{ parameters.participants.length }} participants</span>
529
+ </div>
530
+
531
+ <!-- Use MediaSFU's components in your layout -->
532
+ <app-flexible-video
533
+ [customWidth]="windowWidth"
534
+ [customHeight]="600"
535
+ [parameters]="parameters">
536
+ </app-flexible-video>
537
+
538
+ <app-flexible-grid
539
+ [customWidth]="windowWidth"
540
+ [customHeight]="400"
541
+ [parameters]="parameters">
542
+ </app-flexible-grid>
543
+
544
+ <!-- Custom footer -->
545
+ <div class="custom-footer">
546
+ <button (click)="toggleVideo()">
547
+ {{ parameters.videoAlreadyOn ? 'Stop Video' : 'Start Video' }}
548
+ </button>
549
+ </div>
550
+ </div>
551
+ `,
552
+ styles: [`
553
+ .custom-layout {
554
+ display: flex;
555
+ flex-direction: column;
556
+ height: 100vh;
557
+ }
558
+ .custom-header {
559
+ padding: 20px;
560
+ background: #1976d2;
561
+ color: white;
562
+ }
563
+ `]
564
+ })
565
+ export class CustomMainComponent {
566
+ parameters: any;
567
+ windowWidth = window.innerWidth;
568
+
569
+ toggleVideo() {
570
+ this.parameters?.clickVideo({ parameters: this.parameters });
571
+ }
572
+ }
573
+
574
+ @Component({
575
+ selector: 'app-root',
576
+ standalone: true,
577
+ imports: [MediasfuGeneric],
578
+ template: `
579
+ <app-mediasfu-generic
580
+ [credentials]="credentials"
581
+ [PrejoinPage]="PreJoinPage"
582
+ [customComponent]="CustomMainComponent">
583
+ </app-mediasfu-generic>
584
+ `,
585
+ })
586
+ export class AppComponent {
587
+ PreJoinPage = PreJoinPage;
588
+ CustomMainComponent = CustomMainComponent;
589
+ credentials = { apiUserName: 'username', apiKey: 'key' };
590
+ }
591
+ ```
592
+
593
+ **When to use:**
594
+ - βœ… Need custom main interface but want to keep MediaSFU's components
595
+ - βœ… Partial customization with minimal effort
596
+ - βœ… Want to maintain MediaSFU's functionality while customizing layout
597
+
598
+ #### 3. **Parameters: Your Control Center**
599
+
600
+ The `sourceParameters` object (or `parameters` in custom components) is your gateway to all MediaSFU functionality. It's powered by RxJS BehaviorSubjects for reactive state management:
601
+
602
+ ```typescript
603
+ // Available in sourceParameters or parameters object
604
+ {
605
+ // Media Controls (Methods)
606
+ clickVideo: (options) => {},
607
+ clickAudio: (options) => {},
608
+ clickScreenShare: (options) => {},
609
+
610
+ // Room State (BehaviorSubject values)
611
+ roomName: 'meeting-123',
612
+ participants: [...],
613
+ allVideoStreams: [...],
614
+ allAudioStreams: [...],
615
+
616
+ // UI State (BehaviorSubject values)
617
+ videoAlreadyOn: false,
618
+ audioAlreadyOn: false,
619
+ screenAlreadyOn: false,
620
+
621
+ // Update Functions (BehaviorSubject next())
622
+ updateVideoAlreadyOn: (value) => {},
623
+ updateAudioAlreadyOn: (value) => {},
624
+
625
+ // And 200+ more properties and methods...
626
+ }
627
+ ```
628
+
629
+ **Access patterns:**
630
+
631
+ ```typescript
632
+ // In Mode 1 (Default UI): Parameters are managed internally
633
+ // You don't need to access them directly
634
+
635
+ // In Mode 2 (Custom UI): Access via sourceParameters
636
+ sourceParameters?.clickVideo({ parameters: sourceParameters });
637
+
638
+ // In Mode 3 (Component Replacement): Passed to your custom component
639
+ @Component({
640
+ template: `<button (click)="toggleVideo()">Toggle</button>`
641
+ })
642
+ export class CustomComponent {
643
+ @Input() parameters: any;
644
+
645
+ toggleVideo() {
646
+ this.parameters.clickVideo({ parameters: this.parameters });
647
+ }
648
+ }
649
+
650
+ // Subscribing to reactive state changes
651
+ sourceParameters.participants.subscribe((participants) => {
652
+ console.log('Participants updated:', participants);
653
+ });
654
+ ```
655
+
656
+ ---
657
+
658
+ ## Core Concepts & Components <a name="core-concepts--components"></a>
659
+
660
+ Now that you understand the architecture, let's explore the building blocks.
661
+
662
+ ### 1. Display Components: Building Your Video Layout
663
+
664
+ MediaSFU provides powerful components for organizing and displaying media streams.
665
+
666
+ #### Primary Layout Components
667
+
668
+ **FlexibleVideo** - Main video display area
669
+
670
+ ```typescript
671
+ import { FlexibleVideo } from 'mediasfu-angular';
672
+
673
+ @Component({
674
+ template: `
675
+ <app-flexible-video
676
+ [customWidth]="windowWidth"
677
+ [customHeight]="600"
678
+ [parameters]="parameters">
679
+ </app-flexible-video>
680
+ `
681
+ })
682
+ ```
683
+ - Automatically handles main presenter or screen share
684
+ - Smooth transitions between different video sources
685
+ - Responsive sizing
686
+
687
+ **FlexibleGrid** - Participant grid layout
688
+
689
+ ```typescript
690
+ import { FlexibleGrid } from 'mediasfu-angular';
691
+
692
+ @Component({
693
+ template: `
694
+ <app-flexible-grid
695
+ [customWidth]="windowWidth"
696
+ [customHeight]="800"
697
+ [parameters]="parameters">
698
+ </app-flexible-grid>
699
+ `
700
+ })
701
+ ```
702
+ - Intelligent grid sizing (2x2, 3x3, 4x4, etc.)
703
+ - Pagination for large participant lists
704
+ - Automatic reflow on window resize
705
+
706
+ **AudioGrid** - Audio-only participants
707
+
708
+ ```typescript
709
+ import { AudioGrid } from 'mediasfu-angular';
710
+
711
+ @Component({
712
+ template: `<app-audio-grid [parameters]="parameters"></app-audio-grid>`
713
+ })
714
+ ```
715
+ - Displays participants without video
716
+ - Audio level indicators
717
+ - Compact layout for efficiency
718
+
719
+ #### Container Components
720
+
721
+ | Component | Purpose | Use Case |
722
+ |-----------|---------|----------|
723
+ | **MainContainerComponent** | Primary content wrapper | Wraps all main content areas |
724
+ | **MainAspectComponent** | Aspect ratio container | Maintains proper video proportions |
725
+ | **MainScreenComponent** | Screen layout manager | Organizes screen regions |
726
+ | **SubAspectComponent** | Secondary content container | For picture-in-picture, sidebars |
727
+
728
+ **Example: Building a custom layout**
729
+
730
+ ```typescript
731
+ import { Component } from '@angular/core';
732
+ import {
733
+ MainContainerComponent,
734
+ FlexibleVideo,
735
+ FlexibleGrid,
736
+ AudioGrid
737
+ } from 'mediasfu-angular';
738
+
739
+ @Component({
740
+ selector: 'app-custom-layout',
741
+ standalone: true,
742
+ imports: [
743
+ MainContainerComponent,
744
+ FlexibleVideo,
745
+ FlexibleGrid,
746
+ AudioGrid,
747
+ CommonModule
748
+ ],
749
+ template: `
750
+ <app-main-container-component>
751
+ <div class="layout-container">
752
+ <!-- Main video area -->
753
+ <div class="main-video">
754
+ <app-flexible-video
755
+ [customWidth]="windowWidth"
756
+ [customHeight]="windowHeight * 0.6"
757
+ [parameters]="parameters">
758
+ </app-flexible-video>
759
+ </div>
760
+
761
+ <!-- Participant grid -->
762
+ <div class="participant-grid">
763
+ <app-flexible-grid
764
+ [customWidth]="windowWidth"
765
+ [customHeight]="windowHeight * 0.3"
766
+ [parameters]="parameters">
767
+ </app-flexible-grid>
768
+ </div>
769
+
770
+ <!-- Audio-only participants -->
771
+ <div class="audio-participants">
772
+ <app-audio-grid [parameters]="parameters"></app-audio-grid>
773
+ </div>
774
+ </div>
775
+ </app-main-container-component>
776
+ `,
777
+ styles: [`
778
+ .layout-container {
779
+ display: flex;
780
+ flex-direction: column;
781
+ height: 100vh;
782
+ }
783
+ .main-video { flex: 3; }
784
+ .participant-grid { flex: 2; }
785
+ .audio-participants { height: 80px; }
786
+ `]
787
+ })
788
+ export class CustomLayoutComponent {
789
+ @Input() parameters: any;
790
+ windowWidth = window.innerWidth;
791
+ windowHeight = window.innerHeight;
792
+ }
793
+ ```
794
+
795
+ ### 2. Control Components: User Interactions
796
+
797
+ **ControlButtonsComponent** - Standard control bar
798
+
799
+ ```typescript
800
+ import { ControlButtonsComponent } from 'mediasfu-angular';
801
+
802
+ @Component({
803
+ template: `
804
+ <app-control-buttons-component
805
+ [parameters]="parameters"
806
+ [position]="'bottom'">
807
+ </app-control-buttons-component>
808
+ `
809
+ })
210
810
  ```
811
+ Includes: mute, video, screenshare, participants, chat, settings, etc.
812
+
813
+ **ControlButtonsAltComponent** - Alternative layout
814
+
815
+ ```typescript
816
+ import { ControlButtonsAltComponent } from 'mediasfu-angular';
817
+
818
+ @Component({
819
+ template: `
820
+ <app-control-buttons-alt-component
821
+ [parameters]="parameters"
822
+ [position]="'top'">
823
+ </app-control-buttons-alt-component>
824
+ `
825
+ })
826
+ ```
827
+ Different button arrangement optimized for specific layouts.
828
+
829
+ **ControlButtonsComponentTouch** - Touch-optimized controls
830
+
831
+ ```typescript
832
+ import { ControlButtonsComponentTouch } from 'mediasfu-angular';
833
+
834
+ @Component({
835
+ template: `
836
+ <app-control-buttons-component-touch
837
+ [parameters]="parameters">
838
+ </app-control-buttons-component-touch>
839
+ `
840
+ })
841
+ ```
842
+ Floating action buttons optimized for mobile/tablet interfaces.
843
+
844
+ ### 3. Modal Components: Feature Interfaces
845
+
846
+ MediaSFU includes modals for various features:
847
+
848
+ ```typescript
849
+ import {
850
+ ParticipantsModal,
851
+ MessagesModal,
852
+ SettingsModal,
853
+ DisplaySettingsModal,
854
+ RecordingModal,
855
+ PollModal,
856
+ BreakoutRoomsModal
857
+ } from 'mediasfu-angular';
858
+
859
+ // These are automatically rendered when enabled
860
+ // Control their visibility via parameters
861
+ parameters.updateIsParticipantsModalVisible.next(true);
862
+ parameters.updateIsMessagesModalVisible.next(true);
863
+ parameters.updateIsSettingsModalVisible.next(true);
864
+ ```
865
+
866
+ Available modals:
867
+ - **ParticipantsModal** - Participant list management
868
+ - **MessagesModal** - Chat interface
869
+ - **SettingsModal** - Event and room settings
870
+ - **DisplaySettingsModal** - Layout and display options
871
+ - **RecordingModal** - Recording controls and settings
872
+ - **PollModal** - Create and manage polls
873
+ - **BreakoutRoomsModal** - Breakout room management
874
+ - **MediaSettingsModal** - Camera/microphone selection
875
+ - **BackgroundModal** - Virtual background settings
876
+ - **ConfigureWhiteboardModal** - Whiteboard configuration
877
+
878
+ **Example: Programmatically showing modals**
879
+
880
+ ```typescript
881
+ @Component({
882
+ selector: 'app-custom-toolbar',
883
+ template: `
884
+ <div class="custom-toolbar">
885
+ <button (click)="showParticipants()">
886
+ Show Participants ({{ participantCount }})
887
+ </button>
888
+
889
+ <button (click)="openChat()">
890
+ Open Chat
891
+ </button>
892
+
893
+ <button (click)="createPoll()">
894
+ Create Poll
895
+ </button>
896
+ </div>
897
+ `
898
+ })
899
+ export class CustomToolbarComponent {
900
+ @Input() parameters: any;
901
+
902
+ get participantCount() {
903
+ return this.parameters?.participants?.length || 0;
904
+ }
905
+
906
+ showParticipants() {
907
+ this.parameters?.updateIsParticipantsModalVisible.next(true);
908
+ }
909
+
910
+ openChat() {
911
+ this.parameters?.updateIsMessagesModalVisible.next(true);
912
+ }
913
+
914
+ createPoll() {
915
+ this.parameters?.launchPoll?.launchPoll({ parameters: this.parameters });
916
+ }
917
+ }
918
+ ```
919
+
920
+ ### 4. Video Cards: Individual Participant Display
921
+
922
+ **VideoCard** - Individual participant video element
923
+
924
+ ```typescript
925
+ import { VideoCard } from 'mediasfu-angular';
926
+
927
+ @Component({
928
+ template: `
929
+ <app-video-card
930
+ [videoStream]="participantStream"
931
+ [remoteProducerId]="'producer-id'"
932
+ [eventType]="'conference'"
933
+ [forceFullDisplay]="false"
934
+ [participant]="participantObject"
935
+ [backgroundColor]="'#000000'"
936
+ [showControls]="true"
937
+ [showInfo]="true"
938
+ [name]="'Participant Name'"
939
+ [parameters]="parameters">
940
+ </app-video-card>
941
+ `
942
+ })
943
+ ```
944
+
945
+ **AudioCard** - Individual audio-only participant
946
+
947
+ ```typescript
948
+ import { AudioCard } from 'mediasfu-angular';
949
+
950
+ @Component({
951
+ template: `
952
+ <app-audio-card
953
+ [name]="'Participant Name'"
954
+ [barColor]="'#4CAF50'"
955
+ [textColor]="'#FFFFFF'"
956
+ [customStyle]="{ borderRadius: '10px' }"
957
+ [controlsPosition]="'topLeft'"
958
+ [infoPosition]="'topRight'"
959
+ [participant]="participantObject"
960
+ [parameters]="parameters">
961
+ </app-audio-card>
962
+ `
963
+ })
964
+ ```
965
+
966
+ **MiniCard** - Compact participant display (for grids)
967
+
968
+ ```typescript
969
+ import { MiniCard } from 'mediasfu-angular';
970
+
971
+ @Component({
972
+ template: `
973
+ <app-mini-card
974
+ [participant]="participantObject"
975
+ [showControls]="false"
976
+ [parameters]="parameters">
977
+ </app-mini-card>
978
+ `
979
+ })
980
+ ```
981
+
982
+ **Example: Custom Video Card**
983
+
984
+ ```typescript
985
+ @Component({
986
+ selector: 'app-my-custom-video-card',
987
+ standalone: true,
988
+ template: `
989
+ <div class="custom-video-card">
990
+ <video
991
+ #videoElement
992
+ [srcObject]="stream"
993
+ autoplay
994
+ [muted]="true"
995
+ playsinline>
996
+ </video>
997
+
998
+ <div class="participant-info">
999
+ {{ participant.name }}
1000
+ @if (participant.muted) {
1001
+ <span>πŸ”‡</span>
1002
+ }
1003
+ </div>
1004
+ </div>
1005
+ `,
1006
+ styles: [`
1007
+ .custom-video-card {
1008
+ border: 3px solid #00ff88;
1009
+ border-radius: 15px;
1010
+ overflow: hidden;
1011
+ position: relative;
1012
+ }
1013
+ video {
1014
+ width: 100%;
1015
+ height: 100%;
1016
+ object-fit: cover;
1017
+ }
1018
+ .participant-info {
1019
+ position: absolute;
1020
+ bottom: 0;
1021
+ left: 0;
1022
+ right: 0;
1023
+ background: rgba(0, 255, 136, 0.8);
1024
+ color: black;
1025
+ padding: 8px;
1026
+ font-weight: bold;
1027
+ }
1028
+ `]
1029
+ })
1030
+ export class MyCustomVideoCardComponent {
1031
+ @Input() stream!: MediaStream;
1032
+ @Input() participant!: any;
1033
+ @Input() parameters!: any;
1034
+ }
1035
+
1036
+ // Use it in MediasfuGeneric
1037
+ @Component({
1038
+ template: `
1039
+ <app-mediasfu-generic
1040
+ [credentials]="credentials"
1041
+ [customVideoCard]="CustomVideoCard">
1042
+ </app-mediasfu-generic>
1043
+ `
1044
+ })
1045
+ export class AppComponent {
1046
+ CustomVideoCard = MyCustomVideoCardComponent;
1047
+ credentials = { apiUserName: 'username', apiKey: 'key' };
1048
+ }
1049
+ ```
1050
+
1051
+ ---
1052
+
1053
+ ## Working with Methods <a name="working-with-methods"></a>
1054
+
1055
+ MediaSFU provides 200+ methods for controlling every aspect of your real-time communication experience. Let's explore the most important categories.
1056
+
1057
+ ### Media Control Methods
1058
+
1059
+ #### Video Control
1060
+
1061
+ ```typescript
1062
+ // Toggle video on/off
1063
+ parameters.clickVideo({ parameters });
1064
+
1065
+ // Switch camera (front/back on mobile)
1066
+ parameters.switchVideoAlt({ parameters });
1067
+
1068
+ // Switch to specific camera by ID
1069
+ const cameras = await parameters.getMediaDevicesList('videoinput');
1070
+ parameters.switchUserVideo({
1071
+ videoPreference: cameras[1].deviceId,
1072
+ parameters
1073
+ });
1074
+
1075
+ // Get current video state
1076
+ const isVideoOn = parameters.videoAlreadyOn;
1077
+
1078
+ // Subscribe to video state changes (RxJS)
1079
+ parameters.videoAlreadyOn.subscribe((isOn: boolean) => {
1080
+ console.log('Video is now:', isOn ? 'ON' : 'OFF');
1081
+ });
1082
+
1083
+ // Update video state programmatically
1084
+ parameters.updateVideoAlreadyOn.next(true);
1085
+ ```
1086
+
1087
+ #### Audio Control
1088
+
1089
+ ```typescript
1090
+ // Toggle audio on/off
1091
+ parameters.clickAudio({ parameters });
1092
+
1093
+ // Switch microphone
1094
+ const microphones = await parameters.getMediaDevicesList('audioinput');
1095
+ parameters.switchUserAudio({
1096
+ audioPreference: microphones[1].deviceId,
1097
+ parameters
1098
+ });
1099
+
1100
+ // Get current audio state
1101
+ const isAudioOn = parameters.audioAlreadyOn;
1102
+ const hasHostPermission = parameters.micAction; // Host approval status
1103
+
1104
+ // Subscribe to audio state changes
1105
+ parameters.audioAlreadyOn.subscribe((isOn: boolean) => {
1106
+ console.log('Audio is now:', isOn ? 'ON' : 'OFF');
1107
+ });
1108
+
1109
+ // Mute/unmute specific participant (host only)
1110
+ parameters.controlMedia({
1111
+ participantId: 'participant-id',
1112
+ participantName: 'John Doe',
1113
+ type: 'audio',
1114
+ socket: parameters.socket,
1115
+ roomName: parameters.roomName
1116
+ });
1117
+ ```
1118
+
1119
+ #### Screen Sharing
1120
+
1121
+ ```typescript
1122
+ // Start screen sharing
1123
+ parameters.clickScreenShare({ parameters });
1124
+
1125
+ // Stop screen sharing
1126
+ parameters.stopShareScreen({ parameters });
1127
+
1128
+ // Check if screen sharing is available
1129
+ const canShare = await parameters.checkScreenShare({ parameters });
1130
+
1131
+ // Get screen share state
1132
+ const isSharing = parameters.screenAlreadyOn;
1133
+ const shareAudio = parameters.shareScreenStarted; // Sharing with audio
1134
+
1135
+ // Subscribe to screen share state
1136
+ parameters.screenAlreadyOn.subscribe((isSharing: boolean) => {
1137
+ console.log('Screen sharing:', isSharing ? 'ACTIVE' : 'INACTIVE');
1138
+ });
1139
+ ```
1140
+
1141
+ ### Media Device and Stream Utility Methods
1142
+
1143
+ #### Get Available Media Devices
1144
+
1145
+ ```typescript
1146
+ // Get available cameras
1147
+ const cameras = await parameters.getMediaDevicesList('videoinput');
1148
+ cameras.forEach(camera => {
1149
+ console.log(`Camera: ${camera.label} (${camera.deviceId})`);
1150
+ });
1151
+
1152
+ // Get available microphones
1153
+ const microphones = await parameters.getMediaDevicesList('audioinput');
1154
+ microphones.forEach(mic => {
1155
+ console.log(`Microphone: ${mic.label} (${mic.deviceId})`);
1156
+ });
1157
+
1158
+ // Building a device selector UI
1159
+ @Component({
1160
+ selector: 'app-device-selector',
1161
+ template: `
1162
+ <div class="device-selector">
1163
+ <select (change)="onCameraChange($event)">
1164
+ <option value="">Select Camera</option>
1165
+ @for (camera of cameras; track camera.deviceId) {
1166
+ <option [value]="camera.deviceId">
1167
+ {{ camera.label }}
1168
+ </option>
1169
+ }
1170
+ </select>
1171
+
1172
+ <select (change)="onMicrophoneChange($event)">
1173
+ <option value="">Select Microphone</option>
1174
+ @for (mic of microphones; track mic.deviceId) {
1175
+ <option [value]="mic.deviceId">
1176
+ {{ mic.label }}
1177
+ </option>
1178
+ }
1179
+ </select>
1180
+ </div>
1181
+ `
1182
+ })
1183
+ export class DeviceSelectorComponent implements OnInit {
1184
+ @Input() parameters: any;
1185
+ cameras: MediaDeviceInfo[] = [];
1186
+ microphones: MediaDeviceInfo[] = [];
1187
+
1188
+ async ngOnInit() {
1189
+ await this.loadDevices();
1190
+ }
1191
+
1192
+ async loadDevices() {
1193
+ this.cameras = await this.parameters.getMediaDevicesList('videoinput');
1194
+ this.microphones = await this.parameters.getMediaDevicesList('audioinput');
1195
+ }
1196
+
1197
+ onCameraChange(event: any) {
1198
+ this.parameters.switchUserVideo({
1199
+ videoPreference: event.target.value,
1200
+ parameters: this.parameters
1201
+ });
1202
+ }
1203
+
1204
+ onMicrophoneChange(event: any) {
1205
+ this.parameters.switchUserAudio({
1206
+ audioPreference: event.target.value,
1207
+ parameters: this.parameters
1208
+ });
1209
+ }
1210
+ }
1211
+ ```
1212
+
1213
+ #### Get Participant Media Streams
1214
+
1215
+ ```typescript
1216
+ // Get participant video stream by ID
1217
+ const videoStream = await parameters.getParticipantMedia({
1218
+ id: 'producer-123',
1219
+ kind: 'video'
1220
+ });
1221
+
1222
+ // Get participant audio stream by name
1223
+ const audioStream = await parameters.getParticipantMedia({
1224
+ name: 'John Doe',
1225
+ kind: 'audio'
1226
+ });
1227
+
1228
+ // Example: Custom participant stream monitor
1229
+ @Component({
1230
+ selector: 'app-stream-monitor',
1231
+ template: `
1232
+ <div class="stream-monitor">
1233
+ <h3>Participant Streams</h3>
1234
+ @for (participant of participants; track participant.id) {
1235
+ <div class="participant-item">
1236
+ <span>{{ participant.name }}</span>
1237
+ <button (click)="viewStream(participant)">View Stream</button>
1238
+ <button (click)="monitorAudio(participant)">Monitor Audio</button>
1239
+ </div>
1240
+ }
1241
+
1242
+ @if (selectedStream) {
1243
+ <div class="stream-viewer">
1244
+ <video #streamVideo autoplay playsinline></video>
1245
+ </div>
1246
+ }
1247
+ </div>
1248
+ `,
1249
+ styles: [`
1250
+ .stream-monitor { padding: 20px; }
1251
+ .participant-item {
1252
+ margin: 10px 0;
1253
+ display: flex;
1254
+ gap: 10px;
1255
+ align-items: center;
1256
+ }
1257
+ .stream-viewer video {
1258
+ width: 100%;
1259
+ max-width: 640px;
1260
+ border: 2px solid #1976d2;
1261
+ }
1262
+ `]
1263
+ })
1264
+ export class StreamMonitorComponent {
1265
+ @Input() parameters: any;
1266
+ @ViewChild('streamVideo') videoElement!: ElementRef<HTMLVideoElement>;
1267
+
1268
+ selectedStream: MediaStream | null = null;
1269
+
1270
+ get participants() {
1271
+ return this.parameters?.participants || [];
1272
+ }
1273
+
1274
+ async viewStream(participant: any) {
1275
+ const stream = await this.parameters.getParticipantMedia({
1276
+ id: participant.videoID,
1277
+ name: participant.name,
1278
+ kind: 'video'
1279
+ });
1280
+
1281
+ if (stream && this.videoElement) {
1282
+ this.selectedStream = stream;
1283
+ this.videoElement.nativeElement.srcObject = stream;
1284
+ } else {
1285
+ console.log('No video stream found for participant');
1286
+ }
1287
+ }
1288
+
1289
+ async monitorAudio(participant: any) {
1290
+ const stream = await this.parameters.getParticipantMedia({
1291
+ id: participant.audioID,
1292
+ name: participant.name,
1293
+ kind: 'audio'
1294
+ });
1295
+
1296
+ if (stream) {
1297
+ // Create audio context for analysis
1298
+ const audioContext = new AudioContext();
1299
+ const analyser = audioContext.createAnalyser();
1300
+ const source = audioContext.createMediaStreamSource(stream);
1301
+ source.connect(analyser);
1302
+
1303
+ console.log('Monitoring audio for:', participant.name);
1304
+ // Add your audio analysis logic here
1305
+ } else {
1306
+ console.log('No audio stream found for participant');
1307
+ }
1308
+ }
1309
+ }
1310
+ ```
1311
+
1312
+ ### Participant Management Methods
1313
+
1314
+ ```typescript
1315
+ // Get all participants
1316
+ const participants = parameters.participants;
1317
+ const participantCount = parameters.participantsCounter;
1318
+
1319
+ // Subscribe to participant changes
1320
+ parameters.participants.subscribe((participants: any[]) => {
1321
+ console.log('Participants updated:', participants);
1322
+ });
1323
+
1324
+ // Filter participants
1325
+ const videoParticipants = participants.filter((p: any) => p.videoOn);
1326
+ const audioOnlyParticipants = participants.filter((p: any) => !p.videoOn);
1327
+ const mutedParticipants = participants.filter((p: any) => p.muted);
1328
+
1329
+ // Find specific participant
1330
+ const participant = participants.find((p: any) => p.name === 'John Doe');
1331
+
1332
+ // Remove participant from room (host only)
1333
+ parameters.disconnectUserInitiate({
1334
+ member: participantId,
1335
+ roomName: parameters.roomName,
1336
+ socket: parameters.socket
1337
+ });
1338
+
1339
+ // Change participant role (host only)
1340
+ parameters.updateParticipant({
1341
+ participantId: 'participant-id',
1342
+ islevel: '2', // '2' = host, '1' = co-host, '0' = participant
1343
+ parameters
1344
+ });
1345
+
1346
+ // Request to unmute participant (sends request)
1347
+ parameters.requestScreenShare({ parameters });
1348
+ ```
1349
+
1350
+ ### Chat & Messaging Methods
1351
+
1352
+ ```typescript
1353
+ // Send a group message
1354
+ parameters.sendMessage({
1355
+ message: 'Hello everyone!',
1356
+ type: 'group',
1357
+ parameters
1358
+ });
1359
+
1360
+ // Send direct message
1361
+ parameters.sendMessage({
1362
+ message: 'Private message',
1363
+ type: 'direct',
1364
+ receivers: ['participant-id'],
1365
+ parameters
1366
+ });
1367
+
1368
+ // Access message history
1369
+ const messages = parameters.messages;
1370
+
1371
+ // Subscribe to new messages (RxJS)
1372
+ parameters.messages.subscribe((messages: any[]) => {
1373
+ console.log('Messages updated:', messages);
1374
+ });
1375
+
1376
+ // Example: Custom chat component
1377
+ @Component({
1378
+ selector: 'app-custom-chat',
1379
+ standalone: true,
1380
+ imports: [CommonModule, FormsModule],
1381
+ template: `
1382
+ <div class="chat-container">
1383
+ <div class="messages">
1384
+ @for (msg of messages; track msg.timestamp) {
1385
+ <div class="message">
1386
+ <strong>{{ msg.sender }}:</strong> {{ msg.message }}
1387
+ </div>
1388
+ }
1389
+ </div>
1390
+ <div class="input-area">
1391
+ <input
1392
+ [(ngModel)]="message"
1393
+ (keyup.enter)="sendMessage()"
1394
+ placeholder="Type a message..."
1395
+ />
1396
+ <button (click)="sendMessage()">Send</button>
1397
+ </div>
1398
+ </div>
1399
+ `,
1400
+ styles: [`
1401
+ .chat-container {
1402
+ display: flex;
1403
+ flex-direction: column;
1404
+ height: 400px;
1405
+ }
1406
+ .messages {
1407
+ flex: 1;
1408
+ overflow-y: auto;
1409
+ padding: 10px;
1410
+ }
1411
+ .message {
1412
+ margin: 5px 0;
1413
+ padding: 8px;
1414
+ background: #f5f5f5;
1415
+ border-radius: 4px;
1416
+ }
1417
+ .input-area {
1418
+ display: flex;
1419
+ gap: 10px;
1420
+ padding: 10px;
1421
+ border-top: 1px solid #ddd;
1422
+ }
1423
+ input {
1424
+ flex: 1;
1425
+ padding: 8px;
1426
+ border: 1px solid #ddd;
1427
+ border-radius: 4px;
1428
+ }
1429
+ button {
1430
+ padding: 8px 16px;
1431
+ background: #1976d2;
1432
+ color: white;
1433
+ border: none;
1434
+ border-radius: 4px;
1435
+ cursor: pointer;
1436
+ }
1437
+ `]
1438
+ })
1439
+ export class CustomChatComponent implements OnInit {
1440
+ @Input() parameters: any;
1441
+ message = '';
1442
+ messages: any[] = [];
1443
+
1444
+ ngOnInit() {
1445
+ // Subscribe to messages
1446
+ this.parameters?.messages?.subscribe((msgs: any[]) => {
1447
+ this.messages = msgs;
1448
+ });
1449
+ }
1450
+
1451
+ sendMessage() {
1452
+ if (this.message.trim()) {
1453
+ this.parameters?.sendMessage({
1454
+ message: this.message,
1455
+ type: 'group',
1456
+ parameters: this.parameters
1457
+ });
1458
+ this.message = '';
1459
+ }
1460
+ }
1461
+ }
1462
+ ```
1463
+
1464
+ ### Recording Methods
1465
+
1466
+ ```typescript
1467
+ // Start recording
1468
+ parameters.startRecording({ parameters });
1469
+
1470
+ // Stop recording
1471
+ parameters.stopRecording({ parameters });
1472
+
1473
+ // Pause recording
1474
+ parameters.pauseRecording({ parameters });
1475
+
1476
+ // Resume recording
1477
+ parameters.resumeRecording({ parameters });
1478
+
1479
+ // Configure recording settings
1480
+ parameters.updateRecording({
1481
+ recordingMediaOptions: 'video', // or 'audio'
1482
+ recordingAudioOptions: 'all', // or 'host'
1483
+ recordingVideoOptions: 'all', // or 'host'
1484
+ recordingVideoType: 'fullDisplay', // or 'bestDisplay', 'all'
1485
+ recordingDisplayType: 'video', // 'media', 'video', 'all'
1486
+ recordingBackgroundColor: '#000000',
1487
+ recordingNameTagsColor: '#ffffff',
1488
+ recordingOrientationVideo: 'landscape', // or 'portrait'
1489
+ recordingNameTags: true,
1490
+ recordingAddHLS: false,
1491
+ parameters
1492
+ });
1493
+
1494
+ // Check recording state
1495
+ const isRecording = parameters.recordStarted;
1496
+ const isPaused = parameters.recordPaused;
1497
+ const recordingTime = parameters.recordElapsedTime;
1498
+
1499
+ // Subscribe to recording state changes
1500
+ parameters.recordStarted.subscribe((isRecording: boolean) => {
1501
+ console.log('Recording:', isRecording ? 'ACTIVE' : 'STOPPED');
1502
+ });
1503
+ ```
1504
+
1505
+ ### Polls & Surveys Methods
1506
+
1507
+ ```typescript
1508
+ // Create a poll
1509
+ parameters.handleCreatePoll({
1510
+ poll: {
1511
+ question: 'What time works best?',
1512
+ type: 'multiple', // or 'single'
1513
+ options: ['10 AM', '2 PM', '5 PM']
1514
+ },
1515
+ parameters
1516
+ });
1517
+
1518
+ // Vote on a poll
1519
+ parameters.handleVotePoll({
1520
+ pollId: 'poll-id',
1521
+ optionIndex: 1,
1522
+ parameters
1523
+ });
1524
+
1525
+ // End a poll
1526
+ parameters.handleEndPoll({
1527
+ pollId: 'poll-id',
1528
+ parameters
1529
+ });
1530
+
1531
+ // Access poll data
1532
+ const polls = parameters.polls;
1533
+
1534
+ // Subscribe to polls changes
1535
+ parameters.polls.subscribe((polls: any[]) => {
1536
+ const activePoll = polls.find(p => p.status === 'active');
1537
+ console.log('Active poll:', activePoll);
1538
+ });
1539
+
1540
+ // Example: Custom poll component
1541
+ @Component({
1542
+ selector: 'app-custom-poll',
1543
+ standalone: true,
1544
+ imports: [CommonModule],
1545
+ template: `
1546
+ @if (activePoll) {
1547
+ <div class="poll-container">
1548
+ <h3>{{ activePoll.question }}</h3>
1549
+ <div class="poll-options">
1550
+ @for (option of activePoll.options; track $index) {
1551
+ <button
1552
+ class="poll-option"
1553
+ (click)="vote($index)">
1554
+ {{ option }}
1555
+ <span class="votes">({{ activePoll.votes?.[$index] || 0 }} votes)</span>
1556
+ </button>
1557
+ }
1558
+ </div>
1559
+ @if (isHost) {
1560
+ <button class="end-poll" (click)="endPoll()">
1561
+ End Poll
1562
+ </button>
1563
+ }
1564
+ </div>
1565
+ }
1566
+ `,
1567
+ styles: [`
1568
+ .poll-container {
1569
+ padding: 20px;
1570
+ background: white;
1571
+ border-radius: 8px;
1572
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
1573
+ }
1574
+ .poll-options {
1575
+ display: flex;
1576
+ flex-direction: column;
1577
+ gap: 10px;
1578
+ margin: 20px 0;
1579
+ }
1580
+ .poll-option {
1581
+ padding: 12px;
1582
+ border: 2px solid #1976d2;
1583
+ background: white;
1584
+ border-radius: 4px;
1585
+ cursor: pointer;
1586
+ transition: all 0.3s;
1587
+ display: flex;
1588
+ justify-content: space-between;
1589
+ }
1590
+ .poll-option:hover {
1591
+ background: #1976d2;
1592
+ color: white;
1593
+ }
1594
+ .votes {
1595
+ font-size: 0.9em;
1596
+ opacity: 0.7;
1597
+ }
1598
+ .end-poll {
1599
+ padding: 10px 20px;
1600
+ background: #d32f2f;
1601
+ color: white;
1602
+ border: none;
1603
+ border-radius: 4px;
1604
+ cursor: pointer;
1605
+ }
1606
+ `]
1607
+ })
1608
+ export class CustomPollComponent implements OnInit {
1609
+ @Input() parameters: any;
1610
+ activePoll: any = null;
1611
+
1612
+ ngOnInit() {
1613
+ this.parameters?.polls?.subscribe((polls: any[]) => {
1614
+ this.activePoll = polls.find((p: any) => p.status === 'active');
1615
+ });
1616
+ }
1617
+
1618
+ get isHost() {
1619
+ return this.parameters?.islevel === '2';
1620
+ }
1621
+
1622
+ vote(optionIndex: number) {
1623
+ if (this.activePoll) {
1624
+ this.parameters?.handleVotePoll({
1625
+ pollId: this.activePoll.id,
1626
+ optionIndex,
1627
+ parameters: this.parameters
1628
+ });
1629
+ }
1630
+ }
1631
+
1632
+ endPoll() {
1633
+ if (this.activePoll) {
1634
+ this.parameters?.handleEndPoll({
1635
+ pollId: this.activePoll.id,
1636
+ parameters: this.parameters
1637
+ });
1638
+ }
1639
+ }
1640
+ }
1641
+ ```
1642
+
1643
+ ### Breakout Rooms Methods
1644
+
1645
+ ```typescript
1646
+ // Create breakout rooms
1647
+ parameters.createBreakoutRooms({
1648
+ numberOfRooms: 3,
1649
+ participants: parameters.participants,
1650
+ parameters
1651
+ });
1652
+
1653
+ // Assign participant to room
1654
+ parameters.assignParticipantToRoom({
1655
+ participantId: 'participant-id',
1656
+ roomIndex: 0,
1657
+ parameters
1658
+ });
1659
+
1660
+ // Start breakout rooms
1661
+ parameters.startBreakoutRooms({ parameters });
1662
+
1663
+ // Stop breakout rooms
1664
+ parameters.stopBreakoutRooms({ parameters });
1665
+
1666
+ // Access breakout room data
1667
+ const breakoutRooms = parameters.breakoutRooms;
1668
+ const currentRoom = parameters.currentBreakoutRoom;
1669
+
1670
+ // Subscribe to breakout room changes
1671
+ parameters.breakoutRooms.subscribe((rooms: any[]) => {
1672
+ console.log('Breakout rooms:', rooms);
1673
+ });
1674
+ ```
1675
+
1676
+ ### Whiteboard Methods
1677
+
1678
+ ```typescript
1679
+ // Show/hide whiteboard
1680
+ parameters.updateWhiteboardStarted.next(true);
1681
+ parameters.updateWhiteboardEnded.next(false);
1682
+
1683
+ // Configure whiteboard
1684
+ parameters.launchConfigureWhiteboard?.launchConfigureWhiteboard({ parameters });
1685
+
1686
+ // Access whiteboard state
1687
+ const isWhiteboardActive = parameters.whiteboardStarted;
1688
+
1689
+ // Subscribe to whiteboard state
1690
+ parameters.whiteboardStarted.subscribe((isActive: boolean) => {
1691
+ console.log('Whiteboard:', isActive ? 'ACTIVE' : 'INACTIVE');
1692
+ });
1693
+
1694
+ // Access whiteboard users
1695
+ const whiteboardUsers = parameters.whiteboardUsers;
1696
+ ```
1697
+
1698
+ ### Utility Methods
1699
+
1700
+ ```typescript
1701
+ // Check permissions
1702
+ const hasPermission = await parameters.checkPermission({
1703
+ permissionType: 'video', // or 'audio'
1704
+ parameters
1705
+ });
1706
+
1707
+ // Format large numbers
1708
+ const formatted = parameters.formatNumber(1250000); // Returns "1.25M"
1709
+
1710
+ // Sleep/delay
1711
+ await parameters.sleep({ ms: 1000 });
1712
+
1713
+ // Update display settings
1714
+ parameters.updateMainWindow.next(true); // Show/hide main window
1715
+
1716
+ // Trigger layout recalculation
1717
+ parameters.onScreenChanges({ changed: true, parameters });
1718
+
1719
+ // Get room information
1720
+ const roomInfo = {
1721
+ name: parameters.roomName,
1722
+ host: parameters.host,
1723
+ capacity: parameters.capacity,
1724
+ eventType: parameters.eventType,
1725
+ participants: parameters.participants,
1726
+ isRecording: parameters.recordStarted
1727
+ };
1728
+
1729
+ // Subscribe to room state changes
1730
+ parameters.roomName.subscribe((name: string) => {
1731
+ console.log('Room name:', name);
1732
+ });
1733
+
1734
+ parameters.participantsCounter.subscribe((count: number) => {
1735
+ console.log('Participant count:', count);
1736
+ });
1737
+ ```
1738
+
1739
+ ---
211
1740
 
212
1741
  ## Programmatically Fetching Tokens
213
1742