mediasfu-angular 2.2.0 → 2.2.2

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.
Files changed (79) hide show
  1. package/README.md +2288 -375
  2. package/dist/README.md +2288 -375
  3. package/dist/fesm2022/mediasfu-angular.mjs +9519 -4188
  4. package/dist/fesm2022/mediasfu-angular.mjs.map +1 -1
  5. package/dist/lib/@types/types.d.ts +6 -1
  6. package/dist/lib/@types/ui-overrides.types.d.ts +310 -0
  7. package/dist/lib/components/background-components/background-modal/background-modal.component.d.ts +1023 -2
  8. package/dist/lib/components/breakout-components/breakout-rooms-modal.component.d.ts +1069 -45
  9. package/dist/lib/components/breakout-components/edit-room-modal/edit-room-modal.component.d.ts +1054 -42
  10. package/dist/lib/components/co-host-components/co-host-modal/co-host-modal.component.d.ts +1067 -56
  11. package/dist/lib/components/display-components/alert-component/alert.component.component.d.ts +80 -24
  12. package/dist/lib/components/display-components/audio-grid/audio-grid.component.d.ts +83 -11
  13. package/dist/lib/components/display-components/flexible-grid/flexible-grid.component.d.ts +96 -33
  14. package/dist/lib/components/display-components/loading-modal/loading-modal.component.d.ts +16 -26
  15. package/dist/lib/components/display-components/main-aspect-component/main-aspect-component.component.d.ts +6 -2
  16. package/dist/lib/components/display-components/main-container-component/main-container-component.component.d.ts +6 -2
  17. package/dist/lib/components/display-components/main-grid-component/main-grid-component.component.d.ts +7 -13
  18. package/dist/lib/components/display-components/main-screen-component/main-screen-component.component.d.ts +508 -7
  19. package/dist/lib/components/display-components/other-grid-component/other-grid-component.component.d.ts +7 -1
  20. package/dist/lib/components/display-components/sub-aspect-component/sub-aspect-component.component.d.ts +7 -2
  21. package/dist/lib/components/display-settings-components/display-settings-modal.component.d.ts +1107 -27
  22. package/dist/lib/components/event-settings-components/event-settings-modal/event-settings-modal.component.d.ts +1134 -49
  23. package/dist/lib/components/exit-components/confirm-exit-modal/confirm-exit-modal.component.d.ts +94 -32
  24. package/dist/lib/components/media-settings-components/media-settings-modal/media-settings-modal.component.d.ts +1123 -47
  25. package/dist/lib/components/mediasfu-components/mediasfu-broadcast.component.d.ts +24966 -73
  26. package/dist/lib/components/mediasfu-components/mediasfu-chat.component.d.ts +276 -73
  27. package/dist/lib/components/mediasfu-components/mediasfu-conference.component.d.ts +18132 -93
  28. package/dist/lib/components/mediasfu-components/mediasfu-generic.component.d.ts +56344 -93
  29. package/dist/lib/components/mediasfu-components/mediasfu-webinar.component.d.ts +20849 -93
  30. package/dist/lib/components/menu-components/custom-buttons/custom-buttons.component.d.ts +23 -4
  31. package/dist/lib/components/menu-components/meeting-id-component/meeting-id-component.component.d.ts +78 -1
  32. package/dist/lib/components/menu-components/meeting-passcode-component/meeting-passcode-component.component.d.ts +37 -1
  33. package/dist/lib/components/menu-components/menu-modal/menu-modal.component.d.ts +689 -9
  34. package/dist/lib/components/menu-components/share-buttons-component/share-buttons-component.component.d.ts +46 -2
  35. package/dist/lib/components/message-components/messages-modal/messages-modal.component.d.ts +76 -13
  36. package/dist/lib/components/misc-components/confirm-here-modal/confirm-here-modal.component.d.ts +1113 -17
  37. package/dist/lib/components/misc-components/share-event-modal/share-event-modal.component.d.ts +1114 -29
  38. package/dist/lib/components/participants-components/participants-modal/participants-modal.component.d.ts +1084 -6
  39. package/dist/lib/components/polls-components/poll-modal/poll-modal.component.d.ts +1060 -21
  40. package/dist/lib/components/recording-components/recording-modal/recording-modal.component.d.ts +1054 -35
  41. package/dist/lib/components/requests-components/requests-modal/requests-modal.component.d.ts +1117 -45
  42. package/dist/lib/components/screenboard-components/screenboard-modal/screenboard-modal.component.d.ts +1059 -47
  43. package/dist/lib/components/waiting-components/waiting-room-modal.component.d.ts +1119 -46
  44. package/dist/lib/components/whiteboard-components/configure-whiteboard-modal/configure-whiteboard-modal.component.d.ts +1049 -16
  45. package/dist/lib/consumers/add-videos-grid.service.d.ts +3 -0
  46. package/dist/lib/consumers/connect-ips.service.d.ts +3 -1
  47. package/dist/lib/consumers/connect-recv-transport.service.d.ts +4 -1
  48. package/dist/lib/consumers/connect-send-transport-audio.service.d.ts +5 -1
  49. package/dist/lib/consumers/connect-send-transport-screen.service.d.ts +6 -1
  50. package/dist/lib/consumers/connect-send-transport-video.service.d.ts +6 -1
  51. package/dist/lib/consumers/connect-send-transport.service.d.ts +3 -1
  52. package/dist/lib/consumers/consumer-resume.service.d.ts +2 -1
  53. package/dist/lib/consumers/create-send-transport.service.d.ts +4 -1
  54. package/dist/lib/consumers/disconnect-send-transport-audio.service.d.ts +3 -1
  55. package/dist/lib/consumers/disconnect-send-transport-screen.service.d.ts +3 -1
  56. package/dist/lib/consumers/disconnect-send-transport-video.service.d.ts +3 -1
  57. package/dist/lib/consumers/prepopulate-user-media.service.d.ts +3 -0
  58. package/dist/lib/consumers/resume-send-transport-audio.service.d.ts +3 -1
  59. package/dist/lib/consumers/signal-new-consumer-transport.service.d.ts +3 -1
  60. package/dist/lib/consumers/socket-receive-methods/join-consume-room.service.d.ts +3 -1
  61. package/dist/lib/consumers/socket-receive-methods/new-pipe-producer.service.d.ts +3 -1
  62. package/dist/lib/consumers/stream-success-audio-switch.service.d.ts +5 -1
  63. package/dist/lib/consumers/stream-success-audio.service.d.ts +3 -1
  64. package/dist/lib/consumers/stream-success-video.service.d.ts +5 -1
  65. package/dist/lib/directives/with-override.directive.d.ts +76 -0
  66. package/dist/lib/methods/utils/initial-values.util.d.ts +7 -2
  67. package/dist/lib/methods/utils/mini-audio-player/mini-audio-player.component.d.ts +3 -1
  68. package/dist/lib/methods/utils/producer/a-params.service.d.ts +4 -1
  69. package/dist/lib/methods/utils/producer/h-params.service.d.ts +4 -1
  70. package/dist/lib/methods/utils/producer/screen-params.service.d.ts +4 -1
  71. package/dist/lib/methods/utils/producer/v-params.service.d.ts +4 -1
  72. package/dist/lib/methods/whiteboard-methods/capture-canvas-stream.service.d.ts +3 -1
  73. package/dist/lib/producer-client/producer-client-emits/create-device-client.service.d.ts +4 -1
  74. package/dist/lib/producer-client/producer-client-emits/update-room-parameters-client.service.d.ts +3 -1
  75. package/dist/lib/producers/producer-emits/join-con-room.service.d.ts +3 -1
  76. package/dist/lib/producers/socket-receive-methods/get-domains.service.d.ts +3 -1
  77. package/dist/lib/services/ui-override-resolver.service.d.ts +91 -0
  78. package/dist/public-api.d.ts +4 -0
  79. package/package.json +2 -2
package/README.md CHANGED
@@ -4,19 +4,34 @@
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
+ </a>
21
+ </p>
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" />
20
35
  </a>
21
36
  </p>
22
37
 
@@ -59,20 +74,359 @@ MediaSFU offers a cutting-edge streaming experience that empowers users to custo
59
74
 
60
75
  **[Get started now on GitHub!](https://github.com/MediaSFU/MediaSFUOpen)**
61
76
 
77
+ ### ✅ Angular SDK Setup Guide
78
+ Coming soon! Watch this space for our comprehensive video tutorial on setting up the Angular SDK.
79
+
62
80
  ---
63
81
 
64
82
  ## Table of Contents
65
-
83
+ - [Quick Reference: Component Props & UI Overrides](#quick-reference-component-props--ui-overrides)
66
84
  - [Features](#features)
67
85
  - [Getting Started](#getting-started)
68
- - [Basic Usage Guide](#basic-usage-guide)
69
- - [Intermediate Usage Guide](#intermediate-usage-guide)
70
- - [Advanced Usage Guide](#advanced-usage-guide)
71
- - [Custom Component Injection Service](#custom-component-injection-service)
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)
72
93
  - [API Reference](#api-reference)
73
94
  - [Troubleshooting](#troubleshooting)
74
95
  - [Contributing](#contributing)
75
96
 
97
+
98
+ ---
99
+
100
+ ## Quick Reference: Component Props & UI Overrides
101
+
102
+ > **New:** UI override parity now extends across Webinar and Chat layouts, unifying customization for every MediaSFU interface.
103
+
104
+ Every primary MediaSFU UI export—`MediasfuGeneric`, `MediasfuBroadcast`, `MediasfuConference`, `MediasfuWebinar`, and `MediasfuChat`—now ships with a consistent prop surface and a powerful `uiOverrides` input, so you can bend the bundled experience to match your product without losing MediaSFU's hardened real-time logic.
105
+
106
+ ### Shared component inputs (applies to every MediaSFU UI component)
107
+
108
+ | Input | Type | Default | What it does |
109
+ | --- | --- | --- | --- |
110
+ | `PrejoinPage` | `ComponentType` | `WelcomePage` | Swap in a custom pre-join experience. Receives unified pre-join options so you can add branding, legal copy, or warm-up flows. |
111
+ | `localLink` | `string` | `""` | Point the SDK at your self-hosted MediaSFU server. Leave empty when using MediaSFU Cloud. |
112
+ | `connectMediaSFU` | `boolean` | `true` | Toggle automatic socket/WebRTC connections. Set to `false` when you only need the UI shell. |
113
+ | `credentials` | `{ apiUserName: string; apiKey: string }` | `{ apiUserName: "", apiKey: "" }` | Supply cloud credentials without hard-coding them elsewhere. |
114
+ | `useLocalUIMode` | `boolean` | `false` | Run the interface in local/demo mode with no remote signaling. |
115
+ | `seedData`, `useSeed` | `SeedData`, `boolean` | `{}`, `false` | Pre-populate the UI for demos, snapshot tests, or onboarding tours. |
116
+ | `imgSrc` | `string` | `https://mediasfu.com/images/logo192.png` | Default artwork used across pre-join and modal flows. |
117
+ | `sourceParameters` | `Record<string, unknown>` | `undefined` | Shared helper bag (media devices, participant helpers, layout handlers). Pair with `updateSourceParameters` to mirror the SDK's internal utilities. |
118
+ | `updateSourceParameters` | `EventEmitter` | `undefined` | Receive the latest helper bundle so you can bridge MediaSFU logic into your own components. |
119
+ | `returnUI` | `boolean` | `true` | When `false`, mount the logic only—a perfect stepping stone to a fully bespoke interface. |
120
+ | `noUIPreJoinOptions` | `CreateJoinRoomParameters \| JoinLocalEventRoomParameters` | `undefined` | Feed pre-join data when `returnUI` is `false` and you want to bypass the on-screen wizard. |
121
+ | `joinRoom`, `createRoom` | `Function` | `undefined` | Inject your own networking layers for joining or creating rooms. |
122
+ | `customComponent` | `ComponentType` | `undefined` | Replace the entire UI while retaining transports, sockets, and helpers. |
123
+ | `customVideoCard`, `customAudioCard`, `customMiniCard` | `ComponentType` | `undefined` | Override participant card renders to add metadata, CTAs, or badges. |
124
+ | `[customStyles]` | `Record<string, any>` | `undefined` | Apply inline styles to the root wrapper (dashboards, split views, etc.). |
125
+ | `[uiOverrides]` | `MediasfuUICustomOverrides` | `undefined` | Targeted component/function overrides described below. |
126
+
127
+ > **Power combo:** Set `returnUI="false"` to run MediaSFU logic headless, capture helpers via `updateSourceParameters` output, and selectively bring UI pieces back with `uiOverrides`. That gives you progressive migration with minimal code churn.
128
+
129
+ ```typescript
130
+ import type { MediasfuUICustomOverrides } from 'mediasfu-angular';
131
+
132
+ const overrides: MediasfuUICustomOverrides = { /* ... */ };
133
+ ```
134
+
135
+ Bring the types into your project to unlock full IntelliSense for every override slot.
136
+
137
+ ### Custom UI Playbook
138
+
139
+ Use a toggle-driven "playbook" component to experiment with MediaSFU's customization layers. Flip a couple of booleans and you can watch the SDK jump between prebuilt layouts, headless logic, or a fully bespoke workspace driven by `customComponent`.
140
+
141
+ #### What the playbook demonstrates
142
+
143
+ - **Connection presets**: toggle `connectionScenario` between `cloud`, `hybrid`, or `ce` to swap credentials, local links, and connection modes in one place.
144
+ - **Experience selector**: the `selectedExperience` switch renders `MediasfuGeneric`, `MediasfuBroadcast`, `MediasfuWebinar`, `MediasfuConference`, or `MediasfuChat` without touching the rest of your stack.
145
+ - **UI strategy flags**: booleans like `showPrebuiltUI`, `enableFullCustomUI`, and `enableNoUIPreJoin` demonstrate how to run the MediaSFU logic with or without the bundled UI.
146
+ - **Layered overrides**: toggles enable the custom video/audio/mini cards, drop-in `uiOverrides` for layout and modal surfaces, container styling, and backend proxy helpers.
147
+ - **Custom workspace demo**: a `customComponent` receives live MediaSFU helpers so you can build dashboards, CRM surfaces, or any bespoke host interface.
148
+ - **Debug panel & helpers**: optional JSON panel exposes the `updateSourceParameters` payload so you can see exactly what to wire into your own components.
149
+
150
+ #### Try it quickly
151
+
152
+ ```typescript
153
+ @Component({
154
+ selector: 'app-custom-ui-playbook',
155
+ template: `
156
+ <ng-container [ngSwitch]="selectedExperience">
157
+ <app-mediasfu-generic *ngSwitchCase="'generic'"
158
+ [credentials]="currentPreset.credentials"
159
+ [localLink]="currentPreset.localLink"
160
+ [connectMediaSFU]="currentPreset.connectMediaSFU"
161
+ [returnUI]="showPrebuiltUI"
162
+ [uiOverrides]="overrides"
163
+ [customStyles]="containerStyles">
164
+ </app-mediasfu-generic>
165
+
166
+ <app-mediasfu-broadcast *ngSwitchCase="'broadcast'"
167
+ [credentials]="currentPreset.credentials"
168
+ [localLink]="currentPreset.localLink"
169
+ [connectMediaSFU]="currentPreset.connectMediaSFU"
170
+ [returnUI]="showPrebuiltUI"
171
+ [uiOverrides]="overrides">
172
+ </app-mediasfu-broadcast>
173
+
174
+ <!-- Similar for webinar, conference, chat -->
175
+ </ng-container>
176
+ `
177
+ })
178
+ export class CustomUIPlaybookComponent {
179
+ connectionScenario: 'cloud' | 'hybrid' | 'ce' = 'cloud';
180
+ selectedExperience: 'generic' | 'broadcast' | 'webinar' | 'conference' | 'chat' = 'generic';
181
+ showPrebuiltUI = true;
182
+ enableFullCustomUI = false;
183
+
184
+ connectionPresets = {
185
+ cloud: {
186
+ credentials: { apiUserName: 'demo', apiKey: 'demo' },
187
+ localLink: '',
188
+ connectMediaSFU: true
189
+ },
190
+ hybrid: {
191
+ credentials: { apiUserName: 'demo', apiKey: 'demo' },
192
+ localLink: 'http://localhost:3000',
193
+ connectMediaSFU: true
194
+ },
195
+ ce: {
196
+ credentials: undefined,
197
+ localLink: 'http://localhost:3000',
198
+ connectMediaSFU: false
199
+ },
200
+ };
201
+
202
+ get currentPreset() {
203
+ return this.connectionPresets[this.connectionScenario];
204
+ }
205
+
206
+ overrides: MediasfuUICustomOverrides = {
207
+ mainContainer: this.enableFullCustomUI ? {
208
+ component: CustomMainContainerComponent
209
+ } : undefined,
210
+ };
211
+
212
+ containerStyles = {
213
+ background: 'linear-gradient(135deg, #0f172a, #1e3a8a)',
214
+ minHeight: '100vh'
215
+ };
216
+ }
217
+ ```
218
+
219
+ Toggle the configuration values at the top of the playbook and watch the UI reconfigure instantly. It's the fastest path to understand MediaSFU's override surface before you fold the patterns into your production entrypoint.
220
+
221
+ #### Passing custom props and UI overrides
222
+
223
+ Use the same playbook to validate bespoke cards, override bundles, and fully custom workspaces before you move them into production code:
224
+
225
+ ```typescript
226
+ @Component({
227
+ selector: 'app-advanced-playbook',
228
+ template: `
229
+ <app-mediasfu-generic
230
+ [credentials]="credentials"
231
+ [customVideoCard]="videoCard"
232
+ [customAudioCard]="audioCard"
233
+ [customMiniCard]="miniCard"
234
+ [customComponent]="enableFullCustomUI ? customWorkspace : undefined"
235
+ [customStyles]="containerStyles"
236
+ [uiOverrides]="uiOverrides">
237
+ </app-mediasfu-generic>
238
+ `
239
+ })
240
+ export class AdvancedPlaybookComponent {
241
+ credentials = { apiUserName: 'demo', apiKey: 'demo' };
242
+ enableFullCustomUI = false;
243
+
244
+ // Custom card components with themed styling
245
+ videoCard = VideoCardComponent; // Pass component class
246
+ audioCard = AudioCardComponent;
247
+ miniCard = MiniCardComponent;
248
+ customWorkspace = CustomWorkspaceComponent;
249
+
250
+ containerStyles = {
251
+ background: '#0f172a',
252
+ borderRadius: '32px',
253
+ overflow: 'hidden'
254
+ };
255
+
256
+ uiOverrides: MediasfuUICustomOverrides = {
257
+ mainContainer: {
258
+ component: CustomMainContainerComponent
259
+ },
260
+ menuModal: {
261
+ component: CustomMenuModalComponent
262
+ },
263
+ consumerResume: {
264
+ wrap: (original) => async (params) => {
265
+ const startedAt = performance.now();
266
+ const result = await original(params);
267
+ console.log('consumer_resume', {
268
+ durationMs: performance.now() - startedAt,
269
+ consumerId: params?.consumer?.id,
270
+ });
271
+ return result;
272
+ },
273
+ },
274
+ };
275
+ }
276
+ ```
277
+
278
+ Because the playbook surfaces `updateSourceParameters`, you can also log or snapshot the helper bundle (`getParticipantMedia`, `toggleMenuModal`, `showAlert`, and more) to ensure your custom UI always receives the hooks it expects.
279
+
280
+ ### `uiOverrides` input — override keys at a glance
281
+
282
+ Each key accepts a `CustomComponentOverride<Props>` or `CustomFunctionOverride<Fn>` object with optional `component` and `wrap` fields. You can fully replace the default implementation or wrap it while forwarding props.
283
+
284
+ #### Layout & control surfaces
285
+
286
+ | Key | Default component | Typical use |
287
+ | --- | --- | --- |
288
+ | `mainContainer` | `MainContainerComponent` | Inject theming providers or dashboard layouts. |
289
+ | `mainAspect` | `MainAspectComponent` | Tune how the main region splits space. |
290
+ | `mainScreen` | `MainScreenComponent` | Orchestrate hero video + gallery interplay. |
291
+ | `mainGrid` | `MainGridComponent` | Modify layout or layering of primary participants. |
292
+ | `subAspect` | `SubAspectComponent` | Restyle fixed control strips in webinar/conference modes. |
293
+ | `otherGrid` | `OtherGridComponent` | Change presentation of off-stage attendees. |
294
+ | `flexibleGrid`, `flexibleGridAlt` | `FlexibleGrid` | Implement AI-driven or branded array layouts. |
295
+ | `flexibleVideo` | `FlexibleVideo` | Add captions, watermarks, or overlays to highlighted speakers. |
296
+ | `audioGrid` | `AudioGrid` | Customise audio-only attendee presentation. |
297
+ | `pagination` | `Pagination` | Introduce infinite scroll or auto-cycling carousels. |
298
+ | `controlButtons` | `ControlButtonsComponent` | Rebrand the primary action bar. |
299
+ | `controlButtonsAlt` | `ControlButtonsAltComponent` | Control secondary button clusters. |
300
+ | `controlButtonsTouch` | `ControlButtonsComponentTouch` | Deliver mobile-first controls (used heavily by `MediasfuChat`). |
301
+
302
+ #### Participant cards & widgets
303
+
304
+ | Key | Default component | Typical use |
305
+ | --- | --- | --- |
306
+ | `videoCard` | `VideoCard` | Add host badges, reactions, or CRM overlays. |
307
+ | `audioCard` | `AudioCard` | Swap avatars or expose spoken-language info. |
308
+ | `miniCard` | `MiniCard` | Customize thumbnails in picture-in-picture modes. |
309
+ | `miniAudio` | `MiniAudio` | Re-style the audio-only mini indicators. |
310
+ | `meetingProgressTimer` | `MeetingProgressTimer` | Replace the elapsed-time widget with countdowns or milestones. |
311
+ | `miniAudioPlayer` | `MiniAudioPlayer` | Provide alternative UI for recorded clip playback. |
312
+
313
+ #### Modals, dialogs, and collaboration surfaces
314
+
315
+ | Key | Default component | Typical use |
316
+ | --- | --- | --- |
317
+ | `loadingModal` | `LoadingModal` | Show branded skeletons while connecting. |
318
+ | `alert` | `AlertComponent` | Route alerts through your notification system. |
319
+ | `menuModal` | `MenuModal` | Redesign quick-action trays. |
320
+ | `eventSettingsModal` | `EventSettingsModal` | Extend host tools with your own settings. |
321
+ | `requestsModal` | `RequestsModal` | Build moderation queues tailored to your workflows. |
322
+ | `waitingRoomModal` | `WaitingRoomModal` | Deliver custom waiting-room experiences. |
323
+ | `coHostModal` | `CoHostModal` | Manage co-hosts with bespoke UX. |
324
+ | `mediaSettingsModal` | `MediaSettingsModal` | Embed device tests or instructions. |
325
+ | `participantsModal` | `ParticipantsModal` | Introduce advanced filters, search, or notes. |
326
+ | `messagesModal` | `MessagesModal` | Drop in your full-featured chat module. |
327
+ | `displaySettingsModal` | `DisplaySettingsModal` | Let users pick layouts, themes, or captions. |
328
+ | `confirmExitModal` | `ConfirmExitModal` | Meet compliance wording requirements. |
329
+ | `confirmHereModal` | `ConfirmHereModal` | Customize attendance confirmations for webinars. |
330
+ | `shareEventModal` | `ShareEventModal` | Add referral codes or QR sharing. |
331
+ | `recordingModal` | `RecordingModal` | Tailor recording confirmation flows. |
332
+ | `pollModal` | `PollModal` | Integrate your polling/quiz engine. |
333
+ | `backgroundModal` | `BackgroundModal` | Hook AI background replacement or brand presets. |
334
+ | `breakoutRoomsModal` | `BreakoutRoomsModal` | Implement drag-and-drop or AI room suggestions. |
335
+ | `configureWhiteboardModal` | `ConfigureWhiteboardModal` | Adjust collaboration permissions before launch. |
336
+ | `whiteboard` | `Whiteboard` | Replace with your whiteboard provider. |
337
+ | `screenboard` | `Screenboard` | Modify shared-screen annotation layers. |
338
+ | `screenboardModal` | `ScreenboardModal` | Reimagine how users enable shared annotations. |
339
+
340
+ #### Entry flows & custom renderers
341
+
342
+ | Key | Default component | Typical use |
343
+ | --- | --- | --- |
344
+ | `welcomePage` | `WelcomePage` | Provide a fully branded welcome/marketing splash. |
345
+ | `preJoinPage` | `PrejoinPage` | Override the wizard used before joining live sessions. |
346
+ | `customMenuButtonsRenderer` | `ControlButtonsAltComponent` | Supply a bespoke renderer for menu button groups without overriding each button. |
347
+
348
+ #### Function overrides
349
+
350
+ | Key | Default function | Typical use |
351
+ | --- | --- | --- |
352
+ | `consumerResume` | `consumerResume` | Wrap errors, capture analytics, or rate-limit consumer resume behavior. |
353
+ | `addVideosGrid` | `addVideosGrid` | Replace participant ordering or layout heuristics on the fly. |
354
+ | `prepopulateUserMedia` | `prepopulateUserMedia` | Customize initial media setup with custom video/audio/mini cards. |
355
+
356
+ > Function overrides support `{ implementation, wrap }`. Provide `implementation` for a full replacement, or `wrap` to intercept the default behavior before/after it runs.
357
+
358
+ ### Example: swap the chat modal and theme the controls
359
+
360
+ ```typescript
361
+ import { Component } from '@angular/core';
362
+ import { MediasfuGeneric } from 'mediasfu-angular';
363
+ import { MyChatModalComponent } from './ui/my-chat-modal.component';
364
+ import { MyControlsComponent } from './ui/my-controls.component';
365
+
366
+ @Component({
367
+ selector: 'app-my-meeting',
368
+ template: `
369
+ <app-mediasfu-generic
370
+ [credentials]="credentials"
371
+ [uiOverrides]="uiOverrides">
372
+ </app-mediasfu-generic>
373
+ `
374
+ })
375
+ export class MyMeetingComponent {
376
+ credentials = { apiUserName: 'your-api-user', apiKey: 'your-api-key' };
377
+
378
+ uiOverrides = {
379
+ messagesModal: {
380
+ component: MyChatModalComponent,
381
+ },
382
+ controlButtons: {
383
+ component: MyControlsComponent,
384
+ },
385
+ };
386
+ }
387
+ ```
388
+
389
+ ### Example: wrap a MediaSFU helper instead of replacing it
390
+
391
+ ```typescript
392
+ import { Component } from '@angular/core';
393
+ import { MediasfuConference } from 'mediasfu-angular';
394
+
395
+ @Component({
396
+ selector: 'app-analytics-meeting',
397
+ template: `
398
+ <app-mediasfu-conference
399
+ [credentials]="credentials"
400
+ [uiOverrides]="uiOverrides">
401
+ </app-mediasfu-conference>
402
+ `
403
+ })
404
+ export class AnalyticsMeetingComponent {
405
+ credentials = { apiUserName: 'your-api-user', apiKey: 'your-api-key' };
406
+
407
+ uiOverrides = {
408
+ consumerResume: {
409
+ wrap: (original) => async (params) => {
410
+ const startedAt = performance.now();
411
+ const result = await original(params);
412
+
413
+ // Send analytics
414
+ this.analytics.track('consumer_resume', {
415
+ durationMs: performance.now() - startedAt,
416
+ consumerId: params?.consumer?.id,
417
+ });
418
+
419
+ return result;
420
+ },
421
+ },
422
+ };
423
+
424
+ constructor(private analytics: AnalyticsService) {}
425
+ }
426
+ ```
427
+
428
+ ---
429
+
76
430
  # Features <a name="features"></a>
77
431
 
78
432
  MediaSFU's Angular SDK comes with a host of powerful features out of the box:
@@ -88,6 +442,74 @@ MediaSFU's Angular SDK comes with a host of powerful features out of the box:
88
442
  9. **Cloud Recording (track-based)**: Customize recordings with track-based options, including watermarks, name tags, background colors, and more.
89
443
  10. **Managed Events**: Manage events with features to handle abandoned and inactive participants, as well as enforce time and capacity limits.
90
444
 
445
+ ## 🆕 **New Advanced Media Access**
446
+
447
+ The Angular SDK now includes powerful utility methods for fine-grained control over media devices and participant streams:
448
+
449
+ ### **`getMediaDevicesList`** - Device Enumeration
450
+
451
+ Enumerate available cameras and microphones with automatic permission handling:
452
+
453
+ ```typescript
454
+ // Get all available cameras
455
+ const cameras = await sourceParameters.getMediaDevicesList('videoinput');
456
+ cameras.forEach(camera => {
457
+ console.log(`Camera: ${camera.label} (${camera.deviceId})`);
458
+ });
459
+
460
+ // Get all available microphones
461
+ const microphones = await sourceParameters.getMediaDevicesList('audioinput');
462
+ microphones.forEach(mic => {
463
+ console.log(`Microphone: ${mic.label} (${mic.deviceId})`);
464
+ });
465
+ ```
466
+
467
+ **Use Cases:**
468
+ - Build custom device selection interfaces
469
+ - Detect available media hardware
470
+ - Switch between multiple cameras/microphones
471
+ - Pre-flight device checks before joining
472
+
473
+ [See full documentation and examples →](#media-device-and-stream-utility-methods)
474
+
475
+ ---
476
+
477
+ ### **`getParticipantMedia`** - Stream Access
478
+
479
+ Retrieve specific participant's video or audio streams by ID or name:
480
+
481
+ ```typescript
482
+ // Get participant video stream by producer ID
483
+ const videoStream = await sourceParameters.getParticipantMedia({
484
+ id: 'producer-123',
485
+ kind: 'video'
486
+ });
487
+
488
+ // Get participant audio stream by name
489
+ const audioStream = await sourceParameters.getParticipantMedia({
490
+ name: 'John Doe',
491
+ kind: 'audio'
492
+ });
493
+
494
+ // Use the stream (e.g., attach to video element)
495
+ if (videoStream) {
496
+ videoElement.srcObject = videoStream;
497
+ }
498
+ ```
499
+
500
+ **Use Cases:**
501
+ - Monitor specific participant streams
502
+ - Create custom video layouts with individual control
503
+ - Build stream recording features
504
+ - Implement advanced audio/video processing
505
+ - Create picture-in-picture views for specific users
506
+
507
+ [See full documentation and examples →](#media-device-and-stream-utility-methods)
508
+
509
+ ---
510
+
511
+ 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.
512
+
91
513
  # Getting Started <a name="getting-started"></a>
92
514
 
93
515
  This section will guide users through the initial setup and installation of the npm module.
@@ -180,153 +602,1475 @@ If you plan to self-host MediaSFU or use it without MediaSFU Cloud services, you
180
602
  This setup allows full flexibility and customization while bypassing the need for cloud-dependent credentials.
181
603
 
182
604
 
183
- # Basic Usage Guide <a name="basic-usage-guide"></a>
605
+ # 📘 Angular SDK Guide <a name="angular-sdk-guide"></a>
606
+
607
+ 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.
608
+
609
+ ---
610
+
611
+ ## Quick Start (5 Minutes) <a name="quick-start-5-minutes"></a>
612
+
613
+ Get your first MediaSFU app running in just a few minutes.
614
+
615
+ ### Step 1: Install the Package
616
+
617
+ ```bash
618
+ npm install mediasfu-angular
619
+ ```
620
+
621
+ ### Step 2: Import and Use
622
+
623
+ ```typescript
624
+ // app.component.ts
625
+ import { Component } from '@angular/core';
626
+ import { MediasfuGeneric } from 'mediasfu-angular';
627
+
628
+ @Component({
629
+ selector: 'app-root',
630
+ standalone: true,
631
+ imports: [MediasfuGeneric],
632
+ template: `<app-mediasfu-generic></app-mediasfu-generic>`,
633
+ })
634
+ export class AppComponent { }
635
+ ```
636
+
637
+ **Alternative with Credentials:**
638
+
639
+ ```typescript
640
+ import { Component } from '@angular/core';
641
+ import { MediasfuGeneric, PreJoinPage } from 'mediasfu-angular';
642
+
643
+ @Component({
644
+ selector: 'app-root',
645
+ standalone: true,
646
+ imports: [MediasfuGeneric],
647
+ template: `
648
+ <app-mediasfu-generic
649
+ [PrejoinPage]="PreJoinPage"
650
+ [credentials]="credentials">
651
+ </app-mediasfu-generic>
652
+ `,
653
+ })
654
+ export class AppComponent {
655
+ PreJoinPage = PreJoinPage;
656
+ credentials = {
657
+ apiUserName: 'your_username',
658
+ apiKey: 'your_api_key',
659
+ };
660
+ }
661
+ ```
662
+
663
+ ### Step 3: Run Your App
664
+
665
+ ```bash
666
+ ng serve
667
+ ```
668
+
669
+ **That's it!** You now have a fully functional video conferencing app with:
670
+ - ✅ Video and audio streaming
671
+ - ✅ Screen sharing
672
+ - ✅ Chat messaging
673
+ - ✅ Participant management
674
+ - ✅ Recording capabilities
675
+ - ✅ Breakout rooms
676
+ - ✅ Polls and whiteboards
677
+
678
+ ---
679
+
680
+ ## Understanding MediaSFU Architecture <a name="understanding-mediasfu-architecture"></a>
681
+
682
+ Before diving deeper, let's understand how MediaSFU is structured.
683
+
684
+ ### The Three-Layer Architecture
685
+
686
+ ```
687
+ ┌─────────────────────────────────────────────┐
688
+ │ Your Angular Application │
689
+ │ (components, services, business logic) │
690
+ └─────────────────────────────────────────────┘
691
+
692
+ ┌─────────────────────────────────────────────┐
693
+ │ MediaSFU Components Layer │
694
+ │ (MediasfuGeneric, MediasfuBroadcast, etc.) │
695
+ │ - Pre-built UI components │
696
+ │ - Event handling │
697
+ │ - State management (RxJS) │
698
+ └─────────────────────────────────────────────┘
699
+
700
+ ┌─────────────────────────────────────────────┐
701
+ │ MediaSFU Core Methods Layer │
702
+ │ (Stream control, room management, │
703
+ │ WebRTC handling, socket communication) │
704
+ └─────────────────────────────────────────────┘
705
+
706
+ ┌─────────────────────────────────────────────┐
707
+ │ MediaSFU Backend Services │
708
+ │ (MediaSFU Cloud or Community Edition) │
709
+ └─────────────────────────────────────────────┘
710
+ ```
711
+
712
+ ### Key Concepts
713
+
714
+ #### 1. **Event Room Types**
715
+
716
+ MediaSFU provides 5 specialized room types, each optimized for specific use cases:
717
+
718
+ | Room Type | Best For | Key Features |
719
+ |-----------|----------|--------------|
720
+ | **MediasfuGeneric** | General purpose meetings | Flexible layout, all features enabled |
721
+ | **MediasfuBroadcast** | Live streaming events | Optimized for one-to-many communication |
722
+ | **MediasfuWebinar** | Educational sessions | Presenter focus, Q&A features |
723
+ | **MediasfuConference** | Business meetings | Equal participant layout, collaboration tools |
724
+ | **MediasfuChat** | Interactive discussions | Chat-first interface, quick connections |
725
+
726
+ ```typescript
727
+ // Choose the right room type for your use case
728
+ import {
729
+ MediasfuWebinar,
730
+ MediasfuBroadcast,
731
+ MediasfuConference
732
+ } from 'mediasfu-angular';
733
+
734
+ @Component({
735
+ // For a webinar
736
+ template: `<app-mediasfu-webinar [credentials]="credentials"></app-mediasfu-webinar>`,
737
+ // For a broadcast
738
+ // template: `<app-mediasfu-broadcast [credentials]="credentials"></app-mediasfu-broadcast>`,
739
+ // For a conference
740
+ // template: `<app-mediasfu-conference [credentials]="credentials"></app-mediasfu-conference>`,
741
+ })
742
+ ```
743
+
744
+ #### 2. **The Three Usage Modes**
745
+
746
+ MediaSFU offers three progressive levels of customization:
747
+
748
+ ##### Mode 1: Default UI (Simplest)
749
+ Use MediaSFU's complete pre-built interface - perfect for rapid development.
750
+
751
+ ```typescript
752
+ import { Component } from '@angular/core';
753
+ import { MediasfuGeneric } from 'mediasfu-angular';
754
+
755
+ @Component({
756
+ selector: 'app-root',
757
+ standalone: true,
758
+ imports: [MediasfuGeneric],
759
+ template: `<app-mediasfu-generic [credentials]="credentials"></app-mediasfu-generic>`,
760
+ })
761
+ export class AppComponent {
762
+ credentials = { apiUserName: 'username', apiKey: 'key' };
763
+ }
764
+ ```
765
+
766
+ **When to use:**
767
+ - ✅ Prototyping or MVP development
768
+ - ✅ Need a production-ready UI quickly
769
+ - ✅ Standard video conferencing features are sufficient
770
+
771
+ ##### Mode 2: Custom UI with MediaSFU Backend (Most Flexible)
772
+ Build your own UI while using MediaSFU's powerful backend infrastructure.
773
+
774
+ ```typescript
775
+ import { Component, OnInit } from '@angular/core';
776
+ import { MediasfuGeneric } from 'mediasfu-angular';
777
+
778
+ @Component({
779
+ selector: 'app-root',
780
+ standalone: true,
781
+ imports: [MediasfuGeneric, CommonModule],
782
+ template: `
783
+ <app-mediasfu-generic
784
+ [returnUI]="false"
785
+ [sourceParameters]="sourceParameters"
786
+ [updateSourceParameters]="updateSourceParameters.bind(this)"
787
+ [credentials]="credentials"
788
+ [noUIPreJoinOptions]="preJoinOptions">
789
+ </app-mediasfu-generic>
790
+
791
+ <!-- Your custom UI -->
792
+ @if (sourceParameters) {
793
+ <div class="custom-controls">
794
+ <button (click)="toggleVideo()">
795
+ {{ sourceParameters.videoAlreadyOn ? 'Stop Video' : 'Start Video' }}
796
+ </button>
797
+ <button (click)="toggleAudio()">
798
+ {{ sourceParameters.audioAlreadyOn ? 'Mute' : 'Unmute' }}
799
+ </button>
800
+ <button (click)="toggleScreenShare()">
801
+ {{ sourceParameters.screenAlreadyOn ? 'Stop Sharing' : 'Share Screen' }}
802
+ </button>
803
+ </div>
804
+ }
805
+ `,
806
+ })
807
+ export class AppComponent implements OnInit {
808
+ sourceParameters: any = null;
809
+ credentials = { apiUserName: 'username', apiKey: 'key' };
810
+ preJoinOptions = {
811
+ action: 'create',
812
+ userName: 'Your Name',
813
+ capacity: 50,
814
+ duration: 30,
815
+ eventType: 'conference'
816
+ };
817
+
818
+ updateSourceParameters(params: any) {
819
+ this.sourceParameters = params;
820
+ }
821
+
822
+ toggleVideo() {
823
+ this.sourceParameters?.clickVideo({ parameters: this.sourceParameters });
824
+ }
825
+
826
+ toggleAudio() {
827
+ this.sourceParameters?.clickAudio({ parameters: this.sourceParameters });
828
+ }
829
+
830
+ toggleScreenShare() {
831
+ this.sourceParameters?.clickScreenShare({ parameters: this.sourceParameters });
832
+ }
833
+ }
834
+ ```
835
+
836
+ **When to use:**
837
+ - ✅ Need complete control over UI/UX
838
+ - ✅ Building a custom branded experience
839
+ - ✅ Integrating into existing app design
840
+
841
+ ##### Mode 3: Component Replacement (Balanced)
842
+ Replace specific MediaSFU components while keeping the rest of the infrastructure.
843
+
844
+ ```typescript
845
+ import { Component } from '@angular/core';
846
+ import {
847
+ MediasfuGeneric,
848
+ FlexibleVideo,
849
+ FlexibleGrid
850
+ } from 'mediasfu-angular';
851
+
852
+ @Component({
853
+ selector: 'app-custom-main',
854
+ standalone: true,
855
+ imports: [FlexibleVideo, FlexibleGrid, CommonModule],
856
+ template: `
857
+ <div class="custom-layout">
858
+ <!-- Custom header -->
859
+ <div class="custom-header">
860
+ <h1>{{ parameters.roomName }}</h1>
861
+ <span>{{ parameters.participants.length }} participants</span>
862
+ </div>
863
+
864
+ <!-- Use MediaSFU's components in your layout -->
865
+ <app-flexible-video
866
+ [customWidth]="windowWidth"
867
+ [customHeight]="600"
868
+ [parameters]="parameters">
869
+ </app-flexible-video>
870
+
871
+ <app-flexible-grid
872
+ [customWidth]="windowWidth"
873
+ [customHeight]="400"
874
+ [parameters]="parameters">
875
+ </app-flexible-grid>
876
+
877
+ <!-- Custom footer -->
878
+ <div class="custom-footer">
879
+ <button (click)="toggleVideo()">
880
+ {{ parameters.videoAlreadyOn ? 'Stop Video' : 'Start Video' }}
881
+ </button>
882
+ </div>
883
+ </div>
884
+ `,
885
+ styles: [`
886
+ .custom-layout {
887
+ display: flex;
888
+ flex-direction: column;
889
+ height: 100vh;
890
+ }
891
+ .custom-header {
892
+ padding: 20px;
893
+ background: #1976d2;
894
+ color: white;
895
+ }
896
+ `]
897
+ })
898
+ export class CustomMainComponent {
899
+ parameters: any;
900
+ windowWidth = window.innerWidth;
901
+
902
+ toggleVideo() {
903
+ this.parameters?.clickVideo({ parameters: this.parameters });
904
+ }
905
+ }
906
+
907
+ @Component({
908
+ selector: 'app-root',
909
+ standalone: true,
910
+ imports: [MediasfuGeneric],
911
+ template: `
912
+ <app-mediasfu-generic
913
+ [credentials]="credentials"
914
+ [PrejoinPage]="PreJoinPage"
915
+ [customComponent]="CustomMainComponent">
916
+ </app-mediasfu-generic>
917
+ `,
918
+ })
919
+ export class AppComponent {
920
+ PreJoinPage = PreJoinPage;
921
+ CustomMainComponent = CustomMainComponent;
922
+ credentials = { apiUserName: 'username', apiKey: 'key' };
923
+ }
924
+ ```
925
+
926
+ **When to use:**
927
+ - ✅ Need custom main interface but want to keep MediaSFU's components
928
+ - ✅ Partial customization with minimal effort
929
+ - ✅ Want to maintain MediaSFU's functionality while customizing layout
930
+
931
+ #### 3. **Parameters: Your Control Center**
932
+
933
+ 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:
934
+
935
+ ```typescript
936
+ // Available in sourceParameters or parameters object
937
+ {
938
+ // Media Controls (Methods)
939
+ clickVideo: (options) => {},
940
+ clickAudio: (options) => {},
941
+ clickScreenShare: (options) => {},
942
+
943
+ // Room State (BehaviorSubject values)
944
+ roomName: 'meeting-123',
945
+ participants: [...],
946
+ allVideoStreams: [...],
947
+ allAudioStreams: [...],
948
+
949
+ // UI State (BehaviorSubject values)
950
+ videoAlreadyOn: false,
951
+ audioAlreadyOn: false,
952
+ screenAlreadyOn: false,
953
+
954
+ // Update Functions (BehaviorSubject next())
955
+ updateVideoAlreadyOn: (value) => {},
956
+ updateAudioAlreadyOn: (value) => {},
957
+
958
+ // And 200+ more properties and methods...
959
+ }
960
+ ```
961
+
962
+ **Access patterns:**
963
+
964
+ ```typescript
965
+ // In Mode 1 (Default UI): Parameters are managed internally
966
+ // You don't need to access them directly
967
+
968
+ // In Mode 2 (Custom UI): Access via sourceParameters
969
+ sourceParameters?.clickVideo({ parameters: sourceParameters });
970
+
971
+ // In Mode 3 (Component Replacement): Passed to your custom component
972
+ @Component({
973
+ template: `<button (click)="toggleVideo()">Toggle</button>`
974
+ })
975
+ export class CustomComponent {
976
+ @Input() parameters: any;
977
+
978
+ toggleVideo() {
979
+ this.parameters.clickVideo({ parameters: this.parameters });
980
+ }
981
+ }
982
+
983
+ // Subscribing to reactive state changes
984
+ sourceParameters.participants.subscribe((participants) => {
985
+ console.log('Participants updated:', participants);
986
+ });
987
+ ```
988
+
989
+ ---
990
+
991
+ ## Core Concepts & Components <a name="core-concepts--components"></a>
992
+
993
+ Now that you understand the architecture, let's explore the building blocks.
994
+
995
+ ### 1. Display Components: Building Your Video Layout
996
+
997
+ MediaSFU provides powerful components for organizing and displaying media streams.
998
+
999
+ #### Primary Layout Components
1000
+
1001
+ **FlexibleVideo** - Main video display area
1002
+
1003
+ ```typescript
1004
+ import { FlexibleVideo } from 'mediasfu-angular';
1005
+
1006
+ @Component({
1007
+ template: `
1008
+ <app-flexible-video
1009
+ [customWidth]="windowWidth"
1010
+ [customHeight]="600"
1011
+ [parameters]="parameters">
1012
+ </app-flexible-video>
1013
+ `
1014
+ })
1015
+ ```
1016
+ - Automatically handles main presenter or screen share
1017
+ - Smooth transitions between different video sources
1018
+ - Responsive sizing
1019
+
1020
+ **FlexibleGrid** - Participant grid layout
1021
+
1022
+ ```typescript
1023
+ import { FlexibleGrid } from 'mediasfu-angular';
1024
+
1025
+ @Component({
1026
+ template: `
1027
+ <app-flexible-grid
1028
+ [customWidth]="windowWidth"
1029
+ [customHeight]="800"
1030
+ [parameters]="parameters">
1031
+ </app-flexible-grid>
1032
+ `
1033
+ })
1034
+ ```
1035
+ - Intelligent grid sizing (2x2, 3x3, 4x4, etc.)
1036
+ - Pagination for large participant lists
1037
+ - Automatic reflow on window resize
1038
+
1039
+ **AudioGrid** - Audio-only participants
1040
+
1041
+ ```typescript
1042
+ import { AudioGrid } from 'mediasfu-angular';
1043
+
1044
+ @Component({
1045
+ template: `<app-audio-grid [parameters]="parameters"></app-audio-grid>`
1046
+ })
1047
+ ```
1048
+ - Displays participants without video
1049
+ - Audio level indicators
1050
+ - Compact layout for efficiency
1051
+
1052
+ #### Container Components
1053
+
1054
+ | Component | Purpose | Use Case |
1055
+ |-----------|---------|----------|
1056
+ | **MainContainerComponent** | Primary content wrapper | Wraps all main content areas |
1057
+ | **MainAspectComponent** | Aspect ratio container | Maintains proper video proportions |
1058
+ | **MainScreenComponent** | Screen layout manager | Organizes screen regions |
1059
+ | **SubAspectComponent** | Secondary content container | For picture-in-picture, sidebars |
1060
+
1061
+ **Example: Building a custom layout**
1062
+
1063
+ ```typescript
1064
+ import { Component } from '@angular/core';
1065
+ import {
1066
+ MainContainerComponent,
1067
+ FlexibleVideo,
1068
+ FlexibleGrid,
1069
+ AudioGrid
1070
+ } from 'mediasfu-angular';
1071
+
1072
+ @Component({
1073
+ selector: 'app-custom-layout',
1074
+ standalone: true,
1075
+ imports: [
1076
+ MainContainerComponent,
1077
+ FlexibleVideo,
1078
+ FlexibleGrid,
1079
+ AudioGrid,
1080
+ CommonModule
1081
+ ],
1082
+ template: `
1083
+ <app-main-container-component>
1084
+ <div class="layout-container">
1085
+ <!-- Main video area -->
1086
+ <div class="main-video">
1087
+ <app-flexible-video
1088
+ [customWidth]="windowWidth"
1089
+ [customHeight]="windowHeight * 0.6"
1090
+ [parameters]="parameters">
1091
+ </app-flexible-video>
1092
+ </div>
1093
+
1094
+ <!-- Participant grid -->
1095
+ <div class="participant-grid">
1096
+ <app-flexible-grid
1097
+ [customWidth]="windowWidth"
1098
+ [customHeight]="windowHeight * 0.3"
1099
+ [parameters]="parameters">
1100
+ </app-flexible-grid>
1101
+ </div>
1102
+
1103
+ <!-- Audio-only participants -->
1104
+ <div class="audio-participants">
1105
+ <app-audio-grid [parameters]="parameters"></app-audio-grid>
1106
+ </div>
1107
+ </div>
1108
+ </app-main-container-component>
1109
+ `,
1110
+ styles: [`
1111
+ .layout-container {
1112
+ display: flex;
1113
+ flex-direction: column;
1114
+ height: 100vh;
1115
+ }
1116
+ .main-video { flex: 3; }
1117
+ .participant-grid { flex: 2; }
1118
+ .audio-participants { height: 80px; }
1119
+ `]
1120
+ })
1121
+ export class CustomLayoutComponent {
1122
+ @Input() parameters: any;
1123
+ windowWidth = window.innerWidth;
1124
+ windowHeight = window.innerHeight;
1125
+ }
1126
+ ```
1127
+
1128
+ ### 2. Control Components: User Interactions
1129
+
1130
+ **ControlButtonsComponent** - Standard control bar
1131
+
1132
+ ```typescript
1133
+ import { ControlButtonsComponent } from 'mediasfu-angular';
1134
+
1135
+ @Component({
1136
+ template: `
1137
+ <app-control-buttons-component
1138
+ [parameters]="parameters"
1139
+ [position]="'bottom'">
1140
+ </app-control-buttons-component>
1141
+ `
1142
+ })
1143
+ ```
1144
+ Includes: mute, video, screenshare, participants, chat, settings, etc.
1145
+
1146
+ **ControlButtonsAltComponent** - Alternative layout
1147
+
1148
+ ```typescript
1149
+ import { ControlButtonsAltComponent } from 'mediasfu-angular';
1150
+
1151
+ @Component({
1152
+ template: `
1153
+ <app-control-buttons-alt-component
1154
+ [parameters]="parameters"
1155
+ [position]="'top'">
1156
+ </app-control-buttons-alt-component>
1157
+ `
1158
+ })
1159
+ ```
1160
+ Different button arrangement optimized for specific layouts.
1161
+
1162
+ **ControlButtonsComponentTouch** - Touch-optimized controls
1163
+
1164
+ ```typescript
1165
+ import { ControlButtonsComponentTouch } from 'mediasfu-angular';
1166
+
1167
+ @Component({
1168
+ template: `
1169
+ <app-control-buttons-component-touch
1170
+ [parameters]="parameters">
1171
+ </app-control-buttons-component-touch>
1172
+ `
1173
+ })
1174
+ ```
1175
+ Floating action buttons optimized for mobile/tablet interfaces.
1176
+
1177
+ ### 3. Modal Components: Feature Interfaces
1178
+
1179
+ MediaSFU includes modals for various features:
1180
+
1181
+ ```typescript
1182
+ import {
1183
+ ParticipantsModal,
1184
+ MessagesModal,
1185
+ SettingsModal,
1186
+ DisplaySettingsModal,
1187
+ RecordingModal,
1188
+ PollModal,
1189
+ BreakoutRoomsModal
1190
+ } from 'mediasfu-angular';
1191
+
1192
+ // These are automatically rendered when enabled
1193
+ // Control their visibility via parameters
1194
+ parameters.updateIsParticipantsModalVisible.next(true);
1195
+ parameters.updateIsMessagesModalVisible.next(true);
1196
+ parameters.updateIsSettingsModalVisible.next(true);
1197
+ ```
1198
+
1199
+ Available modals:
1200
+ - **ParticipantsModal** - Participant list management
1201
+ - **MessagesModal** - Chat interface
1202
+ - **SettingsModal** - Event and room settings
1203
+ - **DisplaySettingsModal** - Layout and display options
1204
+ - **RecordingModal** - Recording controls and settings
1205
+ - **PollModal** - Create and manage polls
1206
+ - **BreakoutRoomsModal** - Breakout room management
1207
+ - **MediaSettingsModal** - Camera/microphone selection
1208
+ - **BackgroundModal** - Virtual background settings
1209
+ - **ConfigureWhiteboardModal** - Whiteboard configuration
1210
+
1211
+ **Example: Programmatically showing modals**
1212
+
1213
+ ```typescript
1214
+ @Component({
1215
+ selector: 'app-custom-toolbar',
1216
+ template: `
1217
+ <div class="custom-toolbar">
1218
+ <button (click)="showParticipants()">
1219
+ Show Participants ({{ participantCount }})
1220
+ </button>
1221
+
1222
+ <button (click)="openChat()">
1223
+ Open Chat
1224
+ </button>
1225
+
1226
+ <button (click)="createPoll()">
1227
+ Create Poll
1228
+ </button>
1229
+ </div>
1230
+ `
1231
+ })
1232
+ export class CustomToolbarComponent {
1233
+ @Input() parameters: any;
1234
+
1235
+ get participantCount() {
1236
+ return this.parameters?.participants?.length || 0;
1237
+ }
1238
+
1239
+ showParticipants() {
1240
+ this.parameters?.updateIsParticipantsModalVisible.next(true);
1241
+ }
1242
+
1243
+ openChat() {
1244
+ this.parameters?.updateIsMessagesModalVisible.next(true);
1245
+ }
1246
+
1247
+ createPoll() {
1248
+ this.parameters?.launchPoll?.launchPoll({ parameters: this.parameters });
1249
+ }
1250
+ }
1251
+ ```
1252
+
1253
+ ### 4. Video Cards: Individual Participant Display
1254
+
1255
+ **VideoCard** - Individual participant video element
1256
+
1257
+ ```typescript
1258
+ import { VideoCard } from 'mediasfu-angular';
1259
+
1260
+ @Component({
1261
+ template: `
1262
+ <app-video-card
1263
+ [videoStream]="participantStream"
1264
+ [remoteProducerId]="'producer-id'"
1265
+ [eventType]="'conference'"
1266
+ [forceFullDisplay]="false"
1267
+ [participant]="participantObject"
1268
+ [backgroundColor]="'#000000'"
1269
+ [showControls]="true"
1270
+ [showInfo]="true"
1271
+ [name]="'Participant Name'"
1272
+ [parameters]="parameters">
1273
+ </app-video-card>
1274
+ `
1275
+ })
1276
+ ```
1277
+
1278
+ **AudioCard** - Individual audio-only participant
1279
+
1280
+ ```typescript
1281
+ import { AudioCard } from 'mediasfu-angular';
1282
+
1283
+ @Component({
1284
+ template: `
1285
+ <app-audio-card
1286
+ [name]="'Participant Name'"
1287
+ [barColor]="'#4CAF50'"
1288
+ [textColor]="'#FFFFFF'"
1289
+ [customStyle]="{ borderRadius: '10px' }"
1290
+ [controlsPosition]="'topLeft'"
1291
+ [infoPosition]="'topRight'"
1292
+ [participant]="participantObject"
1293
+ [parameters]="parameters">
1294
+ </app-audio-card>
1295
+ `
1296
+ })
1297
+ ```
1298
+
1299
+ **MiniCard** - Compact participant display (for grids)
1300
+
1301
+ ```typescript
1302
+ import { MiniCard } from 'mediasfu-angular';
1303
+
1304
+ @Component({
1305
+ template: `
1306
+ <app-mini-card
1307
+ [participant]="participantObject"
1308
+ [showControls]="false"
1309
+ [parameters]="parameters">
1310
+ </app-mini-card>
1311
+ `
1312
+ })
1313
+ ```
1314
+
1315
+ **Example: Custom Video Card**
1316
+
1317
+ ```typescript
1318
+ @Component({
1319
+ selector: 'app-my-custom-video-card',
1320
+ standalone: true,
1321
+ template: `
1322
+ <div class="custom-video-card">
1323
+ <video
1324
+ #videoElement
1325
+ [srcObject]="stream"
1326
+ autoplay
1327
+ [muted]="true"
1328
+ playsinline>
1329
+ </video>
1330
+
1331
+ <div class="participant-info">
1332
+ {{ participant.name }}
1333
+ @if (participant.muted) {
1334
+ <span>🔇</span>
1335
+ }
1336
+ </div>
1337
+ </div>
1338
+ `,
1339
+ styles: [`
1340
+ .custom-video-card {
1341
+ border: 3px solid #00ff88;
1342
+ border-radius: 15px;
1343
+ overflow: hidden;
1344
+ position: relative;
1345
+ }
1346
+ video {
1347
+ width: 100%;
1348
+ height: 100%;
1349
+ object-fit: cover;
1350
+ }
1351
+ .participant-info {
1352
+ position: absolute;
1353
+ bottom: 0;
1354
+ left: 0;
1355
+ right: 0;
1356
+ background: rgba(0, 255, 136, 0.8);
1357
+ color: black;
1358
+ padding: 8px;
1359
+ font-weight: bold;
1360
+ }
1361
+ `]
1362
+ })
1363
+ export class MyCustomVideoCardComponent {
1364
+ @Input() stream!: MediaStream;
1365
+ @Input() participant!: any;
1366
+ @Input() parameters!: any;
1367
+ }
1368
+
1369
+ // Use it in MediasfuGeneric
1370
+ @Component({
1371
+ template: `
1372
+ <app-mediasfu-generic
1373
+ [credentials]="credentials"
1374
+ [customVideoCard]="CustomVideoCard">
1375
+ </app-mediasfu-generic>
1376
+ `
1377
+ })
1378
+ export class AppComponent {
1379
+ CustomVideoCard = MyCustomVideoCardComponent;
1380
+ credentials = { apiUserName: 'username', apiKey: 'key' };
1381
+ }
1382
+ ```
1383
+
1384
+ ---
1385
+
1386
+ ## Working with Methods <a name="working-with-methods"></a>
1387
+
1388
+ MediaSFU provides 200+ methods for controlling every aspect of your real-time communication experience. Let's explore the most important categories.
1389
+
1390
+ ### Media Control Methods
1391
+
1392
+ #### Video Control
1393
+
1394
+ ```typescript
1395
+ // Toggle video on/off
1396
+ parameters.clickVideo({ parameters });
1397
+
1398
+ // Switch camera (front/back on mobile)
1399
+ parameters.switchVideoAlt({ parameters });
1400
+
1401
+ // Switch to specific camera by ID
1402
+ const cameras = await parameters.getMediaDevicesList('videoinput');
1403
+ parameters.switchUserVideo({
1404
+ videoPreference: cameras[1].deviceId,
1405
+ parameters
1406
+ });
1407
+
1408
+ // Get current video state
1409
+ const isVideoOn = parameters.videoAlreadyOn;
1410
+
1411
+ // Subscribe to video state changes (RxJS)
1412
+ parameters.videoAlreadyOn.subscribe((isOn: boolean) => {
1413
+ console.log('Video is now:', isOn ? 'ON' : 'OFF');
1414
+ });
1415
+
1416
+ // Update video state programmatically
1417
+ parameters.updateVideoAlreadyOn.next(true);
1418
+ ```
1419
+
1420
+ #### Audio Control
1421
+
1422
+ ```typescript
1423
+ // Toggle audio on/off
1424
+ parameters.clickAudio({ parameters });
1425
+
1426
+ // Switch microphone
1427
+ const microphones = await parameters.getMediaDevicesList('audioinput');
1428
+ parameters.switchUserAudio({
1429
+ audioPreference: microphones[1].deviceId,
1430
+ parameters
1431
+ });
1432
+
1433
+ // Get current audio state
1434
+ const isAudioOn = parameters.audioAlreadyOn;
1435
+ const hasHostPermission = parameters.micAction; // Host approval status
1436
+
1437
+ // Subscribe to audio state changes
1438
+ parameters.audioAlreadyOn.subscribe((isOn: boolean) => {
1439
+ console.log('Audio is now:', isOn ? 'ON' : 'OFF');
1440
+ });
1441
+
1442
+ // Mute/unmute specific participant (host only)
1443
+ parameters.controlMedia({
1444
+ participantId: 'participant-id',
1445
+ participantName: 'John Doe',
1446
+ type: 'audio',
1447
+ socket: parameters.socket,
1448
+ roomName: parameters.roomName
1449
+ });
1450
+ ```
1451
+
1452
+ #### Screen Sharing
1453
+
1454
+ ```typescript
1455
+ // Start screen sharing
1456
+ parameters.clickScreenShare({ parameters });
1457
+
1458
+ // Stop screen sharing
1459
+ parameters.stopShareScreen({ parameters });
1460
+
1461
+ // Check if screen sharing is available
1462
+ const canShare = await parameters.checkScreenShare({ parameters });
1463
+
1464
+ // Get screen share state
1465
+ const isSharing = parameters.screenAlreadyOn;
1466
+ const shareAudio = parameters.shareScreenStarted; // Sharing with audio
1467
+
1468
+ // Subscribe to screen share state
1469
+ parameters.screenAlreadyOn.subscribe((isSharing: boolean) => {
1470
+ console.log('Screen sharing:', isSharing ? 'ACTIVE' : 'INACTIVE');
1471
+ });
1472
+ ```
1473
+
1474
+ ### Media Device and Stream Utility Methods
1475
+
1476
+ #### Get Available Media Devices
1477
+
1478
+ ```typescript
1479
+ // Get available cameras
1480
+ const cameras = await parameters.getMediaDevicesList('videoinput');
1481
+ cameras.forEach(camera => {
1482
+ console.log(`Camera: ${camera.label} (${camera.deviceId})`);
1483
+ });
1484
+
1485
+ // Get available microphones
1486
+ const microphones = await parameters.getMediaDevicesList('audioinput');
1487
+ microphones.forEach(mic => {
1488
+ console.log(`Microphone: ${mic.label} (${mic.deviceId})`);
1489
+ });
1490
+
1491
+ // Building a device selector UI
1492
+ @Component({
1493
+ selector: 'app-device-selector',
1494
+ template: `
1495
+ <div class="device-selector">
1496
+ <select (change)="onCameraChange($event)">
1497
+ <option value="">Select Camera</option>
1498
+ @for (camera of cameras; track camera.deviceId) {
1499
+ <option [value]="camera.deviceId">
1500
+ {{ camera.label }}
1501
+ </option>
1502
+ }
1503
+ </select>
1504
+
1505
+ <select (change)="onMicrophoneChange($event)">
1506
+ <option value="">Select Microphone</option>
1507
+ @for (mic of microphones; track mic.deviceId) {
1508
+ <option [value]="mic.deviceId">
1509
+ {{ mic.label }}
1510
+ </option>
1511
+ }
1512
+ </select>
1513
+ </div>
1514
+ `
1515
+ })
1516
+ export class DeviceSelectorComponent implements OnInit {
1517
+ @Input() parameters: any;
1518
+ cameras: MediaDeviceInfo[] = [];
1519
+ microphones: MediaDeviceInfo[] = [];
1520
+
1521
+ async ngOnInit() {
1522
+ await this.loadDevices();
1523
+ }
1524
+
1525
+ async loadDevices() {
1526
+ this.cameras = await this.parameters.getMediaDevicesList('videoinput');
1527
+ this.microphones = await this.parameters.getMediaDevicesList('audioinput');
1528
+ }
1529
+
1530
+ onCameraChange(event: any) {
1531
+ this.parameters.switchUserVideo({
1532
+ videoPreference: event.target.value,
1533
+ parameters: this.parameters
1534
+ });
1535
+ }
1536
+
1537
+ onMicrophoneChange(event: any) {
1538
+ this.parameters.switchUserAudio({
1539
+ audioPreference: event.target.value,
1540
+ parameters: this.parameters
1541
+ });
1542
+ }
1543
+ }
1544
+ ```
1545
+
1546
+ #### Get Participant Media Streams
1547
+
1548
+ ```typescript
1549
+ // Get participant video stream by ID
1550
+ const videoStream = await parameters.getParticipantMedia({
1551
+ id: 'producer-123',
1552
+ kind: 'video'
1553
+ });
1554
+
1555
+ // Get participant audio stream by name
1556
+ const audioStream = await parameters.getParticipantMedia({
1557
+ name: 'John Doe',
1558
+ kind: 'audio'
1559
+ });
1560
+
1561
+ // Example: Custom participant stream monitor
1562
+ @Component({
1563
+ selector: 'app-stream-monitor',
1564
+ template: `
1565
+ <div class="stream-monitor">
1566
+ <h3>Participant Streams</h3>
1567
+ @for (participant of participants; track participant.id) {
1568
+ <div class="participant-item">
1569
+ <span>{{ participant.name }}</span>
1570
+ <button (click)="viewStream(participant)">View Stream</button>
1571
+ <button (click)="monitorAudio(participant)">Monitor Audio</button>
1572
+ </div>
1573
+ }
1574
+
1575
+ @if (selectedStream) {
1576
+ <div class="stream-viewer">
1577
+ <video #streamVideo autoplay playsinline></video>
1578
+ </div>
1579
+ }
1580
+ </div>
1581
+ `,
1582
+ styles: [`
1583
+ .stream-monitor { padding: 20px; }
1584
+ .participant-item {
1585
+ margin: 10px 0;
1586
+ display: flex;
1587
+ gap: 10px;
1588
+ align-items: center;
1589
+ }
1590
+ .stream-viewer video {
1591
+ width: 100%;
1592
+ max-width: 640px;
1593
+ border: 2px solid #1976d2;
1594
+ }
1595
+ `]
1596
+ })
1597
+ export class StreamMonitorComponent {
1598
+ @Input() parameters: any;
1599
+ @ViewChild('streamVideo') videoElement!: ElementRef<HTMLVideoElement>;
1600
+
1601
+ selectedStream: MediaStream | null = null;
1602
+
1603
+ get participants() {
1604
+ return this.parameters?.participants || [];
1605
+ }
184
1606
 
185
- A basic guide on how to use the module for common tasks.
1607
+ async viewStream(participant: any) {
1608
+ const stream = await this.parameters.getParticipantMedia({
1609
+ id: participant.videoID,
1610
+ name: participant.name,
1611
+ kind: 'video'
1612
+ });
186
1613
 
187
- This section will guide users through the initial setup and installation of the npm module.
188
- ## Introduction
1614
+ if (stream && this.videoElement) {
1615
+ this.selectedStream = stream;
1616
+ this.videoElement.nativeElement.srcObject = stream;
1617
+ } else {
1618
+ console.log('No video stream found for participant');
1619
+ }
1620
+ }
189
1621
 
190
- 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.
1622
+ async monitorAudio(participant: any) {
1623
+ const stream = await this.parameters.getParticipantMedia({
1624
+ id: participant.audioID,
1625
+ name: participant.name,
1626
+ kind: 'audio'
1627
+ });
191
1628
 
192
- ### Documentation Reference
1629
+ if (stream) {
1630
+ // Create audio context for analysis
1631
+ const audioContext = new AudioContext();
1632
+ const analyser = audioContext.createAnalyser();
1633
+ const source = audioContext.createMediaStreamSource(stream);
1634
+ source.connect(analyser);
1635
+
1636
+ console.log('Monitoring audio for:', participant.name);
1637
+ // Add your audio analysis logic here
1638
+ } else {
1639
+ console.log('No audio stream found for participant');
1640
+ }
1641
+ }
1642
+ }
1643
+ ```
193
1644
 
194
- 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.
1645
+ ### Participant Management Methods
195
1646
 
196
- ## Prebuilt Event Rooms
1647
+ ```typescript
1648
+ // Get all participants
1649
+ const participants = parameters.participants;
1650
+ const participantCount = parameters.participantsCounter;
1651
+
1652
+ // Subscribe to participant changes
1653
+ parameters.participants.subscribe((participants: any[]) => {
1654
+ console.log('Participants updated:', participants);
1655
+ });
1656
+
1657
+ // Filter participants
1658
+ const videoParticipants = participants.filter((p: any) => p.videoOn);
1659
+ const audioOnlyParticipants = participants.filter((p: any) => !p.videoOn);
1660
+ const mutedParticipants = participants.filter((p: any) => p.muted);
1661
+
1662
+ // Find specific participant
1663
+ const participant = participants.find((p: any) => p.name === 'John Doe');
1664
+
1665
+ // Remove participant from room (host only)
1666
+ parameters.disconnectUserInitiate({
1667
+ member: participantId,
1668
+ roomName: parameters.roomName,
1669
+ socket: parameters.socket
1670
+ });
1671
+
1672
+ // Change participant role (host only)
1673
+ parameters.updateParticipant({
1674
+ participantId: 'participant-id',
1675
+ islevel: '2', // '2' = host, '1' = co-host, '0' = participant
1676
+ parameters
1677
+ });
1678
+
1679
+ // Request to unmute participant (sends request)
1680
+ parameters.requestScreenShare({ parameters });
1681
+ ```
197
1682
 
198
- 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:
1683
+ ### Chat & Messaging Methods
199
1684
 
200
- 1. **MediasfuGeneric**: A generic event room suitable for various types of events.
201
- 2. **MediasfuBroadcast**: A room optimized for broadcasting events.
202
- 3. **MediasfuWebinar**: Specifically designed for hosting webinars.
203
- 4. **MediasfuConference**: Ideal for hosting conferences.
204
- 5. **MediasfuChat**: A room tailored for interactive chat sessions.
1685
+ ```typescript
1686
+ // Send a group message
1687
+ parameters.sendMessage({
1688
+ message: 'Hello everyone!',
1689
+ type: 'group',
1690
+ parameters
1691
+ });
1692
+
1693
+ // Send direct message
1694
+ parameters.sendMessage({
1695
+ message: 'Private message',
1696
+ type: 'direct',
1697
+ receivers: ['participant-id'],
1698
+ parameters
1699
+ });
1700
+
1701
+ // Access message history
1702
+ const messages = parameters.messages;
1703
+
1704
+ // Subscribe to new messages (RxJS)
1705
+ parameters.messages.subscribe((messages: any[]) => {
1706
+ console.log('Messages updated:', messages);
1707
+ });
1708
+
1709
+ // Example: Custom chat component
1710
+ @Component({
1711
+ selector: 'app-custom-chat',
1712
+ standalone: true,
1713
+ imports: [CommonModule, FormsModule],
1714
+ template: `
1715
+ <div class="chat-container">
1716
+ <div class="messages">
1717
+ @for (msg of messages; track msg.timestamp) {
1718
+ <div class="message">
1719
+ <strong>{{ msg.sender }}:</strong> {{ msg.message }}
1720
+ </div>
1721
+ }
1722
+ </div>
1723
+ <div class="input-area">
1724
+ <input
1725
+ [(ngModel)]="message"
1726
+ (keyup.enter)="sendMessage()"
1727
+ placeholder="Type a message..."
1728
+ />
1729
+ <button (click)="sendMessage()">Send</button>
1730
+ </div>
1731
+ </div>
1732
+ `,
1733
+ styles: [`
1734
+ .chat-container {
1735
+ display: flex;
1736
+ flex-direction: column;
1737
+ height: 400px;
1738
+ }
1739
+ .messages {
1740
+ flex: 1;
1741
+ overflow-y: auto;
1742
+ padding: 10px;
1743
+ }
1744
+ .message {
1745
+ margin: 5px 0;
1746
+ padding: 8px;
1747
+ background: #f5f5f5;
1748
+ border-radius: 4px;
1749
+ }
1750
+ .input-area {
1751
+ display: flex;
1752
+ gap: 10px;
1753
+ padding: 10px;
1754
+ border-top: 1px solid #ddd;
1755
+ }
1756
+ input {
1757
+ flex: 1;
1758
+ padding: 8px;
1759
+ border: 1px solid #ddd;
1760
+ border-radius: 4px;
1761
+ }
1762
+ button {
1763
+ padding: 8px 16px;
1764
+ background: #1976d2;
1765
+ color: white;
1766
+ border: none;
1767
+ border-radius: 4px;
1768
+ cursor: pointer;
1769
+ }
1770
+ `]
1771
+ })
1772
+ export class CustomChatComponent implements OnInit {
1773
+ @Input() parameters: any;
1774
+ message = '';
1775
+ messages: any[] = [];
205
1776
 
206
- Users can easily pick an interface and render it in their app.
1777
+ ngOnInit() {
1778
+ // Subscribe to messages
1779
+ this.parameters?.messages?.subscribe((msgs: any[]) => {
1780
+ this.messages = msgs;
1781
+ });
1782
+ }
207
1783
 
208
- If no API credentials are provided, a default home page will be displayed where users can scan or manually enter the event details.
1784
+ sendMessage() {
1785
+ if (this.message.trim()) {
1786
+ this.parameters?.sendMessage({
1787
+ message: this.message,
1788
+ type: 'group',
1789
+ parameters: this.parameters
1790
+ });
1791
+ this.message = '';
1792
+ }
1793
+ }
1794
+ }
1795
+ ```
209
1796
 
210
- To use these prebuilt event rooms, simply import them into your application:
1797
+ ### Recording Methods
211
1798
 
212
- ```javascript
213
- ## Simplest Usage
1799
+ ```typescript
1800
+ // Start recording
1801
+ parameters.startRecording({ parameters });
1802
+
1803
+ // Stop recording
1804
+ parameters.stopRecording({ parameters });
1805
+
1806
+ // Pause recording
1807
+ parameters.pauseRecording({ parameters });
1808
+
1809
+ // Resume recording
1810
+ parameters.resumeRecording({ parameters });
1811
+
1812
+ // Configure recording settings
1813
+ parameters.updateRecording({
1814
+ recordingMediaOptions: 'video', // or 'audio'
1815
+ recordingAudioOptions: 'all', // or 'host'
1816
+ recordingVideoOptions: 'all', // or 'host'
1817
+ recordingVideoType: 'fullDisplay', // or 'bestDisplay', 'all'
1818
+ recordingDisplayType: 'video', // 'media', 'video', 'all'
1819
+ recordingBackgroundColor: '#000000',
1820
+ recordingNameTagsColor: '#ffffff',
1821
+ recordingOrientationVideo: 'landscape', // or 'portrait'
1822
+ recordingNameTags: true,
1823
+ recordingAddHLS: false,
1824
+ parameters
1825
+ });
1826
+
1827
+ // Check recording state
1828
+ const isRecording = parameters.recordStarted;
1829
+ const isPaused = parameters.recordPaused;
1830
+ const recordingTime = parameters.recordElapsedTime;
1831
+
1832
+ // Subscribe to recording state changes
1833
+ parameters.recordStarted.subscribe((isRecording: boolean) => {
1834
+ console.log('Recording:', isRecording ? 'ACTIVE' : 'STOPPED');
1835
+ });
1836
+ ```
214
1837
 
215
- The simplest way to use MediaSFU in your Angular application is by directly rendering the prebuilt event room component, such as `MediasfuGeneric`.
1838
+ ### Polls & Surveys Methods
216
1839
 
217
1840
  ```typescript
218
- // app.component.ts
219
- import { Component } from '@angular/core';
220
- import { MediasfuGeneric } from 'mediasfu-angular';
221
-
1841
+ // Create a poll
1842
+ parameters.handleCreatePoll({
1843
+ poll: {
1844
+ question: 'What time works best?',
1845
+ type: 'multiple', // or 'single'
1846
+ options: ['10 AM', '2 PM', '5 PM']
1847
+ },
1848
+ parameters
1849
+ });
1850
+
1851
+ // Vote on a poll
1852
+ parameters.handleVotePoll({
1853
+ pollId: 'poll-id',
1854
+ optionIndex: 1,
1855
+ parameters
1856
+ });
1857
+
1858
+ // End a poll
1859
+ parameters.handleEndPoll({
1860
+ pollId: 'poll-id',
1861
+ parameters
1862
+ });
1863
+
1864
+ // Access poll data
1865
+ const polls = parameters.polls;
1866
+
1867
+ // Subscribe to polls changes
1868
+ parameters.polls.subscribe((polls: any[]) => {
1869
+ const activePoll = polls.find(p => p.status === 'active');
1870
+ console.log('Active poll:', activePoll);
1871
+ });
1872
+
1873
+ // Example: Custom poll component
222
1874
  @Component({
223
- selector: 'app-root',
1875
+ selector: 'app-custom-poll',
224
1876
  standalone: true,
225
- imports: [MediasfuGeneric],
1877
+ imports: [CommonModule],
226
1878
  template: `
227
- <app-mediasfu-generic></app-mediasfu-generic>
1879
+ @if (activePoll) {
1880
+ <div class="poll-container">
1881
+ <h3>{{ activePoll.question }}</h3>
1882
+ <div class="poll-options">
1883
+ @for (option of activePoll.options; track $index) {
1884
+ <button
1885
+ class="poll-option"
1886
+ (click)="vote($index)">
1887
+ {{ option }}
1888
+ <span class="votes">({{ activePoll.votes?.[$index] || 0 }} votes)</span>
1889
+ </button>
1890
+ }
1891
+ </div>
1892
+ @if (isHost) {
1893
+ <button class="end-poll" (click)="endPoll()">
1894
+ End Poll
1895
+ </button>
1896
+ }
1897
+ </div>
1898
+ }
228
1899
  `,
1900
+ styles: [`
1901
+ .poll-container {
1902
+ padding: 20px;
1903
+ background: white;
1904
+ border-radius: 8px;
1905
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
1906
+ }
1907
+ .poll-options {
1908
+ display: flex;
1909
+ flex-direction: column;
1910
+ gap: 10px;
1911
+ margin: 20px 0;
1912
+ }
1913
+ .poll-option {
1914
+ padding: 12px;
1915
+ border: 2px solid #1976d2;
1916
+ background: white;
1917
+ border-radius: 4px;
1918
+ cursor: pointer;
1919
+ transition: all 0.3s;
1920
+ display: flex;
1921
+ justify-content: space-between;
1922
+ }
1923
+ .poll-option:hover {
1924
+ background: #1976d2;
1925
+ color: white;
1926
+ }
1927
+ .votes {
1928
+ font-size: 0.9em;
1929
+ opacity: 0.7;
1930
+ }
1931
+ .end-poll {
1932
+ padding: 10px 20px;
1933
+ background: #d32f2f;
1934
+ color: white;
1935
+ border: none;
1936
+ border-radius: 4px;
1937
+ cursor: pointer;
1938
+ }
1939
+ `]
229
1940
  })
230
- export class AppComponent { }
231
- ```
1941
+ export class CustomPollComponent implements OnInit {
1942
+ @Input() parameters: any;
1943
+ activePoll: any = null;
232
1944
 
233
- That's it! With just 3 lines of code, you have a fully functional video conferencing application with:
1945
+ ngOnInit() {
1946
+ this.parameters?.polls?.subscribe((polls: any[]) => {
1947
+ this.activePoll = polls.find((p: any) => p.status === 'active');
1948
+ });
1949
+ }
234
1950
 
235
- - 🎥 **Video & Audio Streaming** - High-quality real-time communication
236
- - 🖥️ **Screen Sharing** - Share your screen with annotation support
237
- - 💬 **Interactive Chat** - Direct and group messaging
238
- - 👥 **Participant Management** - Join/leave controls and permissions
239
- - 🎨 **Customizable UI** - Professional, responsive design out of the box
240
- - 📱 **Mobile Responsive** - Works seamlessly on all devices
241
- - 🔧 **Zero Configuration** - No complex setup required
1951
+ get isHost() {
1952
+ return this.parameters?.islevel === '2';
1953
+ }
242
1954
 
243
- ## Why Choose MediaSFU?
1955
+ vote(optionIndex: number) {
1956
+ if (this.activePoll) {
1957
+ this.parameters?.handleVotePoll({
1958
+ pollId: this.activePoll.id,
1959
+ optionIndex,
1960
+ parameters: this.parameters
1961
+ });
1962
+ }
1963
+ }
244
1964
 
245
- ### ⚡ **Lightning Fast Setup**
246
- From `npm install` to a working video conference in under 2 minutes. No WebRTC knowledge required.
1965
+ endPoll() {
1966
+ if (this.activePoll) {
1967
+ this.parameters?.handleEndPoll({
1968
+ pollId: this.activePoll.id,
1969
+ parameters: this.parameters
1970
+ });
1971
+ }
1972
+ }
1973
+ }
1974
+ ```
247
1975
 
248
- ### 🎯 **Multiple Event Types Ready**
249
- Choose from pre-built, production-ready templates:
1976
+ ### Breakout Rooms Methods
250
1977
 
251
1978
  ```typescript
252
- import {
253
- MediasfuGeneric, // 🌐 All-purpose meetings
254
- MediasfuBroadcast, // 📺 Live streaming events
255
- MediasfuWebinar, // 🎓 Educational sessions
256
- MediasfuConference, // 💼 Professional meetings
257
- MediasfuChat // 💬 Interactive chat rooms
258
- } from 'mediasfu-angular';
1979
+ // Create breakout rooms
1980
+ parameters.createBreakoutRooms({
1981
+ numberOfRooms: 3,
1982
+ participants: parameters.participants,
1983
+ parameters
1984
+ });
1985
+
1986
+ // Assign participant to room
1987
+ parameters.assignParticipantToRoom({
1988
+ participantId: 'participant-id',
1989
+ roomIndex: 0,
1990
+ parameters
1991
+ });
1992
+
1993
+ // Start breakout rooms
1994
+ parameters.startBreakoutRooms({ parameters });
1995
+
1996
+ // Stop breakout rooms
1997
+ parameters.stopBreakoutRooms({ parameters });
1998
+
1999
+ // Access breakout room data
2000
+ const breakoutRooms = parameters.breakoutRooms;
2001
+ const currentRoom = parameters.currentBreakoutRoom;
2002
+
2003
+ // Subscribe to breakout room changes
2004
+ parameters.breakoutRooms.subscribe((rooms: any[]) => {
2005
+ console.log('Breakout rooms:', rooms);
2006
+ });
259
2007
  ```
260
2008
 
261
- ### 🛠️ **Infinitely Customizable**
262
-
263
- **Replace Any Component:** Don't like our video card? Swap it with yours:
2009
+ ### Whiteboard Methods
264
2010
 
265
2011
  ```typescript
266
- // Your custom video component
267
- @Component({
268
- selector: 'my-video-card',
269
- template: `<div class="my-custom-video">{{ participant.name }}</div>`
270
- })
271
- export class MyVideoCard { }
272
-
273
- // Use it in MediaSFU
274
- const customVideoCard = this.customComponentService.createComponentWithInjector(
275
- MyVideoCard, { participant: participantData }
276
- );
2012
+ // Show/hide whiteboard
2013
+ parameters.updateWhiteboardStarted.next(true);
2014
+ parameters.updateWhiteboardEnded.next(false);
277
2015
 
278
- // MediaSFU will use your component instead
279
- this.mediasfuParameters = { customVideoCard };
280
- ```
2016
+ // Configure whiteboard
2017
+ parameters.launchConfigureWhiteboard?.launchConfigureWhiteboard({ parameters });
281
2018
 
282
- **Custom Layouts:** Build your own UI while keeping MediaSFU's powerful backend:
2019
+ // Access whiteboard state
2020
+ const isWhiteboardActive = parameters.whiteboardStarted;
283
2021
 
284
- ```typescript
285
- // Your WhatsApp-style video call UI
286
- @Component({
287
- template: `
288
- <div class="whatsapp-style-layout">
289
- <header>My Custom Header</header>
290
- <main>Your video layout here</main>
291
- </div>
292
- `
293
- })
294
- export class MyCustomLayout { }
2022
+ // Subscribe to whiteboard state
2023
+ parameters.whiteboardStarted.subscribe((isActive: boolean) => {
2024
+ console.log('Whiteboard:', isActive ? 'ACTIVE' : 'INACTIVE');
2025
+ });
295
2026
 
296
- // MediaSFU handles all the complex WebRTC, you handle the UI
297
- this.mediasfuParameters = {
298
- customMainComponent: MyCustomLayout,
299
- returnUI: false // Disable default MediaSFU UI
300
- };
2027
+ // Access whiteboard users
2028
+ const whiteboardUsers = parameters.whiteboardUsers;
301
2029
  ```
302
2030
 
303
- ### 🚀 **Production-Ready Features**
304
-
305
- - **Cloud Recording** with custom watermarks and layouts
306
- - **Breakout Rooms** for team collaboration
307
- - **Polls & Surveys** for audience engagement
308
- - **Waiting Rooms** with host controls
309
- - **Advanced Permissions** and moderation tools
310
- - **Real-time Analytics** and monitoring
311
- - **Enterprise Security** with end-to-end encryption
312
-
313
- ### 🌍 **Flexible Deployment Options**
2031
+ ### Utility Methods
314
2032
 
315
2033
  ```typescript
316
- // Option 1: MediaSFU Cloud (Fastest setup)
317
- const credentials = { apiUserName: 'your-key', apiKey: 'your-secret' };
2034
+ // Check permissions
2035
+ const hasPermission = await parameters.checkPermission({
2036
+ permissionType: 'video', // or 'audio'
2037
+ parameters
2038
+ });
2039
+
2040
+ // Format large numbers
2041
+ const formatted = parameters.formatNumber(1250000); // Returns "1.25M"
2042
+
2043
+ // Sleep/delay
2044
+ await parameters.sleep({ ms: 1000 });
2045
+
2046
+ // Update display settings
2047
+ parameters.updateMainWindow.next(true); // Show/hide main window
2048
+
2049
+ // Trigger layout recalculation
2050
+ parameters.onScreenChanges({ changed: true, parameters });
2051
+
2052
+ // Get room information
2053
+ const roomInfo = {
2054
+ name: parameters.roomName,
2055
+ host: parameters.host,
2056
+ capacity: parameters.capacity,
2057
+ eventType: parameters.eventType,
2058
+ participants: parameters.participants,
2059
+ isRecording: parameters.recordStarted
2060
+ };
318
2061
 
319
- // Option 2: Self-hosted Community Edition (Free)
320
- const localLink = 'http://your-server.com';
2062
+ // Subscribe to room state changes
2063
+ parameters.roomName.subscribe((name: string) => {
2064
+ console.log('Room name:', name);
2065
+ });
321
2066
 
322
- // Option 3: Hybrid (Best of both worlds)
323
- const config = {
324
- localLink: 'http://your-server.com',
325
- connectMediaSFU: true, // Use cloud for advanced features
326
- credentials: credentials
327
- };
2067
+ parameters.participantsCounter.subscribe((count: number) => {
2068
+ console.log('Participant count:', count);
2069
+ });
328
2070
  ```
329
2071
 
2072
+ ---
2073
+
330
2074
  ## Programmatically Fetching Tokens
331
2075
 
332
2076
  If you prefer to fetch the required tokens programmatically without visiting MediaSFU's website, you can use the `PreJoinPage` component and pass your credentials as inputs.
@@ -2590,335 +4334,504 @@ Once your custom components are built, modify the imports of `prepopulateUserMed
2590
4334
 
2591
4335
  This allows for full flexibility in how media is displayed in both the main and mini grids, giving you the ability to tailor the user experience to your specific needs.
2592
4336
 
2593
- ## Custom Component Injection Service
2594
4337
 
2595
- MediaSFU provides a powerful `CustomComponentInjectionService` that enables advanced custom component integration across all MediaSFU components. This service offers a comprehensive set of utilities for managing custom component rendering, validation, and lifecycle management.
2596
4338
 
2597
- ### Features
4339
+ ---
2598
4340
 
2599
- - **Type-safe component validation**: Check if components are Angular components, HTML elements, or function components
2600
- - **Dynamic component injection**: Create components with custom parameters and injectors
2601
- - **Safe rendering utilities**: Handle different component types with proper error handling and fallbacks
2602
- - **Component lifecycle management**: Proper cleanup and destruction of custom components
2603
- - **Template integration helpers**: Utilities for the custom component pattern used throughout MediaSFU
4341
+ ## UI Component Customization & Override System
2604
4342
 
2605
- ### Basic Usage
4343
+ MediaSFU provides comprehensive customization capabilities for all UI components through a powerful override system. You can customize styling, inject custom templates, or completely replace components with your own implementations.
2606
4344
 
2607
- First, import the service and types:
4345
+ ### Overview
2608
4346
 
2609
- ```typescript
2610
- import {
2611
- CustomComponentInjectionService,
2612
- CustomComponent,
2613
- CustomComponentType,
2614
- CustomComponentParameters,
2615
- CustomComponentContext
2616
- } from 'mediasfu-angular';
2617
- ```
4347
+ The customization system operates at three levels:
2618
4348
 
2619
- #### Injecting the Service
4349
+ 1. **Style Overrides**: Apply custom CSS styles to containers, overlays, and content areas
4350
+ 2. **Template Injection**: Replace default component templates with custom Angular templates
4351
+ 3. **Component Replacement**: Completely replace MediaSFU components with your own using the `WithOverrideDirective`
2620
4352
 
2621
- ```typescript
2622
- import { Component, inject } from '@angular/core';
4353
+ ### 1. Style Customization
4354
+
4355
+ All display and modal components support style customization through Input properties:
4356
+
4357
+ #### Display Components (MainContainer, MainAspect, MainScreen, MainGrid, OtherGrid, SubAspect, FlexibleGrid, AudioGrid)
2623
4358
 
4359
+ ```typescript
2624
4360
  @Component({
2625
- selector: 'app-my-component',
2626
- template: `...`
4361
+ selector: 'app-my-meeting',
4362
+ template: `
4363
+ <app-main-container
4364
+ [containerStyle]="{
4365
+ backgroundColor: '#1a1a1a',
4366
+ border: '2px solid #333',
4367
+ borderRadius: '12px',
4368
+ padding: '20px'
4369
+ }"
4370
+ [backgroundColor]="'transparent'">
4371
+ </app-main-container>
4372
+
4373
+ <app-flexible-grid
4374
+ [containerStyle]="{
4375
+ display: 'grid',
4376
+ gridTemplateColumns: 'repeat(3, 1fr)',
4377
+ gap: '16px'
4378
+ }">
4379
+ </app-flexible-grid>
4380
+ `
2627
4381
  })
2628
- export class MyComponent {
2629
- private customComponentService = inject(CustomComponentInjectionService);
4382
+ export class MyMeetingComponent {
4383
+ // Custom styles can also be dynamic
4384
+ mainGridStyle: Partial<CSSStyleDeclaration> = {
4385
+ backgroundColor: '#2a2a2a',
4386
+ maxWidth: '1400px',
4387
+ margin: '0 auto'
4388
+ };
2630
4389
  }
2631
4390
  ```
2632
4391
 
2633
- #### Creating Custom Components with Parameters
4392
+ #### Modal Components (All modals support overlayStyle, contentStyle)
2634
4393
 
2635
4394
  ```typescript
2636
- // Create a custom component with injected parameters
2637
- const customVideoCard = this.customComponentService.createComponentWithInjector(
2638
- MyCustomVideoCard,
2639
- {
2640
- participant: participantData,
2641
- options: videoOptions,
2642
- callbacks: eventHandlers
4395
+ @Component({
4396
+ selector: 'app-custom-modal-example',
4397
+ template: `
4398
+ <!-- Participants Modal with custom styling -->
4399
+ <app-participants-modal
4400
+ [isParticipantsModalVisible]="true"
4401
+ [overlayStyle]="{
4402
+ backgroundColor: 'rgba(0, 0, 0, 0.85)',
4403
+ backdropFilter: 'blur(8px)'
4404
+ }"
4405
+ [contentStyle]="{
4406
+ backgroundColor: '#ffffff',
4407
+ borderRadius: '16px',
4408
+ boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
4409
+ maxWidth: '600px',
4410
+ padding: '24px'
4411
+ }">
4412
+ </app-participants-modal>
4413
+
4414
+ <!-- Recording Modal with custom theme -->
4415
+ <app-recording-modal
4416
+ [isRecordingModalVisible]="true"
4417
+ [contentStyle]="{
4418
+ backgroundColor: '#f5f5f5',
4419
+ border: '3px solid #007bff',
4420
+ fontFamily: 'Inter, sans-serif'
4421
+ }">
4422
+ </app-recording-modal>
4423
+ `
4424
+ })
4425
+ export class CustomModalExample {
4426
+ // Styles can be computed or conditional
4427
+ get modalOverlay(): Partial<CSSStyleDeclaration> {
4428
+ return {
4429
+ backgroundColor: this.isDarkMode ? 'rgba(0,0,0,0.9)' : 'rgba(0,0,0,0.5)'
4430
+ };
2643
4431
  }
2644
- );
2645
-
2646
- // Use in MediaSFU component
2647
- const params = {
2648
- customVideoCard: customVideoCard,
2649
- // ... other parameters
2650
- };
2651
- ```
2652
-
2653
- #### Component Type Validation
2654
-
2655
- ```typescript
2656
- // Check component types
2657
- if (this.customComponentService.isCustomComponent(component)) {
2658
- // Handle Angular component with injector
2659
- console.log('Angular component detected');
2660
- } else if (this.customComponentService.isHTMLElement(component)) {
2661
- // Handle HTML element
2662
- console.log('HTML element detected');
2663
- } else if (this.customComponentService.isFunctionComponent(component)) {
2664
- // Handle function component
2665
- console.log('Function component detected');
2666
4432
  }
2667
4433
  ```
2668
4434
 
2669
- #### Safe HTML Element Access
2670
-
2671
- ```typescript
2672
- // Safely get outerHTML from components
2673
- const htmlContent = this.customComponentService.getHtmlElementOuterHTML(component);
2674
- if (htmlContent) {
2675
- // Use the HTML content
2676
- element.innerHTML = htmlContent;
2677
- }
2678
- ```
4435
+ ### 2. Template Injection
2679
4436
 
2680
- #### Component Resolution Pattern
4437
+ Replace default component templates with custom Angular templates using `TemplateRef`:
2681
4438
 
2682
4439
  ```typescript
2683
- // Resolve between custom and default components
2684
- const resolvedComponent = this.customComponentService.resolveComponent(
2685
- customComponent, // May be undefined
2686
- defaultComponent // Fallback component
2687
- );
2688
-
2689
- // Check if custom main component should be used
2690
- const useCustomLayout = this.customComponentService.shouldUseCustomMainComponent(
2691
- customMainComponent
2692
- );
2693
- ```
4440
+ import { Component, TemplateRef, ViewChild } from '@angular/core';
2694
4441
 
2695
- ### Advanced Usage Examples
2696
-
2697
- #### 1. Dynamic Component Rendering
2698
-
2699
- ```typescript
2700
4442
  @Component({
2701
- selector: 'app-dynamic-renderer',
4443
+ selector: 'app-custom-template-demo',
2702
4444
  template: `
2703
- <ng-container #dynamicContainer></ng-container>
2704
- `
2705
- })
2706
- export class DynamicRenderer {
2707
- @ViewChild('dynamicContainer', { read: ViewContainerRef })
2708
- container!: ViewContainerRef;
2709
-
2710
- private customComponentService = inject(CustomComponentInjectionService);
2711
-
2712
- renderCustomComponent(customComponent: CustomComponentType<any>) {
2713
- const context: CustomComponentContext = {
2714
- parameters: {
2715
- data: this.componentData,
2716
- events: this.eventHandlers
2717
- },
2718
- injector: this.injector,
2719
- config: {
2720
- enabled: true,
2721
- fallbackToDefault: true
2722
- }
2723
- };
4445
+ <!-- Define custom template -->
4446
+ <ng-template #customMainGridTemplate let-components="components">
4447
+ <div class="my-custom-grid">
4448
+ <div class="grid-header">
4449
+ <h3>Active Participants</h3>
4450
+ <span class="count">{{ components?.length || 0 }}</span>
4451
+ </div>
4452
+ <div class="grid-content">
4453
+ <!-- Custom rendering logic -->
4454
+ <div *ngFor="let component of components" class="participant-tile">
4455
+ <ng-container *ngComponentOutlet="component"></ng-container>
4456
+ </div>
4457
+ </div>
4458
+ </div>
4459
+ </ng-template>
2724
4460
 
2725
- const componentRef = this.customComponentService.renderCustomComponent(
2726
- customComponent,
2727
- this.container,
2728
- context
2729
- );
4461
+ <!-- Use custom template -->
4462
+ <app-main-grid
4463
+ [customTemplate]="customMainGridTemplate"
4464
+ [mainGridStream]="streams">
4465
+ </app-main-grid>
2730
4466
 
2731
- // Store reference for cleanup
2732
- this.activeComponents.push(componentRef);
2733
- }
4467
+ <!-- Custom modal template -->
4468
+ <ng-template #customAlertTemplate let-message="message" let-type="type">
4469
+ <div class="custom-alert" [class.error]="type === 'error'">
4470
+ <i class="alert-icon"></i>
4471
+ <p>{{ message }}</p>
4472
+ <button (click)="closeAlert()">Dismiss</button>
4473
+ </div>
4474
+ </ng-template>
4475
+
4476
+ <app-alert-component
4477
+ [customTemplate]="customAlertTemplate"
4478
+ [visible]="showAlert"
4479
+ [message]="alertMessage">
4480
+ </app-alert-component>
4481
+ `,
4482
+ styles: [`
4483
+ .my-custom-grid {
4484
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
4485
+ border-radius: 12px;
4486
+ padding: 20px;
4487
+ }
4488
+
4489
+ .grid-header {
4490
+ display: flex;
4491
+ justify-content: space-between;
4492
+ color: white;
4493
+ margin-bottom: 16px;
4494
+ }
4495
+
4496
+ .participant-tile {
4497
+ background: rgba(255, 255, 255, 0.1);
4498
+ border-radius: 8px;
4499
+ padding: 12px;
4500
+ margin: 8px 0;
4501
+ }
4502
+ `]
4503
+ })
4504
+ export class CustomTemplateDemo {
4505
+ @ViewChild('customMainGridTemplate') customMainGridTemplate!: TemplateRef<any>;
4506
+ @ViewChild('customAlertTemplate') customAlertTemplate!: TemplateRef<any>;
2734
4507
 
2735
- ngOnDestroy() {
2736
- // Clean up all custom components
2737
- this.activeComponents.forEach(component => {
2738
- this.customComponentService.destroyCustomComponent(component);
2739
- });
4508
+ streams: any[] = [];
4509
+ showAlert = false;
4510
+ alertMessage = '';
4511
+
4512
+ closeAlert() {
4513
+ this.showAlert = false;
2740
4514
  }
2741
4515
  }
2742
4516
  ```
2743
4517
 
2744
- #### 2. Custom Video Card Implementation
4518
+ ### 3. Component Replacement with UI Overrides
4519
+
4520
+ Use the `uiOverrides` system to completely replace MediaSFU components with your own:
2745
4521
 
2746
4522
  ```typescript
2747
- // Define your custom video card component
4523
+ import { Component, Type } from '@angular/core';
4524
+ import { MediasfuUICustomOverrides } from 'mediasfu-angular';
4525
+
4526
+ // Your custom replacement components
2748
4527
  @Component({
2749
- selector: 'app-custom-video-card',
4528
+ selector: 'app-my-custom-video-card',
2750
4529
  template: `
2751
- <div class="custom-video-wrapper">
2752
- <video [srcObject]="videoStream" autoplay playsinline></video>
2753
- <div class="custom-controls">
2754
- <button (click)="toggleAudio()">🎤</button>
2755
- <button (click)="toggleVideo()">📹</button>
4530
+ <div class="premium-video-card">
4531
+ <video [srcObject]="videoStream" autoplay></video>
4532
+ <div class="overlay">
4533
+ <span class="name">{{ participant.name }}</span>
4534
+ <button class="pin-btn" (click)="togglePin()">📌</button>
2756
4535
  </div>
2757
- <div class="custom-info">{{ participantName }}</div>
2758
4536
  </div>
2759
4537
  `,
2760
- imports: [CommonModule]
4538
+ styles: [`
4539
+ .premium-video-card {
4540
+ position: relative;
4541
+ border-radius: 16px;
4542
+ overflow: hidden;
4543
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
4544
+ }
4545
+ .overlay {
4546
+ position: absolute;
4547
+ bottom: 0;
4548
+ left: 0;
4549
+ right: 0;
4550
+ background: linear-gradient(transparent, rgba(0,0,0,0.8));
4551
+ padding: 12px;
4552
+ display: flex;
4553
+ justify-content: space-between;
4554
+ align-items: center;
4555
+ }
4556
+ `]
2761
4557
  })
2762
- export class CustomVideoCard {
2763
- @Input() participant!: Participant;
2764
- @Input() videoStream!: MediaStream;
2765
- @Input() options!: any;
2766
-
2767
- get participantName() {
2768
- return this.participant?.name || 'Unknown';
2769
- }
2770
-
2771
- toggleAudio() {
2772
- // Custom audio toggle logic
2773
- }
2774
-
2775
- toggleVideo() {
2776
- // Custom video toggle logic
2777
- }
2778
- }
2779
-
2780
- // In your main component
2781
- export class MyMediaComponent {
2782
- private customComponentService = inject(CustomComponentInjectionService);
2783
-
2784
- ngOnInit() {
2785
- // Create custom video card with parameters
2786
- const customVideoCard = this.customComponentService.createComponentWithInjector(
2787
- CustomVideoCard,
2788
- {
2789
- participant: this.currentParticipant,
2790
- options: this.videoOptions
2791
- }
2792
- );
2793
-
2794
- // Use in MediaSFU parameters
2795
- this.mediasfuParameters = {
2796
- customVideoCard: customVideoCard,
2797
- // ... other parameters
2798
- };
2799
- }
4558
+ export class MyCustomVideoCard {
4559
+ // Your custom implementation
2800
4560
  }
2801
- ```
2802
4561
 
2803
- #### 3. Custom Main Component with Template Restructuring
2804
-
2805
- ```typescript
2806
4562
  @Component({
2807
- selector: 'app-custom-main-layout',
4563
+ selector: 'app-my-custom-control-buttons',
2808
4564
  template: `
2809
- <div class="custom-main-layout">
2810
- <header class="custom-header">
2811
- <h1>My Custom Stream</h1>
2812
- <div class="custom-controls">
2813
- <!-- Custom control buttons -->
2814
- </div>
2815
- </header>
2816
-
2817
- <main class="custom-content">
2818
- <div class="video-area">
2819
- <!-- Your custom video layout -->
2820
- </div>
2821
- <div class="participant-list">
2822
- <!-- Custom participant list -->
2823
- </div>
2824
- </main>
2825
-
2826
- <footer class="custom-footer">
2827
- <!-- Custom footer content -->
2828
- </footer>
4565
+ <div class="modern-controls">
4566
+ <button class="control-btn mic" [class.active]="!isMuted" (click)="toggleMic()">
4567
+ <i class="icon-mic"></i>
4568
+ </button>
4569
+ <button class="control-btn camera" [class.active]="!isVideoOff" (click)="toggleVideo()">
4570
+ <i class="icon-camera"></i>
4571
+ </button>
4572
+ <button class="control-btn screen" (click)="toggleScreen()">
4573
+ <i class="icon-screen"></i>
4574
+ </button>
4575
+ <button class="control-btn leave danger" (click)="leaveMeeting()">
4576
+ <i class="icon-leave"></i>
4577
+ </button>
2829
4578
  </div>
2830
4579
  `,
2831
- imports: [CommonModule]
4580
+ styles: [`
4581
+ .modern-controls {
4582
+ display: flex;
4583
+ gap: 12px;
4584
+ padding: 16px;
4585
+ background: rgba(0, 0, 0, 0.8);
4586
+ border-radius: 24px;
4587
+ }
4588
+ .control-btn {
4589
+ width: 48px;
4590
+ height: 48px;
4591
+ border-radius: 50%;
4592
+ border: none;
4593
+ cursor: pointer;
4594
+ transition: all 0.2s;
4595
+ }
4596
+ .control-btn.active {
4597
+ background: #4CAF50;
4598
+ color: white;
4599
+ }
4600
+ .control-btn.danger {
4601
+ background: #f44336;
4602
+ color: white;
4603
+ }
4604
+ `]
2832
4605
  })
2833
- export class CustomMainLayout {
2834
- @Input() parameters!: any;
2835
- @Input() credentials!: any;
4606
+ export class MyCustomControlButtons {
4607
+ // Your custom implementation
2836
4608
  }
2837
4609
 
2838
- // Usage
2839
- const customMainComponent = this.customComponentService.createComponentWithInjector(
2840
- CustomMainLayout,
2841
- {
2842
- theme: 'dark',
2843
- layout: 'grid'
2844
- }
2845
- );
2846
-
2847
- // MediaSFU will use this instead of default layout
2848
- this.mediasfuParameters = {
2849
- customMainComponent: customMainComponent,
2850
- // ... other parameters
2851
- };
2852
- ```
2853
-
2854
- ### Component Validation and Error Handling
2855
-
2856
- ```typescript
2857
- // Validate before using
2858
- if (this.customComponentService.validateCustomComponent(customComponent)) {
2859
- // Component is valid, proceed with rendering
2860
- this.renderComponent(customComponent);
2861
- } else {
2862
- // Handle invalid component, use fallback
2863
- console.warn('Invalid custom component, using default');
2864
- this.renderComponent(this.defaultComponent);
4610
+ @Component({
4611
+ selector: 'app-meeting-room',
4612
+ template: `
4613
+ <app-mediasfu-generic
4614
+ [credentials]="credentials"
4615
+ [uiOverrides]="customUIOverrides"
4616
+ [returnUI]="false">
4617
+ </app-mediasfu-generic>
4618
+ `
4619
+ })
4620
+ export class MeetingRoomComponent {
4621
+ credentials = { apiUserName: 'user', apiKey: 'key' };
4622
+
4623
+ // Define your UI overrides
4624
+ customUIOverrides: Partial<MediasfuUICustomOverrides> = {
4625
+ // Replace video cards
4626
+ VideoCard: MyCustomVideoCard as Type<any>,
4627
+
4628
+ // Replace control buttons
4629
+ ControlButtonsComponent: MyCustomControlButtons as Type<any>,
4630
+
4631
+ // Replace modals
4632
+ ParticipantsModal: MyCustomParticipantsModal as Type<any>,
4633
+
4634
+ // Replace other display components
4635
+ MainGrid: MyCustomMainGrid as Type<any>,
4636
+ FlexibleGrid: MyCustomFlexibleGrid as Type<any>,
4637
+
4638
+ // ... any other components you want to replace
4639
+ };
2865
4640
  }
2866
-
2867
- // Merge options safely
2868
- const mergedOptions = this.customComponentService.mergeComponentOptions(
2869
- customOptions,
2870
- defaultOptions
2871
- );
2872
4641
  ```
2873
4642
 
2874
- ### TypeScript Types
4643
+ ### 4. Using WithOverrideDirective
2875
4644
 
2876
- The service works with these main types:
4645
+ The `*appWithOverride` directive enables conditional component replacement:
2877
4646
 
2878
4647
  ```typescript
2879
- // Union type for all supported component types
2880
- type CustomComponentType<T = any> = CustomComponent<T> | HTMLElement | CustomComponentFunction;
4648
+ import { Component } from '@angular/core';
4649
+ import { WithOverrideDirective } from 'mediasfu-angular';
2881
4650
 
2882
- // Angular component with injector
2883
- interface CustomComponent<T = any> {
2884
- component: Type<T>;
2885
- injector?: Injector;
4651
+ @Component({
4652
+ selector: 'app-conditional-override',
4653
+ imports: [WithOverrideDirective, /* other imports */],
4654
+ template: `
4655
+ <!-- Use default or custom component based on condition -->
4656
+ <app-video-card
4657
+ *appWithOverride="useCustomCard ? MyCustomVideoCard : null"
4658
+ [participant]="currentParticipant">
4659
+ </app-video-card>
4660
+
4661
+ <!-- Override with custom component when premium feature enabled -->
4662
+ <app-control-buttons
4663
+ *appWithOverride="isPremiumUser ? PremiumControlButtons : null">
4664
+ </app-control-buttons>
4665
+ `
4666
+ })
4667
+ export class ConditionalOverrideComponent {
4668
+ useCustomCard = false;
4669
+ isPremiumUser = false;
4670
+
4671
+ MyCustomVideoCard = MyCustomVideoCard;
4672
+ PremiumControlButtons = PremiumControlButtons;
2886
4673
  }
4674
+ ```
2887
4675
 
2888
- // Function that returns HTMLElement
2889
- type CustomComponentFunction = () => HTMLElement;
4676
+ ### 5. Complete Example: Themed Meeting Room
2890
4677
 
2891
- // Parameters for component injection
2892
- interface CustomComponentParameters {
2893
- [key: string]: any;
2894
- }
4678
+ Here's a comprehensive example combining all customization approaches:
2895
4679
 
2896
- // Context for component rendering
2897
- interface CustomComponentContext {
2898
- parameters: CustomComponentParameters;
2899
- injector?: Injector;
2900
- config?: ComponentInjectionConfig;
2901
- }
4680
+ ```typescript
4681
+ import { Component, TemplateRef, ViewChild, Type } from '@angular/core';
4682
+ import { MediasfuUICustomOverrides } from 'mediasfu-angular';
2902
4683
 
2903
- // Configuration options
2904
- interface ComponentInjectionConfig {
2905
- enabled: boolean;
2906
- overrideDefaults: boolean;
2907
- fallbackToDefault: boolean;
4684
+ @Component({
4685
+ selector: 'app-themed-meeting-room',
4686
+ template: `
4687
+ <!-- Custom alert template -->
4688
+ <ng-template #brandedAlertTemplate let-message="message">
4689
+ <div class="branded-alert">
4690
+ <img src="/assets/logo.png" alt="Logo" class="alert-logo">
4691
+ <p>{{ message }}</p>
4692
+ </div>
4693
+ </ng-template>
4694
+
4695
+ <app-mediasfu-conference
4696
+ [credentials]="credentials"
4697
+ [returnUI]="true"
4698
+ [uiOverrides]="themeOverrides"
4699
+ [containerStyle]="meetingContainerStyle">
4700
+
4701
+ <!-- Customize individual components -->
4702
+ <app-main-grid
4703
+ [containerStyle]="gridStyle"
4704
+ [customTemplate]="customGridTemplate">
4705
+ </app-main-grid>
4706
+
4707
+ <app-control-buttons-component
4708
+ [containerStyle]="controlsStyle">
4709
+ </app-control-buttons-component>
4710
+
4711
+ <app-participants-modal
4712
+ [overlayStyle]="modalOverlayStyle"
4713
+ [contentStyle]="modalContentStyle">
4714
+ </app-participants-modal>
4715
+
4716
+ <app-alert-component
4717
+ [customTemplate]="brandedAlertTemplate">
4718
+ </app-alert-component>
4719
+ </app-mediasfu-conference>
4720
+ `,
4721
+ styles: [`
4722
+ .branded-alert {
4723
+ display: flex;
4724
+ align-items: center;
4725
+ gap: 12px;
4726
+ padding: 16px;
4727
+ background: white;
4728
+ border-left: 4px solid #007bff;
4729
+ }
4730
+ `]
4731
+ })
4732
+ export class ThemedMeetingRoomComponent {
4733
+ @ViewChild('brandedAlertTemplate') brandedAlertTemplate!: TemplateRef<any>;
4734
+
4735
+ credentials = {
4736
+ apiUserName: 'your-api-username',
4737
+ apiKey: 'your-api-key'
4738
+ };
4739
+
4740
+ // Container styling
4741
+ meetingContainerStyle: Partial<CSSStyleDeclaration> = {
4742
+ backgroundColor: '#0f0f0f',
4743
+ fontFamily: 'Inter, system-ui, sans-serif'
4744
+ };
4745
+
4746
+ // Grid styling
4747
+ gridStyle: Partial<CSSStyleDeclaration> = {
4748
+ display: 'grid',
4749
+ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
4750
+ gap: '16px',
4751
+ padding: '20px'
4752
+ };
4753
+
4754
+ // Controls styling
4755
+ controlsStyle: Partial<CSSStyleDeclaration> = {
4756
+ position: 'fixed',
4757
+ bottom: '24px',
4758
+ left: '50%',
4759
+ transform: 'translateX(-50%)',
4760
+ backgroundColor: 'rgba(0, 0, 0, 0.9)',
4761
+ borderRadius: '32px',
4762
+ padding: '12px 24px'
4763
+ };
4764
+
4765
+ // Modal styling
4766
+ modalOverlayStyle: Partial<CSSStyleDeclaration> = {
4767
+ backgroundColor: 'rgba(0, 0, 0, 0.85)',
4768
+ backdropFilter: 'blur(10px)'
4769
+ };
4770
+
4771
+ modalContentStyle: Partial<CSSStyleDeclaration> = {
4772
+ backgroundColor: '#1a1a1a',
4773
+ color: '#ffffff',
4774
+ borderRadius: '16px',
4775
+ border: '1px solid #333'
4776
+ };
4777
+
4778
+ // Component overrides
4779
+ themeOverrides: Partial<MediasfuUICustomOverrides> = {
4780
+ VideoCard: BrandedVideoCard as Type<any>,
4781
+ ControlButtonsComponent: ModernControls as Type<any>
4782
+ };
2908
4783
  }
2909
4784
  ```
2910
4785
 
2911
- ### Best Practices
4786
+ ### Customizable Components Reference
4787
+
4788
+ #### Display Components
4789
+ - `MainContainer` - Main viewport container
4790
+ - `MainAspect` - Aspect ratio container
4791
+ - `MainScreen` - Screen share display
4792
+ - `MainGrid` - Primary participant grid
4793
+ - `OtherGrid` - Secondary participant grid
4794
+ - `SubAspect` - Sub-aspect ratio container
4795
+ - `FlexibleGrid` - Flexible layout grid
4796
+ - `AudioGrid` - Audio-only participants grid
4797
+
4798
+ Each accepts: `containerStyle?: Partial<CSSStyleDeclaration>`, `customTemplate?: TemplateRef<any>`
4799
+
4800
+ #### Modal Components
4801
+ - `LoadingModal` - Loading indicator
4802
+ - `ConfirmExitModal` - Exit confirmation
4803
+ - `ConfirmHereModal` - Presence confirmation
4804
+ - `ShareEventModal` - Event sharing
4805
+ - `AlertComponent` - Alert notifications
4806
+ - `MenuModal` - Main menu
4807
+ - `ParticipantsModal` - Participants list
4808
+ - `RecordingModal` - Recording controls
4809
+ - `RequestsModal` - Media requests
4810
+ - `WaitingRoomModal` - Waiting room management
4811
+ - `CoHostModal` - Co-host management
4812
+ - `DisplaySettingsModal` - Display settings
4813
+ - `EventSettingsModal` - Event settings
4814
+ - `MediaSettingsModal` - Media device settings
4815
+ - `MessagesModal` - Chat messages
4816
+ - `PollModal` - Polls interface
4817
+ - `BackgroundModal` - Background effects
4818
+ - `BreakoutRoomsModal` - Breakout rooms
4819
+ - `ConfigureWhiteboardModal` - Whiteboard settings
4820
+ - `ScreenboardModal` - Screen annotation
4821
+
4822
+ Each accepts: `overlayStyle?: Partial<CSSStyleDeclaration>`, `contentStyle?: Partial<CSSStyleDeclaration>`, `customTemplate?: TemplateRef<any>`
2912
4823
 
2913
- 1. **Always validate components** before rendering
2914
- 2. **Use proper cleanup** to prevent memory leaks
2915
- 3. **Leverage type guards** for safe component handling
2916
- 4. **Provide fallbacks** for failed component rendering
2917
- 5. **Use meaningful parameter names** when creating injectors
2918
- 6. **Test custom components** in isolation before integration
4824
+ ### Best Practices
2919
4825
 
2920
- The `CustomComponentInjectionService` provides a robust foundation for building highly customizable MediaSFU applications while maintaining type safety and proper Angular practices.
4826
+ 1. **Type Safety**: Always use `Partial<CSSStyleDeclaration>` for style properties
4827
+ 2. **Template Context**: Ensure your custom templates accept the correct context variables
4828
+ 3. **Component Lifecycle**: Custom replacement components should implement the same interface as the original
4829
+ 4. **Performance**: Avoid inline style objects in templates; define them in the component class
4830
+ 5. **Responsive Design**: Use CSS Grid and Flexbox for responsive layouts
4831
+ 6. **Accessibility**: Maintain ARIA attributes and keyboard navigation in custom components
4832
+ 7. **Testing**: Test custom components in isolation before integration
2921
4833
 
4834
+ ---
2922
4835
 
2923
4836
  # API Reference <a name="api-reference"></a>
2924
4837