Compare commits

..

28 Commits

Author SHA1 Message Date
Piwccle c4cdddd191 openvidu-deployment: add get_value_from_config script in GCP HA deployment 2026-02-03 14:55:21 +01:00
Piwccle 444188d653 openvidu-deployment: Add scripts for managing OpenVidu configuration and secrets in GCP deployment 2026-02-03 13:24:33 +01:00
Piwccle f083ca0cec openvidu-deployment: Update Azure deployment to include OpenVidu and LiveKit URLs in key vault secrets 2026-02-03 13:23:28 +01:00
cruizba fa434d2097 local-meet: Route 9080 through caddy to redir to /meet 2026-02-02 23:34:01 +01:00
Piwccle 9a3811d48e openvidu-deployment: Remove unused cluster data container parameters and related logic 2026-02-02 13:12:10 +01:00
Piwccle 98fc2c2d5b openvidu-deployment: Azure - fix update URL generation to use correct domain variable syntax 2026-01-29 17:59:19 +01:00
Piwccle a492515ce5 openvidu-deployment: Azure deployment scripts to include OpenVidu and LiveKit URLs in key vault secrets 2026-01-29 17:34:25 +01:00
cruizba 5e31998776 openvidu-deployment: gcp - Use main domain for TURN - Remove TURN server configuration parameters and related logic from deployment templates 2026-01-28 21:36:27 +01:00
cruizba c43b3e86e3 openvidu-deployment: azure - Use main domain for TURN - Remove TURN server configuration parameters and related logic from deployment templates 2026-01-27 22:41:33 +01:00
cruizba 755da724b3 openvidu-deployment: aws - Change IAM policy resources in CloudFormation templates for automation execution change in AWS 2026-01-27 20:00:54 +01:00
Piwccle 942e5d1062 Merge branch 'master' of https://github.com/OpenVidu/openvidu 2026-01-27 16:38:05 +01:00
Piwccle 483fda785a openvidu-deployment: Update default max number of media nodes to 5 to match the other clouds 2026-01-27 16:37:33 +01:00
cruizba 617c642215 openvidu-deployment: aws - Use main domain for TURN - Remove TURN server configuration parameters and related logic from deployment templates 2026-01-27 16:37:33 +01:00
CSantosM 83e38ae88a ov-components: Refactor pre-join component to replace Angular animations with CSS animations and optimize video preview container handling 2026-01-27 16:09:07 +01:00
CSantosM ed64c3a305 ov-components: Refactor audio and video device components to use reactive signals for improved performance and cleaner code 2026-01-27 16:01:53 +01:00
CSantosM f4264a2a8a ov-components: Refactors device service for performance
Replaces the old device service with a new implementation using Angular Signals for reactive state management.

This enhances performance by:
- Improving permission requests
- Providing live device detection
- Providing better error handling

The new implementation also integrates with the LiveKit client for track management.
2026-01-27 16:00:39 +01:00
CSantosM 5edb36670b ov-components: standardize device service usage and improve component styles 2026-01-27 15:39:09 +01:00
CSantosM 7208cb3a65 ov-components: Improves virtual background support handling
Adds a more robust mechanism to manage virtual background support, including:

- Displaying a warning message when the browser does not support virtual backgrounds.
- Disabling background selection buttons when virtual backgrounds are not supported.
- Optimizing background processor initialization and attachment for different browsers (handling lazy loading for Firefox).
- Centralizing the check for virtual background support in the OpenViduService.

This change ensures a better user experience by clearly indicating when virtual backgrounds are unavailable and preventing users from attempting to use an unsupported feature.
2026-01-26 13:13:47 +01:00
CSantosM 6cfa44c4f1 ov-components: Refactors Firefox background processor handling
Simplifies the logic for attaching and detaching the background processor in Firefox.

Ensures the processor is stopped when background effects are disabled, optimizing performance.
2026-01-22 19:35:22 +01:00
CSantosM 465403f8cb ov-components: Added getParticipantByIdentity to participantService 2026-01-22 12:31:00 +01:00
CSantosM b321d5bb69 ov-components: enhance resize handling and add height tracking in LayoutComponent 2026-01-19 17:03:24 +01:00
pabloFuente 81e7400807 openvidu-deployment: update all installation scripts from openvidu/agent-speech-processing to openvidu/agent-speech-processing-cloud 2026-01-19 14:14:26 +01:00
pabloFuente 73f84491a5 udpate.sh from agent-speech-processing to agent-speech-processing-cloud 2026-01-19 12:48:06 +01:00
CSantosM 59e4a14d15 ov-components: enhance background processing for Firefox and add getVideoTrack method 2026-01-18 16:31:07 +01:00
Carlos Santos 8dd058fd50 ov-components: add disconnected state handling and translations for multiple languages 2026-01-15 18:03:27 +01:00
cruizba 4a5afac382 openvidu-deployment: AWS - Update EBS volume type from gp2 to gp3 in CloudFormation templates 2026-01-15 17:55:52 +01:00
cruizba 8336b9e81c local-meet: update docker images to use 'main' 2026-01-09 20:04:55 +01:00
GitHub Actions d82d8b061a Revert "Bump version to 3.5.0"
This reverts commit 21906c971e.
2025-12-29 12:59:15 +00:00
68 changed files with 1503 additions and 1856 deletions

View File

@ -106,6 +106,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
private resizeTimeout: NodeJS.Timeout; private resizeTimeout: NodeJS.Timeout;
private videoIsAtRight: boolean = false; private videoIsAtRight: boolean = false;
private lastLayoutWidth: number = 0; private lastLayoutWidth: number = 0;
private lastLayoutHeight: number = 0;
/** /**
* @ignore * @ignore
@ -130,7 +131,9 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
ngAfterViewInit() { ngAfterViewInit() {
console.log('LayoutComponent.ngAfterViewInit'); console.log('LayoutComponent.ngAfterViewInit');
this.layoutService.initialize(this.layoutContainer.element.nativeElement); this.layoutService.initialize(this.layoutContainer.element.nativeElement);
this.lastLayoutWidth = this.layoutContainer.element.nativeElement.getBoundingClientRect().width; const rect = this.layoutContainer.element.nativeElement.getBoundingClientRect();
this.lastLayoutWidth = rect.width;
this.lastLayoutHeight = rect.height;
this.listenToResizeLayout(); this.listenToResizeLayout();
this.listenToCdkDrag(); this.listenToCdkDrag();
} }
@ -217,11 +220,21 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
private listenToResizeLayout() { private listenToResizeLayout() {
this.resizeObserver = new ResizeObserver((entries) => { this.resizeObserver = new ResizeObserver((entries) => {
const { width: parentWidth, height: parentHeight } = entries[0].contentRect;
clearTimeout(this.resizeTimeout); clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(() => { this.resizeTimeout = setTimeout(() => {
// Always update layout when container size changes
// This ensures layout recalculates when parent containers change
const widthDiff = Math.abs(this.lastLayoutWidth - parentWidth);
const heightDiff = Math.abs(this.lastLayoutHeight - parentHeight);
if (widthDiff > 1 || heightDiff > 1) {
this.layoutService.update();
this.cd.markForCheck();
}
// Handle minimized participant positioning
if (this.localParticipant?.isMinimized) { if (this.localParticipant?.isMinimized) {
const { width: parentWidth } = entries[0].contentRect;
if (this.panelService.isPanelOpened()) { if (this.panelService.isPanelOpened()) {
if (this.lastLayoutWidth < parentWidth) { if (this.lastLayoutWidth < parentWidth) {
// Layout is bigger than before. Maybe the settings panel(wider) has been transitioned to another panel. // Layout is bigger than before. Maybe the settings panel(wider) has been transitioned to another panel.
@ -240,8 +253,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
this.moveStreamToRight(parentWidth); this.moveStreamToRight(parentWidth);
} }
} }
this.lastLayoutWidth = parentWidth;
} }
this.lastLayoutWidth = parentWidth;
this.lastLayoutHeight = parentHeight;
}, 100); }, 100);
}); });

View File

@ -13,6 +13,13 @@
</button> </button>
} }
@if (!isVirtualBackgroundSupported()) {
<div class="not-supported-message">
<p class="warning-title">{{ 'PANEL.BACKGROUND.NOT_SUPPORTED' | translate }}</p>
<p class="warning-description">{{ 'PANEL.BACKGROUND.NOT_SUPPORTED_DESCRIPTION' | translate }}</p>
</div>
}
<div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none"> <div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
<div> <div>
<h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4> <h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4>
@ -24,6 +31,7 @@
[class.active-effect-btn]="backgroundSelectedId === effect.id" [class.active-effect-btn]="backgroundSelectedId === effect.id"
(click)="applyBackground(effect)" (click)="applyBackground(effect)"
[attr.id]="effect.id + '-btn'" [attr.id]="effect.id + '-btn'"
[disabled]="!isVirtualBackgroundSupported()"
[matTooltip]=" [matTooltip]="
effect.type === effectType.NONE effect.type === effectType.NONE
? ('PANEL.BACKGROUND.NO_EFFECTS' | translate) ? ('PANEL.BACKGROUND.NO_EFFECTS' | translate)
@ -44,7 +52,8 @@
class="effect-button" class="effect-button"
[id]="'effect-' + effect.id" [id]="'effect-' + effect.id"
[class.active-effect-btn]="backgroundSelectedId === effect.id" [class.active-effect-btn]="backgroundSelectedId === effect.id"
(click)="applyBackground(effect)" [class.disabled]="!isVirtualBackgroundSupported()"
(click)="isVirtualBackgroundSupported() && applyBackground(effect)"
> >
<img [src]="effect.thumbnail" /> <img [src]="effect.thumbnail" />
</div> </div>

View File

@ -12,6 +12,26 @@
margin: 10px 0; margin: 10px 0;
font-weight: 300; font-weight: 300;
} }
.not-supported-message {
padding: 15px;
margin: 10px;
background-color: var(--ov-warn-color, #ff9800);
border-radius: var(--ov-surface-radius);
color: var(--ov-text-surface-color);
.warning-title {
font-weight: 500;
margin: 0 0 8px 0;
}
.warning-description {
font-size: 0.9em;
margin: 0;
opacity: 0.9;
}
}
.effects-container { .effects-container {
display: block !important; display: block !important;
overflow-y: auto; overflow-y: auto;
@ -33,6 +53,11 @@
cursor: pointer; cursor: pointer;
} }
.effect-button.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.active-effect-btn { .active-effect-btn {
border: 2px solid var(--ov-accent-action-color); border: 2px solid var(--ov-accent-action-color);
} }

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, EventEmitter, Input, OnInit, Output, Signal } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { BackgroundEffect, EffectType } from '../../../models/background-effect.model'; import { BackgroundEffect, EffectType } from '../../../models/background-effect.model';
import { PanelType } from '../../../models/panel.model'; import { PanelType } from '../../../models/panel.model';
@ -23,7 +23,7 @@ export class BackgroundEffectsPanelComponent implements OnInit {
effectType = EffectType; effectType = EffectType;
backgroundImages: BackgroundEffect[] = []; backgroundImages: BackgroundEffect[] = [];
noEffectAndBlurredBackground: BackgroundEffect[] = []; noEffectAndBlurredBackground: BackgroundEffect[] = [];
private backgrounds: BackgroundEffect[]; private backgrounds: BackgroundEffect[] = [];
private backgroundSubs: Subscription; private backgroundSubs: Subscription;
/** /**
@ -38,6 +38,14 @@ export class BackgroundEffectsPanelComponent implements OnInit {
private cd: ChangeDetectorRef private cd: ChangeDetectorRef
) {} ) {}
/**
* Computed signal that reactively tracks if virtual background is supported.
* Updates automatically when browser support changes.
*/
readonly isVirtualBackgroundSupported: Signal<boolean> = computed(() =>
this.backgroundService.isVirtualBackgroundSupported()
);
ngOnInit(): void { ngOnInit(): void {
this.subscribeToBackgroundSelected(); this.subscribeToBackgroundSelected();
this.backgrounds = this.backgroundService.getBackgrounds(); this.backgrounds = this.backgroundService.getBackgrounds();

View File

@ -25,7 +25,7 @@
<div class="prejoin-main"> <div class="prejoin-main">
<!-- Video Preview Section --> <!-- Video Preview Section -->
<div class="video-preview-section"> <div class="video-preview-section">
<div class="video-preview-container" [@containerResize]="showBackgroundPanel ? 'compact' : 'normal'"> <div class="video-preview-container" [class.compact]="showBackgroundPanel">
<div class="video-frame"> <div class="video-frame">
<ov-media-element <ov-media-element
[track]="videoTrack" [track]="videoTrack"

View File

@ -3,6 +3,27 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
// CSS Animations (replacing Angular animations)
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.prejoin-container { .prejoin-container {
min-height: 100vh; min-height: 100vh;
background: var(--ov-background-color); background: var(--ov-background-color);
@ -89,6 +110,13 @@
border-radius: var(--ov-surface-radius) var(--ov-surface-radius) 0 0; border-radius: var(--ov-surface-radius) var(--ov-surface-radius) 0 0;
overflow: hidden; overflow: hidden;
background: var(--ov-video-background, var(--ov-primary-action-color)); background: var(--ov-video-background, var(--ov-primary-action-color));
transition: height 250ms cubic-bezier(0.25, 0.8, 0.25, 1);
// Compact state when background panel is shown
&.compact {
height: 300px;
aspect-ratio: unset;
}
.video-frame { .video-frame {
width: 100%; width: 100%;
@ -168,6 +196,11 @@
.vb-container { .vb-container {
height: fit-content; height: fit-content;
overflow: hidden; overflow: hidden;
// Slide-in animation (replacing Angular @slideInOut)
&.slide-in {
animation: slideIn 300ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
} }
// Configuration Section // Configuration Section

View File

@ -9,18 +9,17 @@ import {
OnInit, OnInit,
Output Output
} from '@angular/core'; } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations'; import { LocalTrack, Track } from 'livekit-client';
import { filter, Subject, take, takeUntil } from 'rxjs'; import { filter, Subject, take, takeUntil } from 'rxjs';
import { CustomDevice } from '../../models/device.model';
import { LangOption } from '../../models/lang.model';
import { ILogger } from '../../models/logger.model'; import { ILogger } from '../../models/logger.model';
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service'; import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { LoggerService } from '../../services/logger/logger.service'; import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service'; import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { ViewportService } from '../../services/viewport/viewport.service';
import { TranslateService } from '../../services/translate/translate.service'; import { TranslateService } from '../../services/translate/translate.service';
import { LocalTrack, Track } from 'livekit-client'; import { ViewportService } from '../../services/viewport/viewport.service';
import { CustomDevice } from '../../models/device.model';
import { LangOption } from '../../models/lang.model';
/** /**
* @internal * @internal
@ -30,38 +29,7 @@ import { LangOption } from '../../models/lang.model';
templateUrl: './pre-join.component.html', templateUrl: './pre-join.component.html',
styleUrls: ['./pre-join.component.scss'], styleUrls: ['./pre-join.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false, standalone: false
animations: [
trigger('containerResize', [
state(
'normal',
style({
height: '*'
})
),
state(
'compact',
style({
height: '300px'
})
),
transition('normal => compact', [animate('250ms cubic-bezier(0.25, 0.8, 0.25, 1)')]),
transition('compact => normal', [animate('350ms cubic-bezier(0.25, 0.8, 0.25, 1)')])
]),
trigger('slideInOut', [
transition(':enter', [
style({
opacity: 0
}),
animate(
'300ms cubic-bezier(0.34, 1.56, 0.64, 1)',
style({
opacity: 1
})
)
])
])
]
}) })
export class PreJoinComponent implements OnInit, OnDestroy { export class PreJoinComponent implements OnInit, OnDestroy {
@Input() set error(error: { name: string; message: string } | undefined) { @Input() set error(error: { name: string; message: string } | undefined) {

View File

@ -1,18 +1,18 @@
<div class="audio-device-selector" [class.compact]="compact"> <div class="audio-device-selector" [class.compact]="compact">
<!-- Unified Device Button (Compact Mode) --> <!-- Unified Device Button (Compact Mode) -->
@if (compact) { @if (compact) {
@if (hasAudioDevices) { @if (hasAudioDevices()) {
<div class="unified-device-button"> <div class="unified-device-button">
<!-- Main toggle button --> <!-- Main toggle button -->
<button <button
mat-flat-button mat-flat-button
class="toggle-section" class="toggle-section"
[disabled]="!hasAudioDevices || microphoneStatusChanging" [disabled]="!hasAudioDevices() || microphoneStatusChanging"
[class.device-enabled]="isMicrophoneEnabled" [class.device-enabled]="isMicrophoneEnabled"
[class.device-disabled]="!isMicrophoneEnabled" [class.device-disabled]="!isMicrophoneEnabled"
(click)="toggleMic($event)" (click)="toggleMic($event)"
[matTooltip]="isMicrophoneEnabled ? ('TOOLBAR.MUTE_AUDIO' | translate) : ('TOOLBAR.UNMUTE_AUDIO' | translate)" [matTooltip]="isMicrophoneEnabled ? ('TOOLBAR.MUTE_AUDIO' | translate) : ('TOOLBAR.UNMUTE_AUDIO' | translate)"
[matTooltipDisabled]="!hasAudioDevices" [matTooltipDisabled]="!hasAudioDevices()"
id="microphone-button" id="microphone-button"
> >
<mat-icon [id]="isMicrophoneEnabled ? 'mic' : 'mic_off'">{{ isMicrophoneEnabled ? 'mic' : 'mic_off' }}</mat-icon> <mat-icon [id]="isMicrophoneEnabled ? 'mic' : 'mic_off'">{{ isMicrophoneEnabled ? 'mic' : 'mic_off' }}</mat-icon>
@ -42,7 +42,7 @@
<!-- Normal Mode - Input Style Selector --> <!-- Normal Mode - Input Style Selector -->
<div class="normal-device-selector"> <div class="normal-device-selector">
<!-- Input-style Device Selector --> <!-- Input-style Device Selector -->
<div class="device-input-selector" [class.disabled]="!hasAudioDevices || !isMicrophoneEnabled"> <div class="device-input-selector" [class.disabled]="!hasAudioDevices() || !isMicrophoneEnabled">
<!-- When microphone is enabled --> <!-- When microphone is enabled -->
@if (isMicrophoneEnabled) { @if (isMicrophoneEnabled) {
<div class="device-input-selector"> <div class="device-input-selector">
@ -50,23 +50,23 @@
mat-flat-button mat-flat-button
id="audio-dropdown" id="audio-dropdown"
class="selector-button" class="selector-button"
[disabled]="microphoneStatusChanging || microphones.length <= 1" [disabled]="microphoneStatusChanging || microphones().length <= 1"
[matMenuTriggerFor]="microphoneMenu" [matMenuTriggerFor]="microphoneMenu"
[attr.aria-expanded]="false" [attr.aria-expanded]="false"
> >
<mat-icon class="device-icon">mic</mat-icon> <mat-icon class="device-icon">mic</mat-icon>
<span class="selected-device-name">{{ microphoneSelected?.label || 'No microphone selected' }}</span> <span class="selected-device-name">{{ microphoneSelected()?.label || 'No microphone selected' }}</span>
<mat-icon class="dropdown-icon" *ngIf="microphones.length > 1">expand_more</mat-icon> <mat-icon class="dropdown-icon" *ngIf="microphones().length > 1">expand_more</mat-icon>
</button> </button>
</div> </div>
} @else { } @else {
@if (hasAudioDevices) { @if (hasAudioDevices()) {
<!-- When microphone is disabled --> <!-- When microphone is disabled -->
<div class="device-input-selector disabled"> <div class="device-input-selector disabled">
<div class="selector-button disabled"> <div class="selector-button disabled">
<mat-icon class="device-icon">mic_off</mat-icon> <mat-icon class="device-icon">mic_off</mat-icon>
<span class="selected-device-name"> <span class="selected-device-name">
{{ !hasAudioDevices ? ('PREJOIN.NO_AUDIO_DEVICE' | translate) : 'Microphone disabled' }} {{ !hasAudioDevices() ? ('PREJOIN.NO_AUDIO_DEVICE' | translate) : 'Microphone disabled' }}
</span> </span>
</div> </div>
</div> </div>
@ -84,14 +84,14 @@
<!-- Device Selection Menu (Shared) --> <!-- Device Selection Menu (Shared) -->
<mat-menu #microphoneMenu="matMenu" class="device-menu"> <mat-menu #microphoneMenu="matMenu" class="device-menu">
@for (microphone of microphones; track microphone.device) { @for (microphone of microphones(); track microphone.device) {
<button <button
mat-menu-item mat-menu-item
id="option-{{ microphone.label }}" id="option-{{ microphone.label }}"
(click)="onMicrophoneSelected({ value: microphone })" (click)="onMicrophoneSelected({ value: microphone })"
[class.selected]="microphone.device === microphoneSelected.device" [class.selected]="microphone.device === microphoneSelected()?.device"
> >
<mat-icon *ngIf="microphone.device === microphoneSelected.device">check</mat-icon> <mat-icon *ngIf="microphone.device === microphoneSelected()?.device">check</mat-icon>
<span>{{ microphone.label }}</span> <span>{{ microphone.label }}</span>
</button> </button>
} }

View File

@ -3,6 +3,7 @@
:host { :host {
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%;
.audio-device-selector { .audio-device-selector {
@include shared.device-selector-base(); @include shared.device-selector-base();

View File

@ -1,12 +1,10 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Component, effect, EventEmitter, Input, OnInit, Output, Signal, WritableSignal } from '@angular/core';
import { Subscription } from 'rxjs';
import { CustomDevice } from '../../../models/device.model'; import { CustomDevice } from '../../../models/device.model';
import { ILogger } from '../../../models/logger.model';
import { DeviceService } from '../../../services/device/device.service'; import { DeviceService } from '../../../services/device/device.service';
import { LoggerService } from '../../../services/logger/logger.service';
import { ParticipantService } from '../../../services/participant/participant.service'; import { ParticipantService } from '../../../services/participant/participant.service';
import { StorageService } from '../../../services/storage/storage.service'; import { StorageService } from '../../../services/storage/storage.service';
import { ParticipantModel } from '../../../models/participant.model';
import { LoggerService } from '../../../services/logger/logger.service';
import { ILogger } from '../../../models/logger.model';
/** /**
* @internal * @internal
@ -17,19 +15,20 @@ import { ILogger } from '../../../models/logger.model';
styleUrls: ['./audio-devices.component.scss'], styleUrls: ['./audio-devices.component.scss'],
standalone: false standalone: false
}) })
export class AudioDevicesComponent implements OnInit, OnDestroy { export class AudioDevicesComponent implements OnInit {
@Input() compact: boolean = false; @Input() compact: boolean = false;
@Output() onAudioDeviceChanged = new EventEmitter<CustomDevice>(); @Output() onAudioDeviceChanged = new EventEmitter<CustomDevice>();
@Output() onAudioEnabledChanged = new EventEmitter<boolean>(); @Output() onAudioEnabledChanged = new EventEmitter<boolean>();
microphoneStatusChanging: boolean; microphoneStatusChanging: boolean = false;
hasAudioDevices: boolean; isMicrophoneEnabled: boolean = false;
isMicrophoneEnabled: boolean;
microphoneSelected: CustomDevice | undefined;
microphones: CustomDevice[] = [];
private localParticipantSubscription: Subscription;
private log: ILogger; private log: ILogger;
// Expose signals directly from service (reactive)
protected readonly microphones: WritableSignal<CustomDevice[]>;
protected readonly microphoneSelected: WritableSignal<CustomDevice | undefined>;
protected readonly hasAudioDevices: Signal<boolean>;
constructor( constructor(
private deviceSrv: DeviceService, private deviceSrv: DeviceService,
private storageSrv: StorageService, private storageSrv: StorageService,
@ -37,24 +36,24 @@ export class AudioDevicesComponent implements OnInit, OnDestroy {
private loggerSrv: LoggerService private loggerSrv: LoggerService
) { ) {
this.log = this.loggerSrv.get('AudioDevicesComponent'); this.log = this.loggerSrv.get('AudioDevicesComponent');
this.microphones = this.deviceSrv.microphones;
this.microphoneSelected = this.deviceSrv.microphoneSelected;
this.hasAudioDevices = this.deviceSrv.hasAudioDevices;
// Use effect instead of subscription for reactive updates
effect(() => {
const participant = this.participantService.localParticipantSignal();
if (participant) {
this.isMicrophoneEnabled = participant.isMicrophoneEnabled;
this.storageSrv.setMicrophoneEnabled(this.isMicrophoneEnabled);
}
});
} }
async ngOnInit() { async ngOnInit() {
this.subscribeToParticipantMediaProperties();
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
if (this.hasAudioDevices) {
this.microphones = this.deviceSrv.getMicrophones();
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
}
this.isMicrophoneEnabled = this.participantService.isMyMicrophoneEnabled(); this.isMicrophoneEnabled = this.participantService.isMyMicrophoneEnabled();
} }
ngOnDestroy() {
this.microphones = [];
if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe();
}
async toggleMic(event: any) { async toggleMic(event: any) {
event.stopPropagation(); event.stopPropagation();
this.microphoneStatusChanging = true; this.microphoneStatusChanging = true;
@ -72,8 +71,7 @@ export class AudioDevicesComponent implements OnInit, OnDestroy {
this.microphoneStatusChanging = true; this.microphoneStatusChanging = true;
await this.participantService.switchMicrophone(device.device); await this.participantService.switchMicrophone(device.device);
this.deviceSrv.setMicSelected(device.device); this.deviceSrv.setMicSelected(device.device);
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); this.onAudioDeviceChanged.emit(this.microphoneSelected());
this.onAudioDeviceChanged.emit(this.microphoneSelected);
} }
} catch (error) { } catch (error) {
this.log.e('Error switching microphone', error); this.log.e('Error switching microphone', error);
@ -89,17 +87,4 @@ export class AudioDevicesComponent implements OnInit, OnDestroy {
compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean { compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean {
return o1.label === o2.label; return o1.label === o2.label;
} }
/**
* This subscription is necessary to update the microphone status when the user changes it from toolbar and
* the settings panel is opened. With this, the microphone status is updated in the settings panel.
*/
private subscribeToParticipantMediaProperties() {
this.localParticipantSubscription = this.participantService.localParticipant$.subscribe((p: ParticipantModel | undefined) => {
if (p) {
this.isMicrophoneEnabled = p.isMicrophoneEnabled;
this.storageSrv.setMicrophoneEnabled(this.isMicrophoneEnabled);
}
});
}
} }

View File

@ -179,13 +179,14 @@
align-items: center; align-items: center;
gap: 6px; gap: 6px;
padding: 8px 12px; padding: 8px 12px;
background: rgba(255, 193, 7, 0.1); background: var(--ov-primary-action-color);
border: 1px solid rgba(255, 193, 7, 0.3); border: 1px solid var(--ov-warning-color, #ff9800);
border-radius: 8px; border-radius: 8px;
color: var(--ov-warning-color, #ff9800);
font-size: 12px; font-size: 12px;
color: var(--ov-text-primary-color);
.warning-icon { .warning-icon {
color: var(--ov-warning-color, #ff9800);
font-size: 16px; font-size: 16px;
width: 16px; width: 16px;
height: 16px; height: 16px;

View File

@ -1,18 +1,18 @@
<div class="video-device-selector" [class.compact]="compact"> <div class="video-device-selector" [class.compact]="compact">
<!-- Unified Device Button (Compact Mode) --> <!-- Unified Device Button (Compact Mode) -->
@if (compact) { @if (compact) {
@if (hasVideoDevices) { @if (hasVideoDevices()) {
<div class="unified-device-button"> <div class="unified-device-button">
<!-- Main toggle button --> <!-- Main toggle button -->
<button <button
mat-flat-button mat-flat-button
class="toggle-section" class="toggle-section"
[disabled]="!hasVideoDevices || cameraStatusChanging" [disabled]="!hasVideoDevices() || cameraStatusChanging"
[class.device-enabled]="isCameraEnabled" [class.device-enabled]="isCameraEnabled"
[class.device-disabled]="!isCameraEnabled" [class.device-disabled]="!isCameraEnabled"
(click)="toggleCam($event)" (click)="toggleCam($event)"
[matTooltip]="isCameraEnabled ? ('TOOLBAR.STOP_VIDEO' | translate) : ('TOOLBAR.START_VIDEO' | translate)" [matTooltip]="isCameraEnabled ? ('TOOLBAR.STOP_VIDEO' | translate) : ('TOOLBAR.START_VIDEO' | translate)"
[matTooltipDisabled]="!hasVideoDevices" [matTooltipDisabled]="!hasVideoDevices()"
id="camera-button" id="camera-button"
> >
<mat-icon [id]="isCameraEnabled ? 'videocam' : 'videocam_off'"> <mat-icon [id]="isCameraEnabled ? 'videocam' : 'videocam_off'">
@ -51,15 +51,15 @@
id="video-dropdown" id="video-dropdown"
class="selector-button" class="selector-button"
[matMenuTriggerFor]="cameraMenu" [matMenuTriggerFor]="cameraMenu"
[disabled]="cameraStatusChanging || cameras.length <= 1" [disabled]="cameraStatusChanging || cameras().length <= 1"
> >
<mat-icon class="device-icon">videocam</mat-icon> <mat-icon class="device-icon">videocam</mat-icon>
<span class="selected-device-name">{{ cameraSelected?.label || 'No camera selected' }}</span> <span class="selected-device-name">{{ cameraSelected()?.label || 'No camera selected' }}</span>
<mat-icon class="dropdown-icon" *ngIf="cameras.length > 1">expand_more</mat-icon> <mat-icon class="dropdown-icon" *ngIf="cameras().length > 1">expand_more</mat-icon>
</button> </button>
</div> </div>
} @else { } @else {
@if (hasVideoDevices) { @if (hasVideoDevices()) {
<!-- Disabled state message --> <!-- Disabled state message -->
<div class="device-input-selector disabled"> <div class="device-input-selector disabled">
<div class="selector-button disabled"> <div class="selector-button disabled">
@ -80,14 +80,14 @@
<!-- Device Selection Menu (Shared) --> <!-- Device Selection Menu (Shared) -->
<mat-menu #cameraMenu="matMenu" class="device-menu"> <mat-menu #cameraMenu="matMenu" class="device-menu">
@for (camera of cameras; track camera.device) { @for (camera of cameras(); track camera.device) {
<button <button
mat-menu-item mat-menu-item
id="option-{{ camera.label }}" id="option-{{ camera.label }}"
(click)="onCameraSelected({ value: camera })" (click)="onCameraSelected({ value: camera })"
[class.selected]="camera.device === cameraSelected?.device" [class.selected]="camera.device === cameraSelected()?.device"
> >
<mat-icon *ngIf="camera.device === cameraSelected?.device" class="check-icon">check</mat-icon> <mat-icon *ngIf="camera.device === cameraSelected()?.device" class="check-icon">check</mat-icon>
<span>{{ camera.label }}</span> <span>{{ camera.label }}</span>
</button> </button>
} }

View File

@ -3,6 +3,7 @@
:host { :host {
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%;
.video-device-selector { .video-device-selector {
@include shared.device-selector-base(); @include shared.device-selector-base();

View File

@ -1,12 +1,10 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Component, effect, EventEmitter, Input, OnInit, Output, Signal, WritableSignal } from '@angular/core';
import { Subscription } from 'rxjs';
import { CustomDevice } from '../../../models/device.model'; import { CustomDevice } from '../../../models/device.model';
import { ILogger } from '../../../models/logger.model';
import { DeviceService } from '../../../services/device/device.service'; import { DeviceService } from '../../../services/device/device.service';
import { LoggerService } from '../../../services/logger/logger.service';
import { ParticipantService } from '../../../services/participant/participant.service'; import { ParticipantService } from '../../../services/participant/participant.service';
import { StorageService } from '../../../services/storage/storage.service'; import { StorageService } from '../../../services/storage/storage.service';
import { ParticipantModel } from '../../../models/participant.model';
import { LoggerService } from '../../../services/logger/logger.service';
import { ILogger } from '../../../models/logger.model';
/** /**
* @internal * @internal
@ -17,18 +15,18 @@ import { ILogger } from '../../../models/logger.model';
styleUrls: ['./video-devices.component.scss'], styleUrls: ['./video-devices.component.scss'],
standalone: false standalone: false
}) })
export class VideoDevicesComponent implements OnInit, OnDestroy { export class VideoDevicesComponent implements OnInit {
@Input() compact: boolean = false; @Input() compact: boolean = false;
@Output() onVideoDeviceChanged = new EventEmitter<CustomDevice>(); @Output() onVideoDeviceChanged = new EventEmitter<CustomDevice>();
@Output() onVideoEnabledChanged = new EventEmitter<boolean>(); @Output() onVideoEnabledChanged = new EventEmitter<boolean>();
@Output() onVideoDevicesLoaded = new EventEmitter<CustomDevice[]>(); @Output() onVideoDevicesLoaded = new EventEmitter<CustomDevice[]>();
cameraStatusChanging: boolean; cameraStatusChanging: boolean = false;
isCameraEnabled: boolean; isCameraEnabled: boolean = false;
cameraSelected: CustomDevice | undefined;
hasVideoDevices: boolean; protected readonly cameras: WritableSignal<CustomDevice[]>;
cameras: CustomDevice[] = []; protected readonly cameraSelected: WritableSignal<CustomDevice | undefined>;
localParticipantSubscription: Subscription; protected readonly hasVideoDevices: Signal<boolean>;
private log: ILogger; private log: ILogger;
@ -39,26 +37,26 @@ export class VideoDevicesComponent implements OnInit, OnDestroy {
private loggerSrv: LoggerService private loggerSrv: LoggerService
) { ) {
this.log = this.loggerSrv.get('VideoDevicesComponent'); this.log = this.loggerSrv.get('VideoDevicesComponent');
this.cameras = this.deviceSrv.cameras;
this.cameraSelected = this.deviceSrv.cameraSelected;
this.hasVideoDevices = this.deviceSrv.hasVideoDevices;
// Use effect instead of subscription for reactive updates
effect(() => {
const participant = this.participantService.localParticipantSignal();
if (participant) {
this.isCameraEnabled = participant.isCameraEnabled;
this.storageSrv.setCameraEnabled(this.isCameraEnabled);
}
});
} }
async ngOnInit() { async ngOnInit() {
this.subscribeToParticipantMediaProperties(); // Emit initial device list (reactively)
this.onVideoDevicesLoaded.emit(this.cameras());
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
if (this.hasVideoDevices) {
this.cameras = this.deviceSrv.getCameras();
this.cameraSelected = this.deviceSrv.getCameraSelected();
}
this.onVideoDevicesLoaded.emit(this.cameras);
this.isCameraEnabled = this.participantService.isMyCameraEnabled(); this.isCameraEnabled = this.participantService.isMyCameraEnabled();
} }
async ngOnDestroy() {
this.cameras = [];
if (this.localParticipantSubscription) this.localParticipantSubscription.unsubscribe();
}
async toggleCam(event: any) { async toggleCam(event: any) {
event.stopPropagation(); event.stopPropagation();
this.cameraStatusChanging = true; this.cameraStatusChanging = true;
@ -75,14 +73,10 @@ export class VideoDevicesComponent implements OnInit, OnDestroy {
// Is New deviceId different from the old one? // Is New deviceId different from the old one?
if (this.deviceSrv.needUpdateVideoTrack(device)) { if (this.deviceSrv.needUpdateVideoTrack(device)) {
this.cameraStatusChanging = true; this.cameraStatusChanging = true;
await this.participantService.switchCamera(device.device); await this.participantService.switchCamera(device.device);
this.deviceSrv.setCameraSelected(device.device); this.deviceSrv.setCameraSelected(device.device);
this.cameraSelected = device; this.onVideoDeviceChanged.emit(this.cameraSelected());
this.onVideoDeviceChanged.emit(this.cameraSelected);
} }
} catch (error) { } catch (error) {
this.log.e('Error switching camera', error); this.log.e('Error switching camera', error);
@ -98,17 +92,4 @@ export class VideoDevicesComponent implements OnInit, OnDestroy {
compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean { compareObjectDevices(o1: CustomDevice, o2: CustomDevice): boolean {
return o1.label === o2.label; return o1.label === o2.label;
} }
/**
* This subscription is necessary to update the camera status when the user changes it from toolbar and
* the settings panel is opened. With this, the camera status is updated in the settings panel.
*/
private subscribeToParticipantMediaProperties() {
this.localParticipantSubscription = this.participantService.localParticipant$.subscribe((p: ParticipantModel | undefined) => {
if (p) {
this.isCameraEnabled = p.isCameraEnabled;
this.storageSrv.setCameraEnabled(this.isCameraEnabled);
}
});
}
} }

View File

@ -436,7 +436,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
private panelService: PanelService, private panelService: PanelService,
private participantService: ParticipantService, private participantService: ParticipantService,
private openviduService: OpenViduService, private openviduService: OpenViduService,
private oVDevicesService: DeviceService, private deviceService: DeviceService,
private actionService: ActionService, private actionService: ActionService,
private loggerSrv: LoggerService, private loggerSrv: LoggerService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
@ -517,8 +517,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.room = this.openviduService.getRoom(); this.room = this.openviduService.getRoom();
this.evalAndSetRoomName(this.libService.getRoomName()); this.evalAndSetRoomName(this.libService.getRoomName());
this.hasVideoDevices = this.oVDevicesService.hasVideoDeviceAvailable(); this.hasVideoDevices = this.deviceService.hasVideoDeviceAvailable();
this.hasAudioDevices = this.oVDevicesService.hasAudioDeviceAvailable(); this.hasAudioDevices = this.deviceService.hasAudioDeviceAvailable();
this.setupTemplates(); this.setupTemplates();
this.subscribeToToolbarDirectives(); this.subscribeToToolbarDirectives();

View File

@ -30,6 +30,14 @@
<mat-icon class="error-icon">error</mat-icon> <mat-icon class="error-icon">error</mat-icon>
<span>{{ componentState.error?.message }}</span> <span>{{ componentState.error?.message }}</span>
</div> </div>
} @else if (componentState.state === 'DISCONNECTED') {
<!-- Disconnected state -->
<div id="spinner">
<mat-icon class="info-icon">check_circle</mat-icon>
<span>{{ 'ROOM.DISCONNECTED' | translate }}</span>
<p class="subtitle">{{ 'ROOM.DISCONNECTED_SUBTITLE' | translate }}</p>
</div>
} @else if (componentState.isRoomReady) { } @else if (componentState.isRoomReady) {
<!-- VideoConference --> <!-- VideoConference -->

View File

@ -1,8 +1,6 @@
import { animate, style, transition, trigger } from '@angular/animations'; import { animate, style, transition, trigger } from '@angular/animations';
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
ContentChild, ContentChild,
EventEmitter, EventEmitter,
@ -11,7 +9,16 @@ import {
TemplateRef, TemplateRef,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { Room } from 'livekit-client';
import { Subject, filter, skip, take, takeUntil } from 'rxjs'; import { Subject, filter, skip, take, takeUntil } from 'rxjs';
import {
LayoutAdditionalElementsDirective,
LeaveButtonDirective,
ParticipantPanelAfterLocalParticipantDirective,
PreJoinDirective,
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
import { import {
ActivitiesPanelDirective, ActivitiesPanelDirective,
AdditionalPanelsDirective, AdditionalPanelsDirective,
@ -26,29 +33,17 @@ import {
ToolbarAdditionalPanelButtonsDirective, ToolbarAdditionalPanelButtonsDirective,
ToolbarDirective ToolbarDirective
} from '../../directives/template/openvidu-components-angular.directive'; } from '../../directives/template/openvidu-components-angular.directive';
import { ILogger } from '../../models/logger.model'; import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model';
import { VideoconferenceState, VideoconferenceStateInfo } from '../../models/videoconference-state.model';
import { ActionService } from '../../services/action/action.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { DeviceService } from '../../services/device/device.service';
import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { StorageService } from '../../services/storage/storage.service';
import {
TemplateManagerService,
TemplateConfiguration,
ExternalDirectives,
DefaultTemplates
} from '../../services/template/template-manager.service';
import { Room } from 'livekit-client';
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
import { CustomDevice } from '../../models/device.model'; import { CustomDevice } from '../../models/device.model';
import { LangOption } from '../../models/lang.model';
import { ILogger } from '../../models/logger.model';
import { import {
ActivitiesPanelStatusEvent, ActivitiesPanelStatusEvent,
ChatPanelStatusEvent, ChatPanelStatusEvent,
ParticipantsPanelStatusEvent, ParticipantsPanelStatusEvent,
SettingsPanelStatusEvent SettingsPanelStatusEvent
} from '../../models/panel.model'; } from '../../models/panel.model';
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
RecordingDownloadClickedEvent, RecordingDownloadClickedEvent,
@ -56,18 +51,21 @@ import {
RecordingStartRequestedEvent, RecordingStartRequestedEvent,
RecordingStopRequestedEvent RecordingStopRequestedEvent
} from '../../models/recording.model'; } from '../../models/recording.model';
import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model'; import { VideoconferenceState, VideoconferenceStateInfo } from '../../models/videoconference-state.model';
import { LangOption } from '../../models/lang.model'; import { ActionService } from '../../services/action/action.service';
import { import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
LayoutAdditionalElementsDirective, import { DeviceService } from '../../services/device/device.service';
ParticipantPanelAfterLocalParticipantDirective,
PreJoinDirective,
LeaveButtonDirective,
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
import { OpenViduThemeService } from '../../services/theme/theme.service';
import { E2eeService } from '../../services/e2ee/e2ee.service'; import { E2eeService } from '../../services/e2ee/e2ee.service';
import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { StorageService } from '../../services/storage/storage.service';
import {
DefaultTemplates,
ExternalDirectives,
TemplateConfiguration,
TemplateManagerService
} from '../../services/template/template-manager.service';
import { OpenViduThemeService } from '../../services/theme/theme.service';
/** /**
* The **VideoconferenceComponent** is the parent of all OpenVidu components. * The **VideoconferenceComponent** is the parent of all OpenVidu components.
@ -982,14 +980,16 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
* @internal * @internal
*/ */
_onParticipantLeft(event: ParticipantLeftEvent) { _onParticipantLeft(event: ParticipantLeftEvent) {
// Reset to disconnected state to allow prejoin to show again if needed this.onParticipantLeft.emit(event);
// Reset to disconnected state
// Set showPrejoin to false to prevent prejoin from showing and creating tracks
// This avoids the race condition where tracks are created before navigation
this.updateComponentState({ this.updateComponentState({
state: VideoconferenceState.DISCONNECTED, state: VideoconferenceState.DISCONNECTED,
isRoomReady: false, isRoomReady: false,
showPrejoin: this.libService.showPrejoin() showPrejoin: false
}); });
this.onParticipantLeft.emit(event);
} }
private subscribeToVideconferenceDirectives() { private subscribeToVideconferenceDirectives() {

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "正在加入房间...", "JOINING": "正在加入房间...",
"LANDSCAPE_WARNING": "请将设备旋转到纵向以获得最佳体验" "LANDSCAPE_WARNING": "请将设备旋转到纵向以获得最佳体验",
"DISCONNECTED": "您已离开房间",
"DISCONNECTED_SUBTITLE": "会话已结束"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "设置你的绰号", "NICKNAME_SECTION": "设置你的绰号",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "没有效果和模糊的背景", "BLURRED_SECTION": "没有效果和模糊的背景",
"NO_EFFECTS": "没有背景效果", "NO_EFFECTS": "没有背景效果",
"BLURRED_EFFECT": "模糊的背景", "BLURRED_EFFECT": "模糊的背景",
"IMAGES_SECTION": "背景图像" "IMAGES_SECTION": "背景图像",
"NOT_SUPPORTED": "此浏览器不支持虚拟背景",
"NOT_SUPPORTED_DESCRIPTION": "您的浏览器不支持背景效果。此功能需要 GPU 加速,可能已被禁用或不可用。"
}, },
"RECORDING": { "RECORDING": {
"TITLE": "录音", "TITLE": "录音",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Raum beitreten...", "JOINING": "Raum beitreten...",
"LANDSCAPE_WARNING": "Bitte drehen Sie Ihr Gerät für eine bessere Erfahrung in den Hochformatmodus" "LANDSCAPE_WARNING": "Bitte drehen Sie Ihr Gerät für eine bessere Erfahrung in den Hochformatmodus",
"DISCONNECTED": "Sie haben den Raum verlassen",
"DISCONNECTED_SUBTITLE": "Die Sitzung wurde beendet"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Legen Sie Ihren Spitznamen fest", "NICKNAME_SECTION": "Legen Sie Ihren Spitznamen fest",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Keine Effekte und unscharfer Hintergrund", "BLURRED_SECTION": "Keine Effekte und unscharfer Hintergrund",
"NO_EFFECTS": "Kein Hintergrundeffekt", "NO_EFFECTS": "Kein Hintergrundeffekt",
"BLURRED_EFFECT": "Unscharfer Hintergrund", "BLURRED_EFFECT": "Unscharfer Hintergrund",
"IMAGES_SECTION": "Hintergrundbilder" "IMAGES_SECTION": "Hintergrundbilder",
"NOT_SUPPORTED": "Virtuelle Hintergründe werden in diesem Browser nicht unterstützt",
"NOT_SUPPORTED_DESCRIPTION": "Ihr Browser unterstützt keine Hintergrundeffekte. Diese Funktion erfordert GPU-Beschleunigung, die möglicherweise deaktiviert oder nicht verfügbar ist."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Aufnahme", "TITLE": "Aufnahme",

View File

@ -35,7 +35,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Joining room...", "JOINING": "Joining room...",
"LANDSCAPE_WARNING": "Please rotate your device to portrait for a better experience" "LANDSCAPE_WARNING": "Please rotate your device to portrait for a better experience",
"DISCONNECTED": "You have left the room",
"DISCONNECTED_SUBTITLE": "The session has ended"
}, },
"TOOLBAR": { "TOOLBAR": {
"MUTE_AUDIO": "Mute your audio", "MUTE_AUDIO": "Mute your audio",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "No effects and blurred background", "BLURRED_SECTION": "No effects and blurred background",
"NO_EFFECTS": "No background effect", "NO_EFFECTS": "No background effect",
"BLURRED_EFFECT": "Blurred background", "BLURRED_EFFECT": "Blurred background",
"IMAGES_SECTION": "Background images" "IMAGES_SECTION": "Background images",
"NOT_SUPPORTED": "Virtual backgrounds are not supported in this browser",
"NOT_SUPPORTED_DESCRIPTION": "Your browser does not support background effects. This feature requires GPU acceleration which may be disabled or unavailable."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Recording", "TITLE": "Recording",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Uniéndose a la sala...", "JOINING": "Uniéndose a la sala...",
"LANDSCAPE_WARNING": "Por favor, gira tu dispositivo a modo retrato para una mejor experiencia" "LANDSCAPE_WARNING": "Por favor, gira tu dispositivo a modo retrato para una mejor experiencia",
"DISCONNECTED": "Has salido de la sala",
"DISCONNECTED_SUBTITLE": "La sesión ha finalizado"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Elige tu nombre", "NICKNAME_SECTION": "Elige tu nombre",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Sin efectos y fondo desenfocado", "BLURRED_SECTION": "Sin efectos y fondo desenfocado",
"NO_EFFECTS": "Sin efecto", "NO_EFFECTS": "Sin efecto",
"BLURRED_EFFECT": "Fondo desenfocado", "BLURRED_EFFECT": "Fondo desenfocado",
"IMAGES_SECTION": "Imágenes de fondo" "IMAGES_SECTION": "Imágenes de fondo",
"NOT_SUPPORTED": "Los efectos de fondo virtuales no son compatibles con este navegador",
"NOT_SUPPORTED_DESCRIPTION": "Tu navegador no admite efectos de fondo. Esta función requiere aceleración GPU que puede estar deshabilitada o no disponible."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Grabación", "TITLE": "Grabación",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Rejoindre la salle...", "JOINING": "Rejoindre la salle...",
"LANDSCAPE_WARNING": "Veuillez faire pivoter votre appareil en mode portrait pour une meilleure expérience" "LANDSCAPE_WARNING": "Veuillez faire pivoter votre appareil en mode portrait pour une meilleure expérience",
"DISCONNECTED": "Vous avez quitté la salle",
"DISCONNECTED_SUBTITLE": "La session est terminée"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Définir votre surnom", "NICKNAME_SECTION": "Définir votre surnom",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Aucun effet et arrière-plan flou", "BLURRED_SECTION": "Aucun effet et arrière-plan flou",
"NO_EFFECTS": "Aucun effet de fond", "NO_EFFECTS": "Aucun effet de fond",
"BLURRED_EFFECT": "Arrière-plan flou", "BLURRED_EFFECT": "Arrière-plan flou",
"IMAGES_SECTION": "Images d'arrière-plan" "IMAGES_SECTION": "Images d'arrière-plan",
"NOT_SUPPORTED": "Les arrière-plans virtuels ne sont pas pris en charge dans ce navigateur",
"NOT_SUPPORTED_DESCRIPTION": "Votre navigateur ne prend pas en charge les effets d'arrière-plan. Cette fonctionnalité nécessite l'accélération GPU qui peut être désactivée ou non disponible."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Enregistrement", "TITLE": "Enregistrement",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "कक्ष में शामिल हो रहा है...", "JOINING": "कक्ष में शामिल हो रहा है...",
"LANDSCAPE_WARNING": "कृपया बेहतर अनुभव के लिए अपने डिवाइस को पोर्ट्रेट मोड में घुमाएँ" "LANDSCAPE_WARNING": "कृपया बेहतर अनुभव के लिए अपने डिवाइस को पोर्ट्रेट मोड में घुमाएं",
"DISCONNECTED": "आपने कमरा छोड़ दिया है",
"DISCONNECTED_SUBTITLE": "सत्र समाप्त हो गया है"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "अपना निकनेम सेट करें", "NICKNAME_SECTION": "अपना निकनेम सेट करें",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "कोई प्रभाव नहीं है और पृष्ठभूमि धुंधली है", "BLURRED_SECTION": "कोई प्रभाव नहीं है और पृष्ठभूमि धुंधली है",
"NO_EFFECTS": "कोई पृष्ठभूमि प्रभाव नहीं है", "NO_EFFECTS": "कोई पृष्ठभूमि प्रभाव नहीं है",
"BLURRED_EFFECT": "पृष्ठभूमि धुंधली है", "BLURRED_EFFECT": "पृष्ठभूमि धुंधली है",
"IMAGES_SECTION": "पृष्ठभूमि छवियां" "IMAGES_SECTION": "पृष्ठभूमि छवियां",
"NOT_SUPPORTED": "इस ब्राउज़र में वर्चुअल बैकग्राउंड समर्थित नहीं है",
"NOT_SUPPORTED_DESCRIPTION": "आपका ब्राउज़र बैकग्राउंड इफेक्ट्स का समर्थन नहीं करता है। इस सुविधा के लिए GPU त्वरण की आवश्यकता है जो अक्षम या अनुपलब्ध हो सकता है।"
}, },
"RECORDING": { "RECORDING": {
"TITLE": "रिकॉर्डिंग", "TITLE": "रिकॉर्डिंग",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Unendosi alla stanza...", "JOINING": "Unendosi alla stanza...",
"LANDSCAPE_WARNING": "Per una migliore esperienza, ruota il dispositivo in modalità verticale" "LANDSCAPE_WARNING": "Per una migliore esperienza, ruota il dispositivo in modalità verticale",
"DISCONNECTED": "Hai lasciato la stanza",
"DISCONNECTED_SUBTITLE": "La sessione è terminata"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Imposta il tuo soprannome", "NICKNAME_SECTION": "Imposta il tuo soprannome",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Nessun effetto e sfondo sfocato", "BLURRED_SECTION": "Nessun effetto e sfondo sfocato",
"NO_EFFECTS": "Nessun effetto di sfondo", "NO_EFFECTS": "Nessun effetto di sfondo",
"BLURRED_EFFECT": "Sfondo sfocato", "BLURRED_EFFECT": "Sfondo sfocato",
"IMAGES_SECTION": "Immagini di sfondo" "IMAGES_SECTION": "Immagini di sfondo",
"NOT_SUPPORTED": "Gli sfondi virtuali non sono supportati in questo browser",
"NOT_SUPPORTED_DESCRIPTION": "Il tuo browser non supporta gli effetti di sfondo. Questa funzione richiede l'accelerazione GPU che potrebbe essere disabilitata o non disponibile."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Registrazione", "TITLE": "Registrazione",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "ルームに参加しています...", "JOINING": "ルームに参加しています...",
"LANDSCAPE_WARNING": "より良い体験のために、デバイスを縦向きに回転させてください" "LANDSCAPE_WARNING": "より良い体験のために、デバイスを縦向きに回転させてください",
"DISCONNECTED": "ルームから退出しました",
"DISCONNECTED_SUBTITLE": "セッションが終了しました"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "ニックネームを設定してください", "NICKNAME_SECTION": "ニックネームを設定してください",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "エフェクトなし、ぼやけた背景", "BLURRED_SECTION": "エフェクトなし、ぼやけた背景",
"NO_EFFECTS": "背景エフェクトなし", "NO_EFFECTS": "背景エフェクトなし",
"BLURRED_EFFECT": "ぼやけた背景", "BLURRED_EFFECT": "ぼやけた背景",
"IMAGES_SECTION": "背景画像" "IMAGES_SECTION": "背景画像",
"NOT_SUPPORTED": "このブラウザでは仮想背景はサポートされていません",
"NOT_SUPPORTED_DESCRIPTION": "お使いのブラウザは背景効果をサポートしていません。この機能にはGPUアクセラレーションが必要ですが、無効化されているか利用できません。"
}, },
"RECORDING": { "RECORDING": {
"TITLE": "レコーディング", "TITLE": "レコーディング",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "De ruimte betreden...", "JOINING": "De ruimte betreden...",
"LANDSCAPE_WARNING": "Draai uw apparaat voor een betere ervaring naar portretmodus" "LANDSCAPE_WARNING": "Draai uw apparaat voor een betere ervaring naar portretmodus",
"DISCONNECTED": "U heeft de kamer verlaten",
"DISCONNECTED_SUBTITLE": "De sessie is beëindigd"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Stel je bijnaam in", "NICKNAME_SECTION": "Stel je bijnaam in",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Geen effecten en onscherpe achtergrond", "BLURRED_SECTION": "Geen effecten en onscherpe achtergrond",
"NO_EFFECTS": "Geen achtergrondeffect", "NO_EFFECTS": "Geen achtergrondeffect",
"BLURRED_EFFECT": "Onscherpe achtergrond", "BLURRED_EFFECT": "Onscherpe achtergrond",
"IMAGES_SECTION": "Achtergrondafbeeldingen" "IMAGES_SECTION": "Achtergrondafbeeldingen",
"NOT_SUPPORTED": "Virtuele achtergronden worden niet ondersteund in deze browser",
"NOT_SUPPORTED_DESCRIPTION": "Uw browser ondersteunt geen achtergrondeffecten. Deze functie vereist GPU-versnelling die mogelijk uitgeschakeld of niet beschikbaar is."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Opname", "TITLE": "Opname",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Entrando na sala...", "JOINING": "Entrando na sala...",
"LANDSCAPE_WARNING": "Por favor, gire seu dispositivo para o modo retrato para uma melhor experiência" "LANDSCAPE_WARNING": "Por favor, gire seu dispositivo para o modo retrato para uma melhor experiência",
"DISCONNECTED": "Você saiu da sala",
"DISCONNECTED_SUBTITLE": "A sessão foi encerrada"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Defina seu apelido", "NICKNAME_SECTION": "Defina seu apelido",
@ -118,7 +120,9 @@
"BLURRED_SECTION": "Sem efeitos e fundo desfocado", "BLURRED_SECTION": "Sem efeitos e fundo desfocado",
"NO_EFFECTS": "Sem efeito de fundo", "NO_EFFECTS": "Sem efeito de fundo",
"BLURRED_EFFECT": "Fundo desfocado", "BLURRED_EFFECT": "Fundo desfocado",
"IMAGES_SECTION": "Imagens de fundo" "IMAGES_SECTION": "Imagens de fundo",
"NOT_SUPPORTED": "Fundos virtuais não são suportados neste navegador",
"NOT_SUPPORTED_DESCRIPTION": "Seu navegador não suporta efeitos de fundo. Este recurso requer aceleração de GPU que pode estar desabilitada ou não disponível."
}, },
"RECORDING": { "RECORDING": {
"TITLE": "Gravação", "TITLE": "Gravação",

View File

@ -6,13 +6,13 @@ import { DeleteDialogComponent } from './components/dialogs/delete-recording.com
import { DialogTemplateComponent } from './components/dialogs/dialog.component'; import { DialogTemplateComponent } from './components/dialogs/dialog.component';
import { RecordingDialogComponent } from './components/dialogs/recording-dialog.component'; import { RecordingDialogComponent } from './components/dialogs/recording-dialog.component';
import { LayoutComponent } from './components/layout/layout.component'; import { LayoutComponent } from './components/layout/layout.component';
import { MediaElementComponent } from './components/media-element/media-element.component';
import { ChatPanelComponent } from './components/panel/chat-panel/chat-panel.component'; import { ChatPanelComponent } from './components/panel/chat-panel/chat-panel.component';
import { SessionComponent } from './components/session/session.component'; import { SessionComponent } from './components/session/session.component';
import { StreamComponent } from './components/stream/stream.component'; import { StreamComponent } from './components/stream/stream.component';
import { ToolbarComponent } from './components/toolbar/toolbar.component';
import { ToolbarMediaButtonsComponent } from './components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component'; import { ToolbarMediaButtonsComponent } from './components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component';
import { ToolbarPanelButtonsComponent } from './components/toolbar/toolbar-panel-buttons/toolbar-panel-buttons.component'; import { ToolbarPanelButtonsComponent } from './components/toolbar/toolbar-panel-buttons/toolbar-panel-buttons.component';
import { MediaElementComponent } from './components/media-element/media-element.component'; import { ToolbarComponent } from './components/toolbar/toolbar.component';
import { LinkifyPipe } from './pipes/linkify.pipe'; import { LinkifyPipe } from './pipes/linkify.pipe';
import { RemoteParticipantTracksPipe, TrackPublishedTypesPipe } from './pipes/participant.pipe'; import { RemoteParticipantTracksPipe, TrackPublishedTypesPipe } from './pipes/participant.pipe';
@ -39,15 +39,15 @@ import { BackgroundEffectsPanelComponent } from './components/panel/background-e
import { SettingsPanelComponent } from './components/panel/settings-panel/settings-panel.component'; import { SettingsPanelComponent } from './components/panel/settings-panel/settings-panel.component';
import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component'; import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component';
// import { CaptionsSettingComponent } from './components/settings/captions/captions.component'; // import { CaptionsSettingComponent } from './components/settings/captions/captions.component';
import { LandscapeWarningComponent } from './components/landscape-warning/landscape-warning.component';
import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component'; import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component';
import { ParticipantNameInputComponent } from './components/settings/participant-name-input/participant-name-input.component'; import { ParticipantNameInputComponent } from './components/settings/participant-name-input/participant-name-input.component';
import { ThemeSelectorComponent } from './components/settings/theme-selector/theme-selector.component';
import { VideoDevicesComponent } from './components/settings/video-devices/video-devices.component'; import { VideoDevicesComponent } from './components/settings/video-devices/video-devices.component';
import { VideoPosterComponent } from './components/video-poster/video-poster.component';
import { ApiDirectiveModule } from './directives/api/api.directive.module'; import { ApiDirectiveModule } from './directives/api/api.directive.module';
import { OpenViduComponentsDirectiveModule } from './directives/template/openvidu-components-angular.directive.module'; import { OpenViduComponentsDirectiveModule } from './directives/template/openvidu-components-angular.directive.module';
import { AppMaterialModule } from './openvidu-components-angular.material.module'; import { AppMaterialModule } from './openvidu-components-angular.material.module';
import { ThemeSelectorComponent } from './components/settings/theme-selector/theme-selector.component';
import { LandscapeWarningComponent } from './components/landscape-warning/landscape-warning.component';
import { VideoPosterComponent } from './components/video-poster/video-poster.component';
const publicComponents = [ const publicComponents = [
AdminDashboardComponent, AdminDashboardComponent,

View File

@ -1,6 +1,6 @@
import { OverlayContainer } from '@angular/cdk/overlay'; import { OverlayContainer } from '@angular/cdk/overlay';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { ModuleWithProviders, NgModule, Provider, EnvironmentProviders } from '@angular/core'; import { EnvironmentProviders, ModuleWithProviders, NgModule, Provider } from '@angular/core';
import { CdkOverlayContainer } from './config/custom-cdk-overlay'; import { CdkOverlayContainer } from './config/custom-cdk-overlay';
import { OpenViduComponentsConfig } from './config/openvidu-components-angular.config'; import { OpenViduComponentsConfig } from './config/openvidu-components-angular.config';
@ -8,7 +8,6 @@ import { ActionService } from './services/action/action.service';
import { ChatService } from './services/chat/chat.service'; import { ChatService } from './services/chat/chat.service';
import { DeviceService } from './services/device/device.service'; import { DeviceService } from './services/device/device.service';
import { DocumentService } from './services/document/document.service'; import { DocumentService } from './services/document/document.service';
import { LayoutService } from './services/layout/layout.service';
import { LoggerService } from './services/logger/logger.service'; import { LoggerService } from './services/logger/logger.service';
import { OpenViduService } from './services/openvidu/openvidu.service'; import { OpenViduService } from './services/openvidu/openvidu.service';
import { PanelService } from './services/panel/panel.service'; import { PanelService } from './services/panel/panel.service';
@ -17,13 +16,13 @@ import { PlatformService } from './services/platform/platform.service';
import { RecordingService } from './services/recording/recording.service'; import { RecordingService } from './services/recording/recording.service';
import { StorageService } from './services/storage/storage.service'; import { StorageService } from './services/storage/storage.service';
import { VirtualBackgroundService } from './services/virtual-background/virtual-background.service';
import { BroadcastingService } from './services/broadcasting/broadcasting.service';
import { GlobalConfigService } from './services/config/global-config.service';
import { OpenViduComponentsConfigService } from './services/config/directive-config.service';
import { OpenViduComponentsUiModule } from './openvidu-components-angular-ui.module'; import { OpenViduComponentsUiModule } from './openvidu-components-angular-ui.module';
import { ViewportService } from './services/viewport/viewport.service'; import { BroadcastingService } from './services/broadcasting/broadcasting.service';
import { OpenViduComponentsConfigService } from './services/config/directive-config.service';
import { GlobalConfigService } from './services/config/global-config.service';
import { E2eeService } from './services/e2ee/e2ee.service'; import { E2eeService } from './services/e2ee/e2ee.service';
import { ViewportService } from './services/viewport/viewport.service';
import { VirtualBackgroundService } from './services/virtual-background/virtual-background.service';
@NgModule({ @NgModule({
imports: [OpenViduComponentsUiModule], imports: [OpenViduComponentsUiModule],

View File

@ -1,87 +1,467 @@
import { Injectable } from '@angular/core'; import { computed, Injectable, OnDestroy, signal } from '@angular/core';
import { createLocalTracks, LocalTrack, Room, Track } from 'livekit-client';
import { CameraType, CustomDevice, DeviceType } from '../../models/device.model'; import { CameraType, CustomDevice, DeviceType } from '../../models/device.model';
import { ILogger } from '../../models/logger.model'; import { ILogger } from '../../models/logger.model';
import { LoggerService } from '../logger/logger.service'; import { LoggerService } from '../logger/logger.service';
import { PlatformService } from '../platform/platform.service'; import { PlatformService } from '../platform/platform.service';
import { StorageService } from '../storage/storage.service'; import { StorageService } from '../storage/storage.service';
import { LocalTrack, Room, createLocalTracks } from 'livekit-client';
/** /**
* Device availability state for each media type
*/
interface DeviceAvailabilityState {
hasDevices: boolean;
isEnabled: boolean;
permissionGranted: boolean;
error?: string;
}
/**
* Device service with improved performance and independent audio/video handling.
*
* Key improvements:
* - Smart permission requests (single prompt when possible, fallback to separate)
* - Angular Signals for reactive state management (cameras, microphones as signals)
* - Live device detection - automatically updates when devices are connected/disconnected
* - Better error handling with specific error types per device
* - Performance optimizations with caching
* - LiveKit client integration for modern track management
*
* @internal * @internal
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class DeviceService { export class DeviceService implements OnDestroy {
private devices: MediaDeviceInfo[]; // Reactive device lists with Signals
private cameras: CustomDevice[] = []; readonly cameras = signal<CustomDevice[]>([]);
private microphones: CustomDevice[] = []; readonly microphones = signal<CustomDevice[]>([]);
private cameraSelected?: CustomDevice; readonly cameraSelected = signal<CustomDevice | undefined>(undefined);
private microphoneSelected?: CustomDevice; readonly microphoneSelected = signal<CustomDevice | undefined>(undefined);
// Reactive state management with Signals
private readonly videoState = signal<DeviceAvailabilityState>({
hasDevices: false,
isEnabled: true,
permissionGranted: false
});
private readonly audioState = signal<DeviceAvailabilityState>({
hasDevices: false,
isEnabled: true,
permissionGranted: false
});
// Computed signals for common checks
readonly hasVideoDevices = computed(() =>
this.videoState().hasDevices && this.cameras().length > 0
);
readonly hasAudioDevices = computed(() =>
this.audioState().hasDevices && this.microphones().length > 0
);
readonly hasVideoPermission = computed(() =>
this.videoState().permissionGranted
);
readonly hasAudioPermission = computed(() =>
this.audioState().permissionGranted
);
readonly allPermissionsGranted = computed(() =>
this.videoState().permissionGranted && this.audioState().permissionGranted
);
// Constants
private readonly CACHE_DURATION = 5000; // 5 seconds
// Internal state
private devicesCache: {
timestamp: number;
devices: MediaDeviceInfo[];
} | null = null;
private log: ILogger; private log: ILogger;
private videoDevicesEnabled: boolean = true; private initializationPromise: Promise<void> | null = null;
private audioDevicesEnabled: boolean = true; private deviceChangeHandler: (() => void) | null = null;
private deviceAccessDeniedError: boolean = false;
constructor( constructor(
private loggerSrv: LoggerService, private loggerSrv: LoggerService,
private platformSrv: PlatformService, private platformSrv: PlatformService,
private storageSrv: StorageService private storageSrv: StorageService
) { ) {
this.log = this.loggerSrv.get('DevicesService'); this.log = this.loggerSrv.get('DeviceService');
} }
/** /**
* Initialize media devices and select a devices checking in local storage (if exists) or * Cleanup when service is destroyed
* first devices found by default
*/ */
async initializeDevices() { ngOnDestroy(): void {
// Remove device change listener
if (this.deviceChangeHandler && navigator.mediaDevices?.removeEventListener) {
navigator.mediaDevices.removeEventListener('devicechange', this.deviceChangeHandler);
this.deviceChangeHandler = null;
this.log.d('Device change detection disabled');
}
}
/**
* Initialize media devices with parallel audio/video handling
* Returns a promise that resolves when initialization is complete
*/
async initializeDevices(): Promise<void> {
// Prevent multiple simultaneous initializations
if (this.initializationPromise) {
return this.initializationPromise;
}
this.initializationPromise = this.performInitialization();
try {
await this.initializationPromise;
} finally {
this.initializationPromise = null;
}
}
private async performInitialization(): Promise<void> {
this.clear(); this.clear();
try { try {
this.devices = await this.getLocalDevices(); // Try to get devices with parallel audio/video permission requests
if (this.deviceAccessDeniedError) { const devices = await this.getLocalDevicesOptimized();
this.log.w('Media devices permissions were not granted.');
if (devices.length === 0) {
this.log.w('No media devices found or permissions denied');
return; return;
} }
this.initializeCustomDevices(); this.processDevices(devices);
this.updateSelectedDevices(); this.updateSelectedDevices();
this.log.d('Media devices', this.cameras, this.microphones);
// Setup live device detection
this.setupDeviceChangeDetection();
this.log.d('Media devices initialized', {
cameras: this.cameras().length,
microphones: this.microphones().length
});
} catch (error) { } catch (error) {
this.log.e('Error getting media devices', error); this.log.e('Error initializing devices', error);
throw error;
} }
} }
/** /**
* Check and update the media devices available * Optimized device retrieval with independent audio/video handling
* This solves the critical bug where audio device failure affects video device detection
*/ */
async refreshDevices() { private async getLocalDevicesOptimized(): Promise<MediaDeviceInfo[]> {
if (!this.deviceAccessDeniedError) { // Check cache first
this.devices = await this.getLocalDevices(); if (this.devicesCache && Date.now() - this.devicesCache.timestamp < this.CACHE_DURATION) {
this.initializeCustomDevices(); this.log.d('Using cached devices');
return this.devicesCache.devices;
}
try {
// Try parallel permission requests for better performance
const results = await this.requestPermissionsParallel();
// Get devices after permissions are granted
const devices = await this.enumerateDevices();
// Update cache
this.devicesCache = {
timestamp: Date.now(),
devices
};
// Update state based on results
this.updateDeviceStates(results);
return devices;
} catch (error) {
this.log.e('Error getting devices', error);
// Fallback: try to enumerate devices without permissions
return await this.fallbackDeviceEnumeration();
} }
} }
private initializeCustomDevices(): void { /**
this.cameras = this.devices * Smart permission request strategy:
* 1. Try both together (single prompt - better UX)
* 2. If fails, try individually (fallback for granular permissions)
*
* This minimizes user friction while maintaining independence
*/
private async requestPermissionsParallel(): Promise<{
video: { success: boolean; error?: any };
audio: { success: boolean; error?: any };
}> {
const results = {
video: { success: false, error: undefined as any },
audio: { success: false, error: undefined as any }
};
// Strategy 1: Try requesting both together (single prompt)
try {
this.log.d('Requesting both audio and video permissions together');
const tracks = await createLocalTracks({ audio: true, video: true });
// Check which tracks we got
const videoTrack = tracks.find(t => t.kind === Track.Kind.Video);
const audioTrack = tracks.find(t => t.kind === Track.Kind.Audio);
if (videoTrack) {
results.video.success = true;
this.log.d('Video permission granted');
}
if (audioTrack) {
results.audio.success = true;
this.log.d('Audio permission granted');
}
// Stop tracks immediately after getting permission
tracks.forEach(t => t.stop());
// If both succeeded, return early (best case - single prompt!)
if (results.video.success && results.audio.success) {
this.log.d('Both permissions granted with single prompt');
return results;
}
} catch (error: any) {
this.log.w('Combined permission request failed, trying individually', error);
// Continue to fallback strategy
}
// Strategy 2: Fallback - request individually if combined request failed
// This handles cases where user denied one but might allow the other
const promises: Promise<void>[] = [];
// Try video if not already granted
if (!results.video.success) {
promises.push(
this.requestVideoPermission().then(
(tracks) => {
results.video.success = true;
tracks.forEach(t => t.stop());
this.log.d('Video permission granted individually');
},
(error) => {
results.video.error = error;
this.log.w('Video permission denied', error);
}
)
);
}
// Try audio if not already granted
if (!results.audio.success) {
promises.push(
this.requestAudioPermission().then(
(tracks) => {
results.audio.success = true;
tracks.forEach(t => t.stop());
this.log.d('Audio permission granted individually');
},
(error) => {
results.audio.error = error;
this.log.w('Audio permission denied', error);
}
)
);
}
// Wait for fallback requests to complete
if (promises.length > 0) {
await Promise.allSettled(promises);
}
return results;
}
/**
* Request video permission independently
*/
private async requestVideoPermission(): Promise<LocalTrack[]> {
try {
return await createLocalTracks({ audio: false, video: true });
} catch (error: any) {
this.videoState.update(state => ({
...state,
permissionGranted: false,
error: error.name || 'Unknown error'
}));
throw error;
}
}
/**
* Request audio permission independently
*/
private async requestAudioPermission(): Promise<LocalTrack[]> {
try {
return await createLocalTracks({ audio: true, video: false });
} catch (error: any) {
this.audioState.update(state => ({
...state,
permissionGranted: false,
error: error.name || 'Unknown error'
}));
throw error;
}
}
/**
* Enumerate devices using LiveKit's Room API or browser API
*/
private async enumerateDevices(): Promise<MediaDeviceInfo[]> {
try {
// Use LiveKit's Room.getLocalDevices if available, otherwise fallback to browser API
const devices = await Room.getLocalDevices();
return this.filterValidDevices(devices);
} catch (error) {
this.log.w('LiveKit device enumeration failed, using browser API', error);
// Firefox compatibility
if (this.platformSrv.isFirefox()) {
return await this.getDevicesFirefox();
}
const devices = await navigator.mediaDevices.enumerateDevices();
return this.filterValidDevices(devices);
}
}
/**
* Firefox-specific device enumeration
*/
private async getDevicesFirefox(): Promise<MediaDeviceInfo[]> {
try {
// Firefox may need explicit getUserMedia call
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
stream.getTracks().forEach(track => track.stop());
const devices = await navigator.mediaDevices.enumerateDevices();
return this.filterValidDevices(devices);
} catch (error) {
this.log.w('Firefox getUserMedia failed, trying enumerate directly', error);
const devices = await navigator.mediaDevices.enumerateDevices();
return this.filterValidDevices(devices);
}
}
/**
* Filter out invalid or default devices
*/
private filterValidDevices(devices: MediaDeviceInfo[]): MediaDeviceInfo[] {
return devices.filter(
(d) => d.label && d.deviceId && d.deviceId !== 'default'
);
}
/**
* Fallback device enumeration without permissions
*/
private async fallbackDeviceEnumeration(): Promise<MediaDeviceInfo[]> {
try {
this.log.d('Attempting device enumeration without permissions');
const devices = await navigator.mediaDevices.enumerateDevices();
// Filter devices that have IDs but may not have labels
return devices.filter(d => d.deviceId && d.deviceId !== 'default');
} catch (error) {
this.log.e('Fallback device enumeration failed', error);
return [];
}
}
/**
* Update device states based on permission results
*/
private updateDeviceStates(results: {
video: { success: boolean; error?: any };
audio: { success: boolean; error?: any };
}): void {
// Update video state
this.videoState.update(state => ({
...state,
permissionGranted: results.video.success,
error: results.video.error?.name
}));
// Update audio state
this.audioState.update(state => ({
...state,
permissionGranted: results.audio.success,
error: results.audio.error?.name
}));
}
/**
* Process raw devices into typed camera and microphone lists
*/
private processDevices(devices: MediaDeviceInfo[]): void {
// Process video devices
const camerasArray = devices
.filter((d) => d.kind === DeviceType.VIDEO_INPUT) .filter((d) => d.kind === DeviceType.VIDEO_INPUT)
.map((d) => this.createCustomDevice(d, CameraType.BACK)); .map((d) => this.createCustomDevice(d, CameraType.BACK));
this.microphones = this.devices
// Process audio devices
const microphonesArray = devices
.filter((d) => d.kind === DeviceType.AUDIO_INPUT) .filter((d) => d.kind === DeviceType.AUDIO_INPUT)
.map((d) => ({ label: d.label, device: d.deviceId })); .map((d) => ({ label: d.label, device: d.deviceId }));
// Detect camera types (front/back)
this.detectCameraTypes(camerasArray);
// Update signals
this.cameras.set(camerasArray);
this.microphones.set(microphonesArray);
// Update availability states
this.updateDeviceAvailability(camerasArray.length, microphonesArray.length);
}
/**
* Detect camera types (front/back) based on platform and labels
*/
private detectCameraTypes(cameras: CustomDevice[]): void {
if (cameras.length === 0) return;
if (this.platformSrv.isMobile()) { if (this.platformSrv.isMobile()) {
this.cameras.forEach((c) => { // On mobile, detect by label
if (c.label.toLowerCase().includes(CameraType.FRONT.toLowerCase())) { cameras.forEach((camera) => {
c.type = CameraType.FRONT; if (camera.label.toLowerCase().includes(CameraType.FRONT.toLowerCase())) {
camera.type = CameraType.FRONT;
} }
}); });
} else if (this.cameras.length > 0) { } else {
this.cameras[0].type = CameraType.FRONT; // On desktop, first camera is typically front-facing
cameras[0].type = CameraType.FRONT;
} }
} }
/**
* Update device availability states
*/
private updateDeviceAvailability(cameraCount: number, microphoneCount: number): void {
this.videoState.update(state => ({
...state,
hasDevices: cameraCount > 0
}));
this.audioState.update(state => ({
...state,
hasDevices: microphoneCount > 0
}));
}
/**
* Create custom device object
*/
private createCustomDevice(device: MediaDeviceInfo, defaultType: CameraType): CustomDevice { private createCustomDevice(device: MediaDeviceInfo, defaultType: CameraType): CustomDevice {
return { return {
label: device.label, label: device.label,
@ -90,181 +470,248 @@ export class DeviceService {
}; };
} }
private updateSelectedDevices() { /**
this.cameraSelected = this.getDeviceFromStorage(this.cameras, this.storageSrv.getVideoDevice()) || this.cameras[0]; * Update selected devices from storage or use defaults
this.microphoneSelected = this.getDeviceFromStorage(this.microphones, this.storageSrv.getAudioDevice()) || this.microphones[0]; */
private updateSelectedDevices(): void {
const storedCamera = this.storageSrv.getVideoDevice();
const selectedCam = this.findDeviceOrDefault(
this.cameras(),
storedCamera?.device
);
if (selectedCam) {
this.cameraSelected.set(selectedCam);
} }
private getDeviceFromStorage(devices: CustomDevice[], storageDevice: CustomDevice | null): CustomDevice | undefined { const storedMic = this.storageSrv.getAudioDevice();
if (!storageDevice) return; const selectedMic = this.findDeviceOrDefault(
return devices.find((d) => d.device === storageDevice.device); this.microphones(),
storedMic?.device
);
if (selectedMic) {
this.microphoneSelected.set(selectedMic);
}
} }
/** /**
* @internal * Find device by ID or return first available
*/
private findDeviceOrDefault(devices: CustomDevice[], deviceId?: string): CustomDevice | undefined {
if (devices.length === 0) return undefined;
return deviceId
? devices.find((d) => d.device === deviceId) || devices[0]
: devices[0];
}
/**
* Refresh devices (e.g., when a device is plugged/unplugged)
*/
async refreshDevices(): Promise<void> {
// Invalidate cache
this.devicesCache = null;
const devices = await this.getLocalDevicesOptimized();
this.processDevices(devices);
this.updateSelectedDevices();
this.log.d('Devices refreshed', {
cameras: this.cameras().length,
microphones: this.microphones().length
});
}
/**
* Setup live device change detection
* Automatically refreshes device list when devices are connected/disconnected
*/
private setupDeviceChangeDetection(): void {
if (!navigator.mediaDevices?.addEventListener) {
this.log.w('Device change detection not supported');
return;
}
// Remove existing listener if any
if (this.deviceChangeHandler) {
navigator.mediaDevices.removeEventListener('devicechange', this.deviceChangeHandler);
}
// Create new handler
this.deviceChangeHandler = async () => {
this.log.d('Device change detected, refreshing device list');
await this.refreshDevices();
};
// Register listener
navigator.mediaDevices.addEventListener('devicechange', this.deviceChangeHandler);
this.log.d('Device change detection enabled');
}
// Public API methods (compatible with original DeviceService)
/**
* Check if camera is enabled based on storage and device availability
*/ */
isCameraEnabled(): boolean { isCameraEnabled(): boolean {
return this.hasVideoDeviceAvailable() && this.storageSrv.isCameraEnabled(); return this.hasVideoDeviceAvailable() && this.storageSrv.isCameraEnabled();
} }
/**
* Check if microphone is enabled based on storage and device availability
*/
isMicrophoneEnabled(): boolean { isMicrophoneEnabled(): boolean {
return this.hasAudioDeviceAvailable() && this.storageSrv.isMicrophoneEnabled(); return this.hasAudioDeviceAvailable() && this.storageSrv.isMicrophoneEnabled();
} }
/**
* Get currently selected camera
*/
getCameraSelected(): CustomDevice | undefined { getCameraSelected(): CustomDevice | undefined {
return this.cameraSelected; return this.cameraSelected();
} }
/**
* Get currently selected microphone
*/
getMicrophoneSelected(): CustomDevice | undefined { getMicrophoneSelected(): CustomDevice | undefined {
return this.microphoneSelected; return this.microphoneSelected();
} }
setCameraSelected(deviceId: any) { /**
this.cameraSelected = this.getDeviceById(this.cameras, deviceId); * Set selected camera and persist to storage
const saveFunction = (device) => this.storageSrv.setVideoDevice(device); */
this.saveDeviceToStorage(this.cameraSelected, saveFunction); setCameraSelected(deviceId: string): void {
const device = this.cameras().find((c) => c.device === deviceId);
if (!device) {
this.log.w('Camera not found:', deviceId);
return;
} }
setMicSelected(deviceId: string) { this.cameraSelected.set(device);
this.microphoneSelected = this.getDeviceById(this.microphones, deviceId); this.storageSrv.setVideoDevice(device);
const saveFunction = (device) => this.storageSrv.setAudioDevice(device); this.log.d('Camera selected:', device.label);
this.saveDeviceToStorage(this.microphoneSelected, saveFunction);
} }
/**
* Set selected microphone and persist to storage
*/
setMicSelected(deviceId: string): void {
const device = this.microphones().find((m) => m.device === deviceId);
if (!device) {
this.log.w('Microphone not found:', deviceId);
return;
}
this.microphoneSelected.set(device);
this.storageSrv.setAudioDevice(device);
this.log.d('Microphone selected:', device.label);
}
/**
* Check if video track needs to be updated
*/
needUpdateVideoTrack(newDevice: CustomDevice): boolean { needUpdateVideoTrack(newDevice: CustomDevice): boolean {
return this.cameraSelected?.device !== newDevice.device || this.cameraSelected?.label !== newDevice.label; const current = this.cameraSelected();
return (
current?.device !== newDevice.device ||
current?.label !== newDevice.label
);
} }
/**
* Check if audio track needs to be updated
*/
needUpdateAudioTrack(newDevice: CustomDevice): boolean { needUpdateAudioTrack(newDevice: CustomDevice): boolean {
return this.microphoneSelected?.device !== newDevice.device || this.microphoneSelected?.label !== newDevice.label; const current = this.microphoneSelected();
return (
current?.device !== newDevice.device ||
current?.label !== newDevice.label
);
} }
// ==========================================
// Public API - Device Access
// ==========================================
/**
* Get list of available cameras
*/
getCameras(): CustomDevice[] { getCameras(): CustomDevice[] {
return this.cameras; return this.cameras();
} }
/**
* Get list of available microphones
*/
getMicrophones(): CustomDevice[] { getMicrophones(): CustomDevice[] {
return this.microphones; return this.microphones();
} }
// ==========================================
// Public API - Device State
// ==========================================
/**
* Check if video devices are available
*/
hasVideoDeviceAvailable(): boolean { hasVideoDeviceAvailable(): boolean {
return this.videoDevicesEnabled && this.cameras.length > 0; return this.hasVideoDevices();
} }
/**
* Check if audio devices are available
*/
hasAudioDeviceAvailable(): boolean { hasAudioDeviceAvailable(): boolean {
return this.audioDevicesEnabled && this.microphones.length > 0; return this.hasAudioDevices();
} }
clear() { // ==========================================
this.devices = []; // Public API - Permission State
this.cameras = []; // ==========================================
this.microphones = [];
this.cameraSelected = undefined;
this.microphoneSelected = undefined;
this.videoDevicesEnabled = true;
this.audioDevicesEnabled = true;
}
private getDeviceById(devices: CustomDevice[], deviceId: string): CustomDevice | undefined { /**
return devices.find((d) => d.device === deviceId); * Check if video permission was granted
} */
hasVideoPermissionGranted(): boolean {
private saveDeviceToStorage(device: CustomDevice | undefined, saveFunction: (device: CustomDevice) => void) { return this.hasVideoPermission();
if (device) saveFunction(device);
} }
/** /**
* Retrieves the local media devices (audio and video) available for the user. * Check if audio permission was granted
*
* @returns A promise that resolves to an array of `MediaDeviceInfo` objects representing the available local devices.
*/ */
private async getLocalDevices(): Promise<MediaDeviceInfo[]> { hasAudioPermissionGranted(): boolean {
const strategies = this.getPermissionStrategies(); return this.hasAudioPermission();
for (const strategy of strategies) {
try {
this.log.d(`Trying strategy: audio=${strategy.audio}, video=${strategy.video}`);
const devices = await this.tryPermissionStrategy(strategy);
if (devices) {
return this.filterValidDevices(devices);
}
} catch (error: any) {
this.log.w(`Strategy failed: audio=${strategy.audio}, video=${strategy.video}`, error);
// If it's the last attempt and failed, we handle the error
if (strategy === strategies[strategies.length - 1]) {
return await this.handleFinalFallback(error);
}
}
} }
return []; // ==========================================
} // Public API - Reactive State Access
// For components that need direct signal access, use:
// - this.cameras, this.microphones (device lists)
// - this.cameraSelected, this.microphoneSelected (selections)
// - this.hasVideoDevices, this.hasAudioDevices (availability)
// - this.hasVideoPermission, this.hasAudioPermission (permissions)
// - this.allPermissionsGranted (combined permissions)
// ==========================================
/** /**
* @internal * Clear all device data
* Get the list of permission strategies to try
*/ */
protected getPermissionStrategies(): Array<{ audio: boolean; video: boolean }> { clear(): void {
return [ this.cameras.set([]);
{ audio: true, video: true }, this.microphones.set([]);
{ audio: true, video: false }, this.cameraSelected.set(undefined);
{ audio: false, video: true } this.microphoneSelected.set(undefined);
]; this.devicesCache = null;
}
/** this.videoState.set({
* @internal hasDevices: false,
* Try a specific permission strategy and return devices if successful isEnabled: true,
*/ permissionGranted: false
protected async tryPermissionStrategy(strategy: { audio: boolean; video: boolean }): Promise<MediaDeviceInfo[] | null> { });
const localTracks = await createLocalTracks(strategy);
localTracks.forEach((track) => track.stop());
// Permission granted this.audioState.set({
return this.platformSrv.isFirefox() ? await this.getMediaDevicesFirefox() : await Room.getLocalDevices(); hasDevices: false,
} isEnabled: true,
permissionGranted: false
/** });
* @internal
* Filter devices to remove default and invalid entries
*/
protected filterValidDevices(devices: MediaDeviceInfo[]): MediaDeviceInfo[] {
return devices.filter((d: MediaDeviceInfo) => d.label && d.deviceId && d.deviceId !== 'default');
}
private async getMediaDevicesFirefox(): Promise<MediaDeviceInfo[]> {
// Firefox requires to get user media to get the devices
await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
return navigator.mediaDevices.enumerateDevices();
}
private async handleFinalFallback(error: any): Promise<MediaDeviceInfo[]> {
this.log.w('All permission strategies failed, trying device enumeration without permissions');
try {
return await this.handleFallbackByErrorType(error);
} catch (error) {
this.log.e('Complete failure getting devices', error);
this.deviceAccessDeniedError = true;
return [];
}
}
/**
* @internal
* Handle fallback based on error type
*/
protected async handleFallbackByErrorType(error: any): Promise<MediaDeviceInfo[]> {
if (error?.name === 'NotReadableError' || error?.name === 'AbortError') {
this.log.w('Device busy, using enumerateDevices() instead');
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter((d) => d.deviceId && d.deviceId !== 'default');
}
if (error?.name === 'NotAllowedError' || error?.name === 'SecurityError') {
this.log.w('Permission denied to access devices');
this.deviceAccessDeniedError = true;
}
return [];
} }
} }

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
import { Subject, takeUntil } from 'rxjs';
import { createKeyMaterialFromString, deriveKeys } from 'livekit-client'; import { createKeyMaterialFromString, deriveKeys } from 'livekit-client';
import { Subject, takeUntil } from 'rxjs';
import { OpenViduComponentsConfigService } from '../config/directive-config.service';
/** /**
* Independent E2EE Service for encrypting and decrypting text-based content * Independent E2EE Service for encrypting and decrypting text-based content
@ -12,6 +12,7 @@ import { createKeyMaterialFromString, deriveKeys } from 'livekit-client';
* - Uses deriveKeys from livekit-client for key derivation (HKDF) * - Uses deriveKeys from livekit-client for key derivation (HKDF)
* - Uses Web Crypto API (AES-GCM) for actual encryption/decryption * - Uses Web Crypto API (AES-GCM) for actual encryption/decryption
* - Generates random IV for each encryption operation * - Generates random IV for each encryption operation
* @internal
*/ */
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',

View File

@ -1,8 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable, signal, Signal } from '@angular/core';
import { BackgroundProcessor, /*BackgroundProcessorWrapper,*/ SwitchBackgroundProcessorOptions } from '@livekit/track-processors'; import {
BackgroundProcessor,
supportsBackgroundProcessors,
supportsModernBackgroundProcessors,
/*BackgroundProcessorWrapper,*/ SwitchBackgroundProcessorOptions
} from '@livekit/track-processors';
import { import {
AudioCaptureOptions, AudioCaptureOptions,
ConnectionState, ConnectionState,
createLocalTracks,
CreateLocalTracksOptions, CreateLocalTracksOptions,
E2EEOptions, E2EEOptions,
ExternalE2EEKeyProvider, ExternalE2EEKeyProvider,
@ -13,8 +19,7 @@ import {
RoomOptions, RoomOptions,
Track, Track,
VideoCaptureOptions, VideoCaptureOptions,
VideoPresets, VideoPresets
createLocalTracks
} from 'livekit-client'; } from 'livekit-client';
import { ILogger } from '../../models/logger.model'; import { ILogger } from '../../models/logger.model';
import { OpenViduComponentsConfigService } from '../config/directive-config.service'; import { OpenViduComponentsConfigService } from '../config/directive-config.service';
@ -57,8 +62,20 @@ export class OpenViduService {
/** /**
* Background processor for video tracks. Initialized in disabled mode. * Background processor for video tracks. Initialized in disabled mode.
* This processor is shared between prejoin and in-room states. * This processor is shared between prejoin and in-room states.
* Only initialized if browser supports background processing (GPU available).
*/ */
private backgroundProcessor: BackgroundProcessorWrapper; private backgroundProcessor?: BackgroundProcessorWrapper;
/**
* Signal to track if background processor is supported (requires GPU).
* Set to false if browser doesn't support it or processor initialization fails.
*/
private _isBackgroundProcessorSupported = signal(false);
/**
* Public readonly signal for background processor support status.
*/
readonly isBackgroundProcessorSupported: Signal<boolean> = this._isBackgroundProcessorSupported.asReadonly();
/** /**
* @internal * @internal
@ -67,11 +84,34 @@ export class OpenViduService {
private loggerSrv: LoggerService, private loggerSrv: LoggerService,
private deviceService: DeviceService, private deviceService: DeviceService,
private storageService: StorageService, private storageService: StorageService,
private configService: OpenViduComponentsConfigService private configService: OpenViduComponentsConfigService,
) { ) {
this.log = this.loggerSrv.get('OpenViduService'); this.log = this.loggerSrv.get('OpenViduService');
// this.isSttReadyObs = this._isSttReady.asObservable(); // this.isSttReadyObs = this._isSttReady.asObservable();
// Check if browser supports background processors
if (!supportsBackgroundProcessors()) {
this.log.w('Background processors not supported in this browser (GPU may be disabled)');
this._isBackgroundProcessorSupported.set(false);
return;
}
// Only initialize processor immediately for browsers supporting modern processors
// Browsers without modern support (e.g., Firefox) will initialize on-demand
if (supportsModernBackgroundProcessors()) {
try {
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' }); this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
this._isBackgroundProcessorSupported.set(true);
this.log.d('Background processor initialized at startup (modern processors supported)');
} catch (error: any) {
this.log.w('Failed to initialize background processor:', error?.message || error);
this._isBackgroundProcessorSupported.set(false);
}
} else {
// Mark as supported but don't initialize yet - will be created on-demand
this._isBackgroundProcessorSupported.set(true);
this.log.d('Background processors supported but not modern - will initialize on-demand');
}
} }
/** /**
@ -273,17 +313,77 @@ export class OpenViduService {
return this.localTracks; return this.localTracks;
} }
/** /**
* Switches the background mode on the local video track. * Switches the background mode on the local video track.
* Works both in prejoin and in-room states. * Works both in prejoin and in-room states.
* For Firefox: applies processor when effect is activated to avoid performance issues
* For other browsers: processor is pre-attached, so just switch mode
* @param options - The switch options (mode, blurRadius, imagePath) * @param options - The switch options (mode, blurRadius, imagePath)
* @returns Promise<void> * @returns Promise<void>
* @internal * @internal
*/ */
async switchBackgroundMode(options: SwitchBackgroundProcessorOptions): Promise<void> { async switchBackgroundMode(options: SwitchBackgroundProcessorOptions): Promise<void> {
if (!this.isBackgroundProcessorSupported()) {
this.log.w('Background processor not supported (GPU disabled). Virtual background is disabled.');
return;
}
try {
// For browsers without modern processor support: attach processor on-demand when effect is activated
if (!supportsModernBackgroundProcessors()) {
await this.handleLazyProcessorAttachment(options.mode);
}
// If processor exists, switch mode (either pre-initialized or just created on-demand)
if (this.backgroundProcessor) {
await this.backgroundProcessor.switchTo(options); await this.backgroundProcessor.switchTo(options);
this.log.d('Background mode switched:', options); this.log.d('Background mode switched:', options);
} }
} catch (error: any) {
this.log.e('Failed to switch background mode:', error?.message || error);
this._isBackgroundProcessorSupported.set(false);
// Don't throw - gracefully disable virtual background instead of crashing
}
}
/**
* Handles lazy processor attachment for browsers without modern processor support.
* Creates and attaches processor on-demand when effect is activated.
* This is used for browsers like Firefox that don't support modern background processors.
* @internal
*/
private async handleLazyProcessorAttachment(mode: SwitchBackgroundProcessorOptions['mode']): Promise<void> {
const videoTrack = await this.getVideoTrack();
if (!videoTrack) return;
const hasProcessor = Boolean(videoTrack.getProcessor());
const isDisabled = mode === 'disabled';
if (!isDisabled && !hasProcessor) {
try {
// Create processor on-demand if not already created
if (!this.backgroundProcessor) {
this.log.d('Creating background processor on-demand');
this.backgroundProcessor = BackgroundProcessor({ mode: 'disabled' });
}
this.log.d('Attaching processor on effect activation (lazy loading)');
await videoTrack.setProcessor(this.backgroundProcessor);
} catch (error: any) {
this.log.w('Failed to attach background processor (GPU may be disabled):', error?.message || error);
this._isBackgroundProcessorSupported.set(false);
// Continue without crashing - virtual background will be disabled
}
return;
}
if (isDisabled && hasProcessor) {
this.log.d('Stopping processor on effect deactivation');
await videoTrack.stopProcessor();
}
}
/** /**
* @internal * @internal
@ -321,9 +421,12 @@ export class OpenViduService {
// Video device // Video device
if (videoDeviceId === true) { if (videoDeviceId === true) {
options.video = this.deviceService.hasVideoDeviceAvailable() if (this.deviceService.hasVideoDeviceAvailable()) {
? { deviceId: this.deviceService.getCameraSelected()?.device || 'default' } const selectedCamera = this.deviceService.getCameraSelected();
: false; options.video = { deviceId: selectedCamera?.device || 'default' };
} else {
options.video = false;
}
} else if (videoDeviceId === false) { } else if (videoDeviceId === false) {
options.video = false; options.video = false;
} else { } else {
@ -333,8 +436,8 @@ export class OpenViduService {
// Audio device // Audio device
if (audioDeviceId === true) { if (audioDeviceId === true) {
if (this.deviceService.hasAudioDeviceAvailable()) { if (this.deviceService.hasAudioDeviceAvailable()) {
audioDeviceId = this.deviceService.getMicrophoneSelected()?.device || 'default'; const selectedMic = this.deviceService.getMicrophoneSelected();
(options.audio as AudioCaptureOptions).deviceId = audioDeviceId; (options.audio as AudioCaptureOptions).deviceId = selectedMic?.device || 'default';
} else { } else {
options.audio = false; options.audio = false;
} }
@ -358,11 +461,24 @@ export class OpenViduService {
} }
// Apply background processor to video track (initialized in disabled mode) // Apply background processor to video track (initialized in disabled mode)
// This ensures the processor is attached before publishing for smooth transitions // For browsers with modern processor support: attach processor immediately for smooth transitions
// For browsers without modern support: skip attachment, will be applied on-demand when effect is activated
const videoTrack = newLocalTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined; const videoTrack = newLocalTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
if (videoTrack) { if (videoTrack && supportsModernBackgroundProcessors()) {
if (this.isBackgroundProcessorSupported() && this.backgroundProcessor) {
try {
await videoTrack.setProcessor(this.backgroundProcessor); await videoTrack.setProcessor(this.backgroundProcessor);
this.log.d('Background processor applied to newly created video track'); this.log.d('Background processor applied to newly created video track');
} catch (error: any) {
this.log.w('Failed to apply background processor (GPU may be disabled):', error?.message || error);
this._isBackgroundProcessorSupported.set(false);
// Continue without crashing - virtual background will be disabled
}
} else {
this.log.d('Background processor not supported (GPU disabled or not available)');
}
} else if (videoTrack && !supportsModernBackgroundProcessors()) {
this.log.d('Modern background processors not supported - will apply processor on-demand when effect is activated');
} }
// Mute tracks if devices are disabled // Mute tracks if devices are disabled
@ -593,6 +709,25 @@ export class OpenViduService {
} }
} }
/**
* Gets the current video track from local tracks or room
* @returns LocalVideoTrack or undefined
* @internal
*/
private async getVideoTrack(): Promise<LocalVideoTrack | undefined> {
// First try to get from local tracks (prejoin state)
let videoTrack = this.localTracks.find((t) => t.kind === Track.Kind.Video) as LocalVideoTrack | undefined;
// If not found and room is connected, get from published tracks
if (!videoTrack && this.isRoomConnected()) {
const localParticipant = this.room.localParticipant;
const videoPublication = localParticipant.getTrackPublications().find((pub) => pub.kind === Track.Kind.Video);
videoTrack = videoPublication?.track as LocalVideoTrack | undefined;
}
return videoTrack;
}
/** /**
* Extracts Livekit data from the provided token and returns an object containing the Livekit URL and room admin status. * Extracts Livekit data from the provided token and returns an object containing the Livekit URL and room admin status.
* @param token - The token to extract Livekit data from. * @param token - The token to extract Livekit data from.

View File

@ -406,10 +406,9 @@ export class ParticipantService {
// Update Signal - create new reference to trigger reactivity // Update Signal - create new reference to trigger reactivity
// The Observable will automatically emit via toObservable() // The Observable will automatically emit via toObservable()
if (this.localParticipant) { if (this.localParticipant) {
const updatedParticipant = Object.assign( const updatedParticipant = Object.assign(Object.create(Object.getPrototypeOf(this.localParticipant)), {
Object.create(Object.getPrototypeOf(this.localParticipant)), ...this.localParticipant
{ ...this.localParticipant } });
);
this.localParticipantWritableSignal.set(updatedParticipant); this.localParticipantWritableSignal.set(updatedParticipant);
} else { } else {
this.localParticipantWritableSignal.set(undefined); this.localParticipantWritableSignal.set(undefined);
@ -460,6 +459,19 @@ export class ParticipantService {
} }
} }
/**
* Returns the participant with the given identity.
* @param identity
* @returns
*/
getParticipantByIdentity(identity: string): ParticipantModel | undefined {
if (this.localParticipant?.identity === identity) {
return this.localParticipant;
}
return this.remoteParticipants.find((p) => p.identity === identity);
}
/* ------------------------------ Remote Participants ------------------------------ */ /* ------------------------------ Remote Participants ------------------------------ */
/** /**

View File

@ -1,4 +1,4 @@
import { Injectable } from '@angular/core'; import { computed, Injectable, Signal } from '@angular/core';
import { SwitchBackgroundProcessorOptions } from '@livekit/track-processors'; import { SwitchBackgroundProcessorOptions } from '@livekit/track-processors';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { BackgroundEffect, EffectType } from '../../models/background-effect.model'; import { BackgroundEffect, EffectType } from '../../models/background-effect.model';
@ -59,6 +59,14 @@ export class VirtualBackgroundService {
return this.backgrounds; return this.backgrounds;
} }
/**
* Computed signal that checks if virtual background is supported (requires GPU).
* Reactively tracks the support status from OpenViduService.
*/
readonly isVirtualBackgroundSupported: Signal<boolean> = computed(() =>
this.openviduService.isBackgroundProcessorSupported()
);
isBackgroundApplied(): boolean { isBackgroundApplied(): boolean {
const bgSelected = this.backgroundIdSelected.getValue(); const bgSelected = this.backgroundIdSelected.getValue();
return !!bgSelected && bgSelected !== 'no_effect'; return !!bgSelected && bgSelected !== 'no_effect';
@ -80,6 +88,12 @@ export class VirtualBackgroundService {
* The background processor is centralized in OpenViduService for consistency. * The background processor is centralized in OpenViduService for consistency.
*/ */
async applyBackground(bg: BackgroundEffect) { async applyBackground(bg: BackgroundEffect) {
// Check if virtual background is supported before proceeding
if (!this.isVirtualBackgroundSupported()) {
this.log.w('Virtual background not supported (GPU disabled). Skipping background application.');
return;
}
// If the background is already applied, do nothing // If the background is already applied, do nothing
if (this.backgroundIsAlreadyApplied(bg.id)) return; if (this.backgroundIsAlreadyApplied(bg.id)) return;

View File

@ -1,7 +1,7 @@
services: services:
caddy-proxy: caddy-proxy:
image: docker.io/openvidu/openvidu-caddy-local:3.5.0 image: docker.io/openvidu/openvidu-caddy-local:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -26,6 +26,7 @@ services:
- 7443:7443 - 7443:7443
- 7880:7880 - 7880:7880
- 9443:9443 - 9443:9443
- 9080:9080
depends_on: depends_on:
setup: setup:
condition: service_completed_successfully condition: service_completed_successfully
@ -86,7 +87,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
dashboard: dashboard:
image: docker.io/openvidu/openvidu-dashboard:3.5.0 image: docker.io/openvidu/openvidu-dashboard:main
restart: unless-stopped restart: unless-stopped
environment: environment:
- SERVER_PORT=5000 - SERVER_PORT=5000
@ -100,7 +101,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu: openvidu:
image: docker.io/openvidu/openvidu-server:3.5.0 image: docker.io/openvidu/openvidu-server:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -121,7 +122,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
ingress: ingress:
image: docker.io/openvidu/ingress:3.5.0 image: docker.io/openvidu/ingress:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -139,7 +140,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
egress: egress:
image: docker.io/openvidu/egress:3.5.0 image: docker.io/openvidu/egress:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -154,7 +155,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
operator: operator:
image: docker.io/openvidu/openvidu-operator:3.5.0 image: docker.io/openvidu/openvidu-operator:main
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
@ -177,10 +178,8 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu-meet: openvidu-meet:
image: docker.io/openvidu/openvidu-meet:3.5.0 image: docker.io/openvidu/openvidu-meet:main
restart: on-failure restart: on-failure
ports:
- 9080:6080
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
environment: environment:
@ -218,7 +217,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu-meet-init: openvidu-meet-init:
image: docker.io/openvidu/openvidu-operator:3.5.0 image: docker.io/openvidu/openvidu-operator:main
restart: on-failure restart: on-failure
environment: environment:
- MODE=local-ready-check - MODE=local-ready-check

View File

@ -60,21 +60,6 @@ Parameters:
AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen
ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag). ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag).
TurnDomainName:
Description: '(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls'
Type: String
Default: ''
TurnOwnPublicCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
TurnOwnPrivateCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
# EC2 Instance configuration # EC2 Instance configuration
InstanceType: InstanceType:
Description: "Specifies the EC2 instance type for your OpenVidu instance" Description: "Specifies the EC2 instance type for your OpenVidu instance"
@ -456,12 +441,6 @@ Metadata:
default: "(Optional) Additional Installer Flags" default: "(Optional) Additional Installer Flags"
Parameters: Parameters:
- AdditionalInstallFlags - AdditionalInstallFlags
- Label:
default: (Optional) TURN server configuration with TLS
Parameters:
- TurnDomainName
- TurnOwnPublicCertificate
- TurnOwnPrivateCertificate
Conditions: Conditions:
PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ] PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ]
@ -515,7 +494,6 @@ Resources:
"GRAFANA_URL": "none", "GRAFANA_URL": "none",
"MINIO_URL": "none", "MINIO_URL": "none",
"DOMAIN_NAME": "none", "DOMAIN_NAME": "none",
"LIVEKIT_TURN_DOMAIN_NAME": "none",
"REDIS_PASSWORD": "none", "REDIS_PASSWORD": "none",
"MONGO_ADMIN_USERNAME": "none", "MONGO_ADMIN_USERNAME": "none",
"MONGO_ADMIN_PASSWORD": "none", "MONGO_ADMIN_PASSWORD": "none",
@ -618,7 +596,7 @@ Resources:
'/usr/local/bin/install.sh': '/usr/local/bin/install.sh':
content: !Sub | content: !Sub |
#!/bin/bash -x #!/bin/bash -x
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
@ -644,7 +622,6 @@ Resources:
PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname)
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io"
else else
DOMAIN=${DomainName} DOMAIN=${DomainName}
fi fi
@ -717,18 +694,6 @@ Resources:
done done
fi fi
if [[ "${!TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${!TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TurnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${CertificateType}" == "selfsigned" ]]; then if [[ "${CertificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -748,18 +713,6 @@ Resources:
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${TurnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${TurnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${TurnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -856,12 +809,6 @@ Resources:
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CONFIG_DIR}/openvidu.env"
fi
# Replace rest of the values # Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_USERNAME)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_USERNAME)/" "${!CONFIG_DIR}/openvidu.env"
@ -918,7 +865,6 @@ Resources:
# Update shared secret # Update shared secret
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_REPLICA_SET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_REPLICA_SET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')"
@ -1114,7 +1060,7 @@ Resources:
BlockDeviceMappings: BlockDeviceMappings:
- DeviceName: /dev/sda1 - DeviceName: /dev/sda1
Ebs: Ebs:
VolumeType: gp2 VolumeType: gp3
DeleteOnTermination: true DeleteOnTermination: true
VolumeSize: 200 VolumeSize: 200

View File

@ -25,15 +25,6 @@ param ownPublicCertificate string = ''
@description('If certificate type is \'owncert\', this parameter will be used to specify the private certificate in base64 format') @description('If certificate type is \'owncert\', this parameter will be used to specify the private certificate in base64 format')
param ownPrivateCertificate string = '' param ownPrivateCertificate string = ''
@description('(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls')
param turnDomainName string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPublicCertificate string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPrivateCertificate string = ''
@description('Initial password for the \'admin\' user in OpenVidu Meet. If not provided, a random password will be generated.') @description('Initial password for the \'admin\' user in OpenVidu Meet. If not provided, a random password will be generated.')
@secure() @secure()
param initialMeetAdminPassword string = '' param initialMeetAdminPassword string = ''
@ -161,12 +152,9 @@ resource openviduSharedInfo 'Microsoft.KeyVault/vaults@2023-07-01' = {
var stringInterpolationParams = { var stringInterpolationParams = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
initialMeetApiKey: initialMeetApiKey initialMeetApiKey: initialMeetApiKey
keyVaultName: keyVaultName keyVaultName: keyVaultName
@ -175,7 +163,7 @@ var stringInterpolationParams = {
var installScriptTemplate = ''' var installScriptTemplate = '''
#!/bin/bash -x #!/bin/bash -x
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout
@ -198,7 +186,6 @@ if [[ "${domainName}" == '' ]]; then
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io" DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
else else
DOMAIN=${domainName} DOMAIN=${domainName}
fi fi
@ -272,19 +259,6 @@ if [[ "${additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "${turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${certificateType}" == "selfsigned" ]]; then if [[ "${certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -304,18 +278,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -374,12 +336,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)
export MONGO_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --query value -o tsv) export MONGO_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --query value -o tsv)
@ -452,7 +408,6 @@ CONFIG_DIR="${INSTALL_DIR}/config"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${CONFIG_DIR}/openvidu.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${CONFIG_DIR}/openvidu.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${CONFIG_DIR}/openvidu.env")"
MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${CONFIG_DIR}/openvidu.env")" MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${CONFIG_DIR}/openvidu.env")"
@ -475,7 +430,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${CO
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --value $LIVEKIT_TURN_DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --value $MONGO_ADMIN_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --value $MONGO_ADMIN_PASSWORD
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --value $MONGO_REPLICA_SET_KEY az keyvault secret set --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --value $MONGO_REPLICA_SET_KEY

File diff suppressed because one or more lines are too long

View File

@ -331,70 +331,6 @@
"visible": true "visible": true
} }
] ]
},
{
"name": "parameters TURN",
"label": "(Optional) TURN server configuration with TLS",
"elements": [
{
"name": "turnDomainName",
"type": "Microsoft.Common.TextBox",
"label": "Turn Domain Name",
"subLabel": "\n(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPublicCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Public Certificate",
"subLabel": "\n(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPublicCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPrivateCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Private Certificate",
"subLabel": "\n(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPrivateCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
}
]
} }
] ]
}, },
@ -409,9 +345,6 @@
"domainName": "[steps('parameters SSL').domainName]", "domainName": "[steps('parameters SSL').domainName]",
"ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]", "ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]",
"ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]", "ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]",
"turnDomainName": "[steps('parameters TURN').turnDomainName]",
"turnOwnPublicCertificate": "[steps('parameters TURN').turnOwnPublicCertificate]",
"turnOwnPrivateCertificate": "[steps('parameters TURN').turnOwnPrivateCertificate]",
"initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]", "initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]",
"initialMeetApiKey": "[steps('parameters MEET').initialMeetApiKey]", "initialMeetApiKey": "[steps('parameters MEET').initialMeetApiKey]",
"instanceType": "[steps('parameters INSTANCE').instanceType]", "instanceType": "[steps('parameters INSTANCE').instanceType]",

View File

@ -14,7 +14,7 @@ resource "google_secret_manager_secret" "openvidu_shared_info" {
for_each = toset([ for_each = toset([
"OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD", "OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD",
"MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET", "MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET",
"DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME", "LIVEKIT_TURN_DOMAIN_NAME", "DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME",
"REDIS_PASSWORD", "MONGO_ADMIN_USERNAME", "MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME", "MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY",
"MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD",
"GRAFANA_ADMIN_USERNAME", "GRAFANA_ADMIN_PASSWORD", "ENABLED_MODULES" "GRAFANA_ADMIN_USERNAME", "GRAFANA_ADMIN_PASSWORD", "ENABLED_MODULES"
@ -125,9 +125,6 @@ resource "google_compute_instance" "openvidu_server" {
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
additionalInstallFlags = var.additionalInstallFlags additionalInstallFlags = var.additionalInstallFlags
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName
} }
@ -151,7 +148,7 @@ locals {
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout
@ -183,7 +180,6 @@ if [[ "${var.domainName}" == "" ]]; then
EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip") EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip")
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO=turn-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
else else
DOMAIN="${var.domainName}" DOMAIN="${var.domainName}"
fi fi
@ -255,19 +251,6 @@ if [[ "${var.additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "$TURN_DOMAIN_NAME_SSLIP_IO" != "" ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "$TURN_DOMAIN_NAME_SSLIP_IO")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${var.turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${var.turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${var.certificateType}" == "selfsigned" ]]; then if [[ "${var.certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -286,17 +269,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${var.turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${var.turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${var.turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Final command # Final command
@ -395,12 +367,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(gcloud secrets versions access latest --secret=LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "$${CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD) export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD)
export MONGO_ADMIN_USERNAME=$(gcloud secrets versions access latest --secret=MONGO_ADMIN_USERNAME) export MONGO_ADMIN_USERNAME=$(gcloud secrets versions access latest --secret=MONGO_ADMIN_USERNAME)
@ -471,7 +437,6 @@ CONFIG_DIR="$${INSTALL_DIR}/config"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${CONFIG_DIR}/openvidu.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${CONFIG_DIR}/openvidu.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "$${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "$${CONFIG_DIR}/openvidu.env")"
MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "$${CONFIG_DIR}/openvidu.env")" MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "$${CONFIG_DIR}/openvidu.env")"
@ -493,7 +458,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "$${C
# Update shared secret # Update shared secret
echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-file=- echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-file=-
echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=- echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=-
echo -n "$LIVEKIT_TURN_DOMAIN_NAME" | gcloud secrets versions add LIVEKIT_TURN_DOMAIN_NAME --data-file=-
echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=- echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=-
echo -n "$MONGO_ADMIN_PASSWORD" | gcloud secrets versions add MONGO_ADMIN_PASSWORD --data-file=- echo -n "$MONGO_ADMIN_PASSWORD" | gcloud secrets versions add MONGO_ADMIN_PASSWORD --data-file=-
echo -n "$MONGO_REPLICA_SET_KEY" | gcloud secrets versions add MONGO_REPLICA_SET_KEY --data-file=- echo -n "$MONGO_REPLICA_SET_KEY" | gcloud secrets versions add MONGO_REPLICA_SET_KEY --data-file=-

View File

@ -106,21 +106,3 @@ variable "additionalInstallFlags" {
error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)." error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)."
} }
} }
variable "turnDomainName" {
description = "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls"
type = string
default = ""
}
variable "turnOwnPublicCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}
variable "turnOwnPrivateCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -60,21 +60,6 @@ Parameters:
AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen
ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag). ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag).
TurnDomainName:
Description: '(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls'
Type: String
Default: ''
TurnOwnPublicCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
TurnOwnPrivateCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
OpenViduLicense: OpenViduLicense:
Description: "Visit https://openvidu.io/account" Description: "Visit https://openvidu.io/account"
Type: String Type: String
@ -861,13 +846,6 @@ Metadata:
default: "(Optional) Additional Installer Flags" default: "(Optional) Additional Installer Flags"
Parameters: Parameters:
- AdditionalInstallFlags - AdditionalInstallFlags
- Label:
default: (Optional) TURN server configuration with TLS
Parameters:
- TurnDomainName
- TurnOwnPublicCertificate
- TurnOwnPrivateCertificate
Conditions: Conditions:
PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ] PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ]
PublicElasticIPAbsent: !Equals [!Ref PublicElasticIP, ""] PublicElasticIPAbsent: !Equals [!Ref PublicElasticIP, ""]
@ -931,7 +909,6 @@ Resources:
"GRAFANA_URL": "none", "GRAFANA_URL": "none",
"MINIO_URL": "none", "MINIO_URL": "none",
"DOMAIN_NAME": "none", "DOMAIN_NAME": "none",
"LIVEKIT_TURN_DOMAIN_NAME": "none",
"OPENVIDU_PRO_LICENSE": "none", "OPENVIDU_PRO_LICENSE": "none",
"OPENVIDU_RTC_ENGINE": "none", "OPENVIDU_RTC_ENGINE": "none",
"REDIS_PASSWORD": "none", "REDIS_PASSWORD": "none",
@ -1136,7 +1113,7 @@ Resources:
content: !Sub | content: !Sub |
#!/bin/bash #!/bin/bash
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
@ -1163,7 +1140,6 @@ Resources:
PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname)
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io"
else else
DOMAIN=${DomainName} DOMAIN=${DomainName}
fi fi
@ -1190,7 +1166,7 @@ Resources:
break break
fi fi
RETRY_COUNT=RETRY_COUNT+1 RETRY_COUNT=$((RETRY_COUNT+1))
if [[ $RETRY_COUNT -ge $MAX_RETRIES ]]; then if [[ $RETRY_COUNT -ge $MAX_RETRIES ]]; then
exit 1 exit 1
fi fi
@ -1271,19 +1247,6 @@ Resources:
done done
fi fi
# Turn with TLS
if [[ "${!TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${!TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TurnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${CertificateType}" == "selfsigned" ]]; then if [[ "${CertificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -1303,18 +1266,6 @@ Resources:
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${TurnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${TurnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${TurnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -1412,12 +1363,6 @@ Resources:
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Replace rest of the values # Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!MASTER_NODE_CONFIG_DIR}/master_node.env" sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!MASTER_NODE_CONFIG_DIR}/master_node.env"
sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_RTC_ENGINE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_RTC_ENGINE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
@ -1478,7 +1423,6 @@ Resources:
# Update shared secret # Update shared secret
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!MASTER_NODE_CONFIG_DIR}/master_node.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!MASTER_NODE_CONFIG_DIR}/master_node.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
@ -1679,7 +1623,7 @@ Resources:
BlockDeviceMappings: BlockDeviceMappings:
- DeviceName: /dev/sda1 - DeviceName: /dev/sda1
Ebs: Ebs:
VolumeType: gp2 VolumeType: gp3
DeleteOnTermination: true DeleteOnTermination: true
VolumeSize: 200 VolumeSize: 200
@ -1731,7 +1675,7 @@ Resources:
if [[ "$SHARED_SECRET" != "none" ]]; then if [[ "$SHARED_SECRET" != "none" ]]; then
break break
fi fi
RETRY_COUNT=RETRY_COUNT+1 RETRY_COUNT=$((RETRY_COUNT+1))
if [[ $RETRY_COUNT -ge $MAX_RETRIES ]]; then if [[ $RETRY_COUNT -ge $MAX_RETRIES ]]; then
exit 1 exit 1
fi fi
@ -1905,7 +1849,7 @@ Resources:
BlockDeviceMappings: BlockDeviceMappings:
- DeviceName: /dev/sda1 - DeviceName: /dev/sda1
Ebs: Ebs:
VolumeType: gp2 VolumeType: gp3
DeleteOnTermination: true DeleteOnTermination: true
VolumeSize: 50 VolumeSize: 50
@ -2031,7 +1975,9 @@ Resources:
- Effect: Allow - Effect: Allow
Action: Action:
- ssm:StartAutomationExecution - ssm:StartAutomationExecution
Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${StopMediaNodeAutomationDocument}:$DEFAULT Resource:
- !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-execution/*
- !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:document/${StopMediaNodeAutomationDocument}
PolicyName: !Sub StopMediaNodeCloudWatchEventPolicy-${AWS::Region}-${AWS::StackName} PolicyName: !Sub StopMediaNodeCloudWatchEventPolicy-${AWS::Region}-${AWS::StackName}
- PolicyDocument: - PolicyDocument:
Version: '2012-10-17' Version: '2012-10-17'

View File

@ -25,15 +25,6 @@ param ownPublicCertificate string = ''
@description('If certificate type is \'owncert\', this parameter will be used to specify the private certificate in base64 format') @description('If certificate type is \'owncert\', this parameter will be used to specify the private certificate in base64 format')
param ownPrivateCertificate string = '' param ownPrivateCertificate string = ''
@description('(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls')
param turnDomainName string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPublicCertificate string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPrivateCertificate string = ''
@description('Visit https://openvidu.io/account') @description('Visit https://openvidu.io/account')
@secure() @secure()
param openviduLicense string param openviduLicense string
@ -207,12 +198,9 @@ resource openviduSharedInfo 'Microsoft.KeyVault/vaults@2023-07-01' = {
var stringInterpolationParamsMaster = { var stringInterpolationParamsMaster = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
openviduLicense: openviduLicense openviduLicense: openviduLicense
rtcEngine: rtcEngine rtcEngine: rtcEngine
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
@ -223,7 +211,7 @@ var stringInterpolationParamsMaster = {
var installScriptTemplateMaster = ''' var installScriptTemplateMaster = '''
#!/bin/bash -x #!/bin/bash -x
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
# Assume azure cli is installed # Assume azure cli is installed
@ -247,7 +235,6 @@ if [[ "${domainName}" == '' ]]; then
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io" DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
else else
DOMAIN=${domainName} DOMAIN=${domainName}
fi fi
@ -361,19 +348,6 @@ if [[ "${additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "${turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${certificateType}" == "selfsigned" ]]; then if [[ "${certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -393,18 +367,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -462,12 +424,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)
export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv)
@ -542,7 +498,6 @@ MASTER_NODE_CONFIG_DIR="${INSTALL_DIR}/config/node"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${MASTER_NODE_CONFIG_DIR}/master_node.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${MASTER_NODE_CONFIG_DIR}/master_node.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
@ -566,7 +521,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${CL
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --value $LIVEKIT_TURN_DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME

File diff suppressed because one or more lines are too long

View File

@ -505,70 +505,6 @@
"visible": true "visible": true
} }
] ]
},
{
"name": "parameters TURN",
"label": "(Optional) TURN server configuration with TLS",
"elements": [
{
"name": "turnDomainName",
"type": "Microsoft.Common.TextBox",
"label": "Turn Domain Name",
"subLabel": "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPublicCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Public Certificate",
"subLabel": "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPublicCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPrivateCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Private Certificate",
"subLabel": "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPrivateCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
}
]
} }
] ]
}, },
@ -583,9 +519,6 @@
"domainName": "[steps('parameters SSL').domainName]", "domainName": "[steps('parameters SSL').domainName]",
"ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]", "ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]",
"ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]", "ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]",
"turnDomainName": "[steps('parameters TURN').turnDomainName]",
"turnOwnPublicCertificate": "[steps('parameters TURN').turnOwnPublicCertificate]",
"turnOwnPrivateCertificate": "[steps('parameters TURN').turnOwnPrivateCertificate]",
"openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]", "openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]",
"rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]", "rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]",
"initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]", "initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]",

View File

@ -16,7 +16,7 @@ resource "google_secret_manager_secret" "openvidu_shared_info" {
for_each = toset([ for_each = toset([
"OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD", "OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD",
"MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET", "MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET",
"DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME", "LIVEKIT_TURN_DOMAIN_NAME", "DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME",
"OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME", "OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME",
"MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY",
"DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME", "DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME",
@ -167,9 +167,6 @@ resource "google_compute_instance" "openvidu_master_node" {
initialMeetAdminPassword = var.initialMeetAdminPassword initialMeetAdminPassword = var.initialMeetAdminPassword
initialMeetApiKey = var.initialMeetApiKey initialMeetApiKey = var.initialMeetApiKey
additionalInstallFlags = var.additionalInstallFlags additionalInstallFlags = var.additionalInstallFlags
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName
} }
@ -620,7 +617,7 @@ locals {
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout
@ -652,7 +649,6 @@ if [[ "${var.domainName}" == "" ]]; then
EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip") EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip")
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO=turn-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
else else
DOMAIN="${var.domainName}" DOMAIN="${var.domainName}"
fi fi
@ -738,19 +734,6 @@ if [[ "${var.additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "$TURN_DOMAIN_NAME_SSLIP_IO" != "" ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "$TURN_DOMAIN_NAME_SSLIP_IO")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${var.turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${var.turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${var.certificateType}" == "selfsigned" ]]; then if [[ "${var.certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -769,17 +752,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${var.turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${var.turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${var.turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Final command # Final command
@ -879,12 +851,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(gcloud secrets versions access latest --secret=LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD) export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD)
export OPENVIDU_RTC_ENGINE=$(gcloud secrets versions access latest --secret=OPENVIDU_RTC_ENGINE) export OPENVIDU_RTC_ENGINE=$(gcloud secrets versions access latest --secret=OPENVIDU_RTC_ENGINE)
@ -960,7 +926,6 @@ MASTER_NODE_CONFIG_DIR="$${INSTALL_DIR}/config/node"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${MASTER_NODE_CONFIG_DIR}/master_node.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${MASTER_NODE_CONFIG_DIR}/master_node.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "$${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "$${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
@ -984,7 +949,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "$${C
# Update shared secret # Update shared secret
echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-file=- echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-file=-
echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=- echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=-
echo -n "$LIVEKIT_TURN_DOMAIN_NAME" | gcloud secrets versions add LIVEKIT_TURN_DOMAIN_NAME --data-file=-
echo -n "$OPENVIDU_RTC_ENGINE" | gcloud secrets versions add OPENVIDU_RTC_ENGINE --data-file=- echo -n "$OPENVIDU_RTC_ENGINE" | gcloud secrets versions add OPENVIDU_RTC_ENGINE --data-file=-
echo -n "$OPENVIDU_PRO_LICENSE" | gcloud secrets versions add OPENVIDU_PRO_LICENSE --data-file=- echo -n "$OPENVIDU_PRO_LICENSE" | gcloud secrets versions add OPENVIDU_PRO_LICENSE --data-file=-
echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=- echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=-

View File

@ -112,7 +112,7 @@ variable "minNumberOfMediaNodes" {
variable "maxNumberOfMediaNodes" { variable "maxNumberOfMediaNodes" {
description = "Maximum number of media nodes to deploy" description = "Maximum number of media nodes to deploy"
type = number type = number
default = 2 default = 5
} }
variable "scaleTargetCPU" { variable "scaleTargetCPU" {
@ -152,21 +152,3 @@ variable "additionalInstallFlags" {
error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)." error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)."
} }
} }
variable "turnDomainName" {
description = "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls"
type = string
default = ""
}
variable "turnOwnPublicCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}
variable "turnOwnPrivateCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -14,16 +14,6 @@ Parameters:
AllowedPattern: '.+' AllowedPattern: '.+'
ConstraintDescription: The Load Balancer domain name must be defined ConstraintDescription: The Load Balancer domain name must be defined
TurnDomainName:
Description: '(Optional) Domain name for the TURN server with TLS.'
Type: String
Default: ''
TurnCertificateARN:
Description: '(Optional) Amazon certificate arn resource to load into the TURN LoadBalancer'
Type: String
Default: ''
OpenViduLicense: OpenViduLicense:
Description: "Visit https://openvidu.io/account" Description: "Visit https://openvidu.io/account"
Type: String Type: String
@ -848,14 +838,8 @@ Metadata:
default: "(Optional) Additional Installer Flags" default: "(Optional) Additional Installer Flags"
Parameters: Parameters:
- AdditionalInstallFlags - AdditionalInstallFlags
- Label:
default: (Optional) TURN server configuration with TLS
Parameters:
- TurnDomainName
- TurnCertificateARN
Conditions: Conditions:
TurnTLSIsEnabled: !Or [!Not [!Equals [!Ref TurnDomainName, ""]], !Not [!Equals [!Ref TurnCertificateARN, ""]]]
CreateRecordingsBucket: !Equals [!Ref S3AppDataBucketName, ""] CreateRecordingsBucket: !Equals [!Ref S3AppDataBucketName, ""]
CreateClusterDataBucket: !Equals [!Ref S3ClusterDataBucketName, ""] CreateClusterDataBucket: !Equals [!Ref S3ClusterDataBucketName, ""]
IsMasterGraviton: !Or IsMasterGraviton: !Or
@ -880,22 +864,6 @@ Conditions:
- !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm7g'] - !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm7g']
- !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm7gd'] - !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm7gd']
- !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm8g'] - !Equals [ !Select [ 0, !Split ['.', !Ref MediaNodeInstanceType ]], 'm8g']
# ---
# Experimental TURN TLS with main domain
ExperimentalTurnTLSWithMainDomain:
Fn::Not:
- Fn::Equals:
- !Ref AdditionalInstallFlags
- !Select [0, !Split ["--experimental-turn-tls-with-main-domain", !Ref AdditionalInstallFlags]]
NotExperimentalTurnTLSWithMainDomain:
Fn::Or:
- Fn::Equals:
- !Ref AdditionalInstallFlags
- !Select [0, !Split ["--experimental-turn-tls-with-main-domain", !Ref AdditionalInstallFlags]]
- Fn::Equals:
- !Ref AdditionalInstallFlags
- ""
# ---
Mappings: Mappings:
ArmImage: ArmImage:
@ -933,7 +901,6 @@ Resources:
"GRAFANA_URL": "none", "GRAFANA_URL": "none",
"MINIO_URL": "none", "MINIO_URL": "none",
"DOMAIN_NAME": "none", "DOMAIN_NAME": "none",
"LIVEKIT_TURN_DOMAIN_NAME": "none",
"OPENVIDU_PRO_LICENSE": "none", "OPENVIDU_PRO_LICENSE": "none",
"OPENVIDU_RTC_ENGINE": "none", "OPENVIDU_RTC_ENGINE": "none",
"REDIS_PASSWORD": "none", "REDIS_PASSWORD": "none",
@ -1249,7 +1216,7 @@ Resources:
content: !Sub | content: !Sub |
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
@ -1324,9 +1291,6 @@ Resources:
# If the private IP is the same as the first master node, generate the secrets # If the private IP is the same as the first master node, generate the secrets
if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "false" ]]; then if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "false" ]]; then
DOMAIN="$(/usr/local/bin/store_secret.sh save DOMAIN_NAME "${DomainName}")" DOMAIN="$(/usr/local/bin/store_secret.sh save DOMAIN_NAME "${DomainName}")"
if [[ -n "${TurnDomainName}" ]]; then
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}")"
fi
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU_PRO_LICENSE "${OpenViduLicense}")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU_PRO_LICENSE "${OpenViduLicense}")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU_RTC_ENGINE "${RTCEngine}")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU_RTC_ENGINE "${RTCEngine}")"
# Store version so media nodes can use it to install the same version # Store version so media nodes can use it to install the same version
@ -1411,7 +1375,6 @@ Resources:
MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP" MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP"
DOMAIN=$(echo "$SHARED_SECRET" | jq -r '.DOMAIN_NAME') DOMAIN=$(echo "$SHARED_SECRET" | jq -r '.DOMAIN_NAME')
LIVEKIT_TURN_DOMAIN_NAME=$(echo "$SHARED_SECRET" | jq -r '.LIVEKIT_TURN_DOMAIN_NAME')
OPENVIDU_PRO_LICENSE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_PRO_LICENSE') OPENVIDU_PRO_LICENSE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_PRO_LICENSE')
OPENVIDU_RTC_ENGINE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_RTC_ENGINE') OPENVIDU_RTC_ENGINE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_RTC_ENGINE')
REDIS_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.REDIS_PASSWORD') REDIS_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.REDIS_PASSWORD')
@ -1480,10 +1443,6 @@ Resources:
done done
fi fi
if [[ "${!LIVEKIT_TURN_DOMAIN_NAME}" != "none" ]]; then
COMMON_ARGS+=("--turn-domain-name='${!LIVEKIT_TURN_DOMAIN_NAME}'")
fi
# Construct the final command # Construct the final command
FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${!COMMON_ARGS[@]}")" FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${!COMMON_ARGS[@]}")"
@ -1584,12 +1543,6 @@ Resources:
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Replace rest of the values # Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!MASTER_NODE_CONFIG_DIR}/master_node.env" sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!MASTER_NODE_CONFIG_DIR}/master_node.env"
sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_RTC_ENGINE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_RTC_ENGINE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
@ -1649,7 +1602,6 @@ Resources:
# Update shared secret # Update shared secret
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!MASTER_NODE_CONFIG_DIR}/master_node.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!MASTER_NODE_CONFIG_DIR}/master_node.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
@ -2235,11 +2187,7 @@ Resources:
LaunchTemplateId: !Ref OpenViduMediaNodeLaunchTemplate LaunchTemplateId: !Ref OpenViduMediaNodeLaunchTemplate
Version: !GetAtt OpenViduMediaNodeLaunchTemplate.DefaultVersionNumber Version: !GetAtt OpenViduMediaNodeLaunchTemplate.DefaultVersionNumber
TargetGroupARNs: TargetGroupARNs:
Fn::If: - !Ref OpenViduMediaNodeRTMPTG
- TurnTLSIsEnabled
- - !Ref OpenViduMediaNodeRTMPTG
- !Ref OpenViduMediaNodeTurnTLSTG
- - !Ref OpenViduMediaNodeRTMPTG
MinSize: !Ref MinNumberOfMediaNodes MinSize: !Ref MinNumberOfMediaNodes
MaxSize: !Ref MaxNumberOfMediaNodes MaxSize: !Ref MaxNumberOfMediaNodes
DesiredCapacity: !Ref InitialNumberOfMediaNodes DesiredCapacity: !Ref InitialNumberOfMediaNodes
@ -2353,7 +2301,9 @@ Resources:
- Effect: Allow - Effect: Allow
Action: Action:
- ssm:StartAutomationExecution - ssm:StartAutomationExecution
Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${StopMediaNodeAutomationDocument}:$DEFAULT Resource:
- !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-execution/*
- !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:document/${StopMediaNodeAutomationDocument}
PolicyName: !Sub StopMediaNodeCloudWatchEventPolicy-${AWS::Region}-${AWS::StackName} PolicyName: !Sub StopMediaNodeCloudWatchEventPolicy-${AWS::Region}-${AWS::StackName}
- PolicyDocument: - PolicyDocument:
Version: '2012-10-17' Version: '2012-10-17'
@ -2700,27 +2650,6 @@ Resources:
ToPort: 9092 ToPort: 9092
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
OpenViduLoadBalancerTurnTLSToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: TurnTLSIsEnabled
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 5349
ToPort: 5349
SourceSecurityGroupId: !Ref OpenViduTurnTLSLoadBalancerSG
OpenViduLoadBalancerTurnTLSToMediaNodeHealthCheckSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: TurnTLSIsEnabled
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 7880
ToPort: 7880
SourceSecurityGroupId: !Ref OpenViduTurnTLSLoadBalancerSG
OpenViduMasterToMediaNodeServerIngressSG: OpenViduMasterToMediaNodeServerIngressSG:
Type: AWS::EC2::SecurityGroupIngress Type: AWS::EC2::SecurityGroupIngress
Properties: Properties:
@ -2739,11 +2668,8 @@ Resources:
ToPort: 8080 ToPort: 8080
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
# ---
# Experimental TURN TLS with main domain
OpenViduTurnTLSMasterNodeToMediaNodeIngressSG: OpenViduTurnTLSMasterNodeToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress Type: AWS::EC2::SecurityGroupIngress
Condition: ExperimentalTurnTLSWithMainDomain
Properties: Properties:
GroupId: !Ref OpenViduMediaNodeSG GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp IpProtocol: tcp
@ -2753,14 +2679,12 @@ Resources:
OpenViduTurnTLSLoadBalancerToMediaNodeIngressSG: OpenViduTurnTLSLoadBalancerToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress Type: AWS::EC2::SecurityGroupIngress
Condition: ExperimentalTurnTLSWithMainDomain
Properties: Properties:
GroupId: !Ref OpenViduMasterNodeSG GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp IpProtocol: tcp
FromPort: 443 FromPort: 443
ToPort: 443 ToPort: 443
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
# ---
OpenViduLoadBalancerSG: OpenViduLoadBalancerSG:
Type: AWS::EC2::SecurityGroup Type: AWS::EC2::SecurityGroup
@ -2794,23 +2718,6 @@ Resources:
ToPort: 1935 ToPort: 1935
CidrIpv6: ::/0 CidrIpv6: ::/0
OpenViduTurnTLSLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Condition: TurnTLSIsEnabled
Properties:
GroupDescription: Security group for the Load Balancer for TURN with TLS
GroupName: !Sub openvidu-ha-turn-tls-lb-sg-${AWS::Region}-${AWS::StackName}
VpcId: !Ref OpenViduVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIpv6: ::/0
LoadBalancer: LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer Type: AWS::ElasticLoadBalancingV2::LoadBalancer
DependsOn: DependsOn:
@ -2831,54 +2738,8 @@ Resources:
- Key: Name - Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Load Balancer Value: !Sub ${AWS::StackName} - OpenVidu HA - Load Balancer
TurnTLSLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Condition: TurnTLSIsEnabled
Properties:
Name:
Fn::Join:
# Generate a not too long and unique load balancer name
# Getting a unique identifier from the stack id
- ''
- - OpenViduHA-TurnTLS-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
Subnets: !Ref OpenViduMediaNodeSubnets
SecurityGroups:
- !Ref OpenViduTurnTLSLoadBalancerSG
Type: network
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN with TLS Load Balancer
OpenViduMasterNodeHTTPListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: NotExperimentalTurnTLSWithMainDomain
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMasterNodeHTTPTG
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: TCP
OpenViduMasterNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: NotExperimentalTurnTLSWithMainDomain
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMasterNodeTG
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: TLS
Certificates:
- CertificateArn: !Ref OpenViduCertificateARN
# ---
# Experimental TURN TLS with main domain
OpenViduMasterNodeWithTurnTLSHTTPListener: OpenViduMasterNodeWithTurnTLSHTTPListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener' Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: ExperimentalTurnTLSWithMainDomain
Properties: Properties:
DefaultActions: DefaultActions:
- Type: forward - Type: forward
@ -2889,7 +2750,6 @@ Resources:
OpenViduMasterNodeWithTurnTLSListener: OpenViduMasterNodeWithTurnTLSListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener' Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: ExperimentalTurnTLSWithMainDomain
Properties: Properties:
DefaultActions: DefaultActions:
- Type: forward - Type: forward
@ -2899,7 +2759,6 @@ Resources:
Protocol: TLS Protocol: TLS
Certificates: Certificates:
- CertificateArn: !Ref OpenViduCertificateARN - CertificateArn: !Ref OpenViduCertificateARN
# ---
OpenViduRTMPMediaNodeListener: OpenViduRTMPMediaNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener' Type: 'AWS::ElasticLoadBalancingV2::Listener'
@ -2913,22 +2772,8 @@ Resources:
Certificates: Certificates:
- CertificateArn: !Ref OpenViduCertificateARN - CertificateArn: !Ref OpenViduCertificateARN
OpenViduTurnTLSMediaNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: TurnTLSIsEnabled
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMediaNodeTurnTLSTG
LoadBalancerArn: !Ref TurnTLSLoadBalancer
Port: 443
Protocol: TLS
Certificates:
- CertificateArn: !Ref TurnCertificateARN
OpenViduMasterNodeHTTPTG: OpenViduMasterNodeHTTPTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: NotExperimentalTurnTLSWithMainDomain
Properties: Properties:
Name: Name:
Fn::Join: Fn::Join:
@ -2962,47 +2807,8 @@ Resources:
- Key: Name - Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master HTTP Target Group Value: !Sub ${AWS::StackName} - OpenVidu HA - Master HTTP Target Group
OpenViduMasterNodeTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: NotExperimentalTurnTLSWithMainDomain
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OpenVidu-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
TargetType: instance
Targets:
- Id: !Ref OpenViduMasterNode1
- Id: !Ref OpenViduMasterNode2
- Id: !Ref OpenViduMasterNode3
- Id: !Ref OpenViduMasterNode4
VpcId: !Ref OpenViduVPC
Port: 7880
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health/caddy
HealthCheckProtocol: HTTP
HealthCheckPort: '7880'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Target Group
# ---
# Experimental TURN TLS with main domain
OpenViduMasterNodeWithTurnTLSTG: OpenViduMasterNodeWithTurnTLSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: ExperimentalTurnTLSWithMainDomain
Properties: Properties:
Name: Name:
Fn::Join: Fn::Join:
@ -3035,7 +2841,6 @@ Resources:
Tags: Tags:
- Key: Name - Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN TLS Master Target Group Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN TLS Master Target Group
# ---
OpenViduMediaNodeRTMPTG: OpenViduMediaNodeRTMPTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup Type: AWS::ElasticLoadBalancingV2::TargetGroup
@ -3067,33 +2872,6 @@ Resources:
- Key: Name - Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - RTMP Target Group Value: !Sub ${AWS::StackName} - OpenVidu HA - RTMP Target Group
OpenViduMediaNodeTurnTLSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: TurnTLSIsEnabled
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OVTurnTLS-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
VpcId: !Ref OpenViduVPC
Port: 5349
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckPort: '7880'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN TLS Target Group
Outputs: Outputs:
ServicesAndCredentials: ServicesAndCredentials:
Description: Services and credentials Description: Services and credentials

View File

@ -25,15 +25,6 @@ param ownPrivateCertificate string = ''
@description('Name of the PublicIPAddress resource in Azure when using certificateType \'owncert\' or \'letsencrypt\'') @description('Name of the PublicIPAddress resource in Azure when using certificateType \'owncert\' or \'letsencrypt\'')
param publicIpAddressObject object param publicIpAddressObject object
@description('(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls')
param turnDomainName string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPublicCertificate string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPrivateCertificate string = ''
@description('Visit https://openvidu.io/account') @description('Visit https://openvidu.io/account')
@secure() @secure()
param openviduLicense string param openviduLicense string
@ -95,8 +86,20 @@ var isEmptyDomain = domainName == ''
// The pattern checks for 'p' followed by optional letters (like 'l', 'd', 's') before '_v' version suffix // The pattern checks for 'p' followed by optional letters (like 'l', 'd', 's') before '_v' version suffix
var masterNodeInstanceTypeLower = toLower(masterNodeInstanceType) var masterNodeInstanceTypeLower = toLower(masterNodeInstanceType)
var mediaNodeInstanceTypeLower = toLower(mediaNodeInstanceType) var mediaNodeInstanceTypeLower = toLower(mediaNodeInstanceType)
var isMasterArm64 = contains(masterNodeInstanceTypeLower, 'ps_v') || contains(masterNodeInstanceTypeLower, 'pls_v') || contains(masterNodeInstanceTypeLower, 'pds_v') || contains(masterNodeInstanceTypeLower, 'plds_v') || contains(masterNodeInstanceTypeLower, 'psv') || contains(masterNodeInstanceTypeLower, 'plsv') || contains(masterNodeInstanceTypeLower, 'pdsv') || contains(masterNodeInstanceTypeLower, 'pldsv') var isMasterArm64 = contains(masterNodeInstanceTypeLower, 'ps_v') || contains(masterNodeInstanceTypeLower, 'pls_v') || contains(
var isMediaArm64 = contains(mediaNodeInstanceTypeLower, 'ps_v') || contains(mediaNodeInstanceTypeLower, 'pls_v') || contains(mediaNodeInstanceTypeLower, 'pds_v') || contains(mediaNodeInstanceTypeLower, 'plds_v') || contains(mediaNodeInstanceTypeLower, 'psv') || contains(mediaNodeInstanceTypeLower, 'plsv') || contains(mediaNodeInstanceTypeLower, 'pdsv') || contains(mediaNodeInstanceTypeLower, 'pldsv') masterNodeInstanceTypeLower,
'pds_v'
) || contains(masterNodeInstanceTypeLower, 'plds_v') || contains(masterNodeInstanceTypeLower, 'psv') || contains(
masterNodeInstanceTypeLower,
'plsv'
) || contains(masterNodeInstanceTypeLower, 'pdsv') || contains(masterNodeInstanceTypeLower, 'pldsv')
var isMediaArm64 = contains(mediaNodeInstanceTypeLower, 'ps_v') || contains(mediaNodeInstanceTypeLower, 'pls_v') || contains(
mediaNodeInstanceTypeLower,
'pds_v'
) || contains(mediaNodeInstanceTypeLower, 'plds_v') || contains(mediaNodeInstanceTypeLower, 'psv') || contains(
mediaNodeInstanceTypeLower,
'plsv'
) || contains(mediaNodeInstanceTypeLower, 'pdsv') || contains(mediaNodeInstanceTypeLower, 'pldsv')
var masterUbuntuSku = isMasterArm64 ? 'server-arm64' : 'server' var masterUbuntuSku = isMasterArm64 ? 'server-arm64' : 'server'
var mediaUbuntuSku = isMediaArm64 ? 'server-arm64' : 'server' var mediaUbuntuSku = isMediaArm64 ? 'server-arm64' : 'server'
@ -145,8 +148,6 @@ var mediaNodeVMSettings = {
} }
} }
var turnTLSIsEnabled = turnDomainName != ''
var keyVaultName = '${stackName}-keyvault' var keyVaultName = '${stackName}-keyvault'
var location = resourceGroup().location var location = resourceGroup().location
@ -226,12 +227,9 @@ resource openviduSharedInfo 'Microsoft.KeyVault/vaults@2023-07-01' = {
var stringInterpolationParamsMaster1 = { var stringInterpolationParamsMaster1 = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
openviduLicense: openviduLicense openviduLicense: openviduLicense
rtcEngine: rtcEngine rtcEngine: rtcEngine
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
@ -244,12 +242,9 @@ var stringInterpolationParamsMaster1 = {
var stringInterpolationParamsMaster2 = { var stringInterpolationParamsMaster2 = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
openviduLicense: openviduLicense openviduLicense: openviduLicense
rtcEngine: rtcEngine rtcEngine: rtcEngine
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
@ -262,12 +257,9 @@ var stringInterpolationParamsMaster2 = {
var stringInterpolationParamsMaster3 = { var stringInterpolationParamsMaster3 = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
openviduLicense: openviduLicense openviduLicense: openviduLicense
rtcEngine: rtcEngine rtcEngine: rtcEngine
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
@ -280,12 +272,9 @@ var stringInterpolationParamsMaster3 = {
var stringInterpolationParamsMaster4 = { var stringInterpolationParamsMaster4 = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
openviduLicense: openviduLicense openviduLicense: openviduLicense
rtcEngine: rtcEngine rtcEngine: rtcEngine
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
@ -298,7 +287,7 @@ var stringInterpolationParamsMaster4 = {
var installScriptTemplateMaster = ''' var installScriptTemplateMaster = '''
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
# Assume azure cli is installed # Assume azure cli is installed
@ -361,7 +350,6 @@ if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "" || "$ALL_SE
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io" DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
else else
DOMAIN=${domainName} DOMAIN=${domainName}
fi fi
@ -380,16 +368,6 @@ if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "" || "$ALL_SE
MEET_INITIAL_API_KEY="$(/usr/local/bin/store_secret.sh save MEET-INITIAL-API-KEY "")" MEET_INITIAL_API_KEY="$(/usr/local/bin/store_secret.sh save MEET-INITIAL-API-KEY "")"
fi fi
# Configure TURN server domain name
if [[ -n "${turnDomainName}" ]]; then
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${turnDomainName}")"
elif [[ "${TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Store usernames and generate random passwords # Store usernames and generate random passwords
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU-PRO-LICENSE "${openviduLicense}")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU-PRO-LICENSE "${openviduLicense}")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU-RTC-ENGINE "${rtcEngine}")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU-RTC-ENGINE "${rtcEngine}")"
@ -435,9 +413,6 @@ MASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName}
MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP" MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP"
DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv) DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv)
if [[ -n "${turnDomainName}" ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv)
fi
OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv)
OPENVIDU_PRO_LICENSE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --query value -o tsv) OPENVIDU_PRO_LICENSE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --query value -o tsv)
REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)
@ -507,10 +482,6 @@ if [[ "${additionalInstallFlags}" != "" ]]; then
done done
fi fi
if [[ $LIVEKIT_TURN_DOMAIN_NAME != "" ]]; then
COMMON_ARGS+=("--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME")
fi
# Certificate arguments # Certificate arguments
if [[ "${certificateType}" == "selfsigned" ]]; then if [[ "${certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -530,18 +501,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command # Construct the final command
@ -559,12 +518,16 @@ az login --identity --allow-no-subscriptions > /dev/null
# Generate URLs # Generate URLs
DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv) DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv)
OPENVIDU_URL="https://${DOMAIN}/"
LIVEKIT_URL="wss://${DOMAIN}/"
DASHBOARD_URL="https://${DOMAIN}/dashboard/" DASHBOARD_URL="https://${DOMAIN}/dashboard/"
GRAFANA_URL="https://${DOMAIN}/grafana/" GRAFANA_URL="https://${DOMAIN}/grafana/"
MINIO_URL="https://${DOMAIN}/minio-console/" MINIO_URL="https://${DOMAIN}/minio-console/"
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-URL --value $OPENVIDU_URL
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-URL --value $LIVEKIT_URL
az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL
az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL
az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL
@ -594,12 +557,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)
export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv)
@ -645,12 +602,16 @@ fi
sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$ENABLED_MODULES/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$ENABLED_MODULES/" "${CLUSTER_CONFIG_DIR}/openvidu.env"
# Update URLs in secret # Update URLs in secret
OPENVIDU_URL="https://${DOMAIN}/"
LIVEKIT_URL="wss://${DOMAIN}/"
DASHBOARD_URL="https://${DOMAIN}/dashboard/" DASHBOARD_URL="https://${DOMAIN}/dashboard/"
GRAFANA_URL="https://${DOMAIN}/grafana/" GRAFANA_URL="https://${DOMAIN}/grafana/"
MINIO_URL="https://${DOMAIN}/minio-console/" MINIO_URL="https://${DOMAIN}/minio-console/"
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-URL --value $OPENVIDU_URL
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-URL --value $LIVEKIT_URL
az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL
az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL
az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL
@ -670,7 +631,6 @@ MASTER_NODE_CONFIG_DIR="${INSTALL_DIR}/config/node"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${MASTER_NODE_CONFIG_DIR}/master_node.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${MASTER_NODE_CONFIG_DIR}/master_node.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")"
@ -694,7 +654,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${CL
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --value $LIVEKIT_TURN_DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME
@ -903,9 +862,6 @@ var blobStorageParams = {
storageAccountName: isEmptyStorageAccountName ? storageAccount.name : existingStorageAccount.name storageAccountName: isEmptyStorageAccountName ? storageAccount.name : existingStorageAccount.name
storageAccountKey: listKeys(storageAccount.id, '2021-04-01').keys[0].value storageAccountKey: listKeys(storageAccount.id, '2021-04-01').keys[0].value
storageAccountContainerName: isEmptyAppDataContainerName ? 'openvidu-appdata' : '${appDataContainerName}' storageAccountContainerName: isEmptyAppDataContainerName ? 'openvidu-appdata' : '${appDataContainerName}'
storageAccountClusterContainerName: isEmptyClusterContainerName
? 'openvidu-clusterdata'
: '${clusterDataContainerName}'
} }
var config_blobStorageScript = reduce( var config_blobStorageScript = reduce(
@ -2784,44 +2740,6 @@ resource loadBalancerToMediaHealthcheckIngress 'Microsoft.Network/networkSecurit
} }
} }
resource loadBalancerToMediaTurnTlsIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = if (turnTLSIsEnabled == true) {
parent: openviduMediaNodeNSG
name: 'loadbalancer_to_mediaNode_TURN_TLS_INGRESS'
properties: {
protocol: 'Tcp'
sourceAddressPrefix: 'AzureLoadBalancer'
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMediaNodeASG.id
}
]
destinationPortRange: '5349'
access: 'Allow'
priority: 180
direction: 'Inbound'
}
}
resource loadBalancerToMediaTurnTlsHealthCheckIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = if (turnTLSIsEnabled == true) {
parent: openviduMediaNodeNSG
name: 'masterNode_to_mediaNode_TURN_TLSHEALTHCHECK_INGRESS'
properties: {
protocol: 'Tcp'
sourceAddressPrefix: 'AzureLoadBalancer'
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMediaNodeASG.id
}
]
destinationPortRange: '7880'
access: 'Allow'
priority: 190
direction: 'Inbound'
}
}
resource masterToMediaServerIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { resource masterToMediaServerIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMediaNodeNSG parent: openviduMediaNodeNSG
name: 'masterNode_to_mediaNode_SERVER_INGRESS' name: 'masterNode_to_mediaNode_SERVER_INGRESS'
@ -2868,6 +2786,29 @@ resource masterToMediaClientIngress 'Microsoft.Network/networkSecurityGroups/sec
} }
} }
resource masterToMediaTurnTlsIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMediaNodeNSG
name: 'masterNode_to_mediaNode_TURN_TLS_INGRESS'
properties: {
protocol: 'Tcp'
sourceApplicationSecurityGroups: [
{
id: openviduMasterNodeASG.id
}
]
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMediaNodeASG.id
}
]
destinationPortRange: '5349'
access: 'Allow'
priority: 220
direction: 'Inbound'
}
}
/*------------------------------------------- STORAGE ACCOUNT ----------------------------------------*/ /*------------------------------------------- STORAGE ACCOUNT ----------------------------------------*/
@description('Name of an existing storage account. It is essential that this parameter is filled just when you want to save recordings and still using the same container after an update. If not specified, a new storage account will be generated.') @description('Name of an existing storage account. It is essential that this parameter is filled just when you want to save recordings and still using the same container after an update. If not specified, a new storage account will be generated.')
@ -2913,18 +2854,4 @@ resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/container
} }
} }
@description('Name of the bucket where OpenVidu will store the recordings if a new Storage account is being creating. If not specified, a default bucket will be created. If you want to use an existing storage account, fill this parameter with the name of the container where the recordings are stored.')
param clusterDataContainerName string = ''
var isEmptyClusterContainerName = clusterDataContainerName == ''
resource clusterDatablobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = if (isEmptyStorageAccountName == true) {
name: isEmptyClusterContainerName
? '${storageAccount.name}/default/openvidu-clusterdata'
: '${storageAccount.name}/default/${clusterDataContainerName}'
properties: {
publicAccess: 'None'
}
}
/*------------------------------------------- OUTPUTS -------------------------------------------*/ /*------------------------------------------- OUTPUTS -------------------------------------------*/

File diff suppressed because one or more lines are too long

View File

@ -520,70 +520,6 @@
"visible": true "visible": true
} }
] ]
},
{
"name": "parameters TURN",
"label": "(Optional) TURN server configuration with TLS",
"elements": [
{
"name": "turnDomainName",
"type": "Microsoft.Common.TextBox",
"label": "Turn Domain Name",
"subLabel": "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPublicCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Public Certificate",
"subLabel": "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPublicCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPrivateCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Private Certificate",
"subLabel": "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPrivateCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
}
]
} }
] ]
}, },
@ -598,9 +534,6 @@
"ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]", "ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]",
"ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]", "ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]",
"publicIpAddressObject": "[steps('parameters SSL').publicIpAddressObject]", "publicIpAddressObject": "[steps('parameters SSL').publicIpAddressObject]",
"turnDomainName": "[steps('parameters TURN').turnDomainName]",
"turnOwnPublicCertificate": "[steps('parameters TURN').turnOwnPublicCertificate]",
"turnOwnPrivateCertificate": "[steps('parameters TURN').turnOwnPrivateCertificate]",
"openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]", "openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]",
"rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]", "rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]",
"initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]", "initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]",

View File

@ -16,7 +16,7 @@ resource "google_secret_manager_secret" "openvidu_shared_info" {
for_each = toset([ for_each = toset([
"OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD", "OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD",
"MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET", "MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET",
"DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME", "LIVEKIT_TURN_DOMAIN_NAME", "DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME",
"OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME", "OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME",
"MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY",
"DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME", "DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME",
@ -314,9 +314,6 @@ resource "google_compute_instance" "openvidu_master_node_1" {
certificateType = var.certificateType certificateType = var.certificateType
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
openviduLicense = var.openviduLicense openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword initialMeetAdminPassword = var.initialMeetAdminPassword
@ -367,9 +364,6 @@ resource "google_compute_instance" "openvidu_master_node_2" {
certificateType = var.certificateType certificateType = var.certificateType
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
openviduLicense = var.openviduLicense openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword initialMeetAdminPassword = var.initialMeetAdminPassword
@ -422,9 +416,6 @@ resource "google_compute_instance" "openvidu_master_node_3" {
certificateType = var.certificateType certificateType = var.certificateType
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
openviduLicense = var.openviduLicense openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword initialMeetAdminPassword = var.initialMeetAdminPassword
@ -477,9 +468,6 @@ resource "google_compute_instance" "openvidu_master_node_4" {
certificateType = var.certificateType certificateType = var.certificateType
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
openviduLicense = var.openviduLicense openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword initialMeetAdminPassword = var.initialMeetAdminPassword
@ -952,7 +940,7 @@ locals {
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
@ -995,7 +983,6 @@ if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "false" ]]; th
EXTERNAL_IP=$(gcloud compute addresses describe "${lower("${var.stackName}-nlb-ip")}" --region ${var.region} --format="get(address)") EXTERNAL_IP=$(gcloud compute addresses describe "${lower("${var.stackName}-nlb-ip")}" --region ${var.region} --format="get(address)")
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io" DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io"
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io"
else else
DOMAIN="${var.domainName}" DOMAIN="${var.domainName}"
fi fi
@ -1013,13 +1000,6 @@ if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "false" ]]; th
MEET_INITIAL_API_KEY="$(/usr/local/bin/store_secret.sh save MEET_INITIAL_API_KEY "${var.initialMeetApiKey}")" MEET_INITIAL_API_KEY="$(/usr/local/bin/store_secret.sh save MEET_INITIAL_API_KEY "${var.initialMeetApiKey}")"
fi fi
# Configure TURN server domain name
if [[ -n "${var.turnDomainName}" ]]; then
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${var.turnDomainName}")"
elif [[ "$${TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "$${TURN_DOMAIN_NAME_SSLIP_IO}")
fi
# Store usernames and generate random passwords # Store usernames and generate random passwords
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU_PRO_LICENSE "${var.openviduLicense}")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU_PRO_LICENSE "${var.openviduLicense}")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU_RTC_ENGINE "${var.rtcEngine}")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU_RTC_ENGINE "${var.rtcEngine}")"
@ -1065,7 +1045,6 @@ MASTER_NODE_4_PRIVATE_IP=$(gcloud secrets versions access latest --secret=MASTER
MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP" MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP"
DOMAIN=$(gcloud secrets versions access latest --secret=DOMAIN_NAME) DOMAIN=$(gcloud secrets versions access latest --secret=DOMAIN_NAME)
LIVEKIT_TURN_DOMAIN_NAME=$(gcloud secrets versions access latest --secret=LIVEKIT_TURN_DOMAIN_NAME)
OPENVIDU_PRO_LICENSE=$(gcloud secrets versions access latest --secret=OPENVIDU_PRO_LICENSE) OPENVIDU_PRO_LICENSE=$(gcloud secrets versions access latest --secret=OPENVIDU_PRO_LICENSE)
OPENVIDU_RTC_ENGINE=$(gcloud secrets versions access latest --secret=OPENVIDU_RTC_ENGINE) OPENVIDU_RTC_ENGINE=$(gcloud secrets versions access latest --secret=OPENVIDU_RTC_ENGINE)
REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD) REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD)
@ -1132,10 +1111,6 @@ if [[ "${var.additionalInstallFlags}" != "" ]]; then
done done
fi fi
if [[ "$LIVEKIT_TURN_DOMAIN_NAME" != "" ]]; then
COMMON_ARGS+=("--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME")
fi
# Certificate arguments # Certificate arguments
if [[ "${var.certificateType}" == "selfsigned" ]]; then if [[ "${var.certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -1155,18 +1130,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${var.turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${var.turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${var.turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command # Construct the final command
@ -1248,6 +1211,178 @@ if [[ $? -ne 0 ]]; then
fi fi
EOF EOF
update_config_from_secret_script = <<-EOF
#!/bin/bash -x
set -e
# Configure gcloud with instance service account
gcloud auth activate-service-account --key-file=/dev/null 2>/dev/null || true
# Installation directory
INSTALL_DIR="/opt/openvidu"
CLUSTER_CONFIG_DIR="$${INSTALL_DIR}/config/cluster"
MASTER_NODE_CONFIG_DIR="$${INSTALL_DIR}/config/node"
# Replace DOMAIN_NAME
export DOMAIN=$(gcloud secrets versions access latest --secret=DOMAIN_NAME)
if [[ -n "$DOMAIN" ]]; then
sed -i "s/DOMAIN_NAME=.*/DOMAIN_NAME=$DOMAIN/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
else
exit 1
fi
# Get the rest of the values
export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD)
export OPENVIDU_RTC_ENGINE=$(gcloud secrets versions access latest --secret=OPENVIDU_RTC_ENGINE)
export OPENVIDU_PRO_LICENSE=$(gcloud secrets versions access latest --secret=OPENVIDU_PRO_LICENSE)
export MONGO_ADMIN_USERNAME=$(gcloud secrets versions access latest --secret=MONGO_ADMIN_USERNAME)
export MONGO_ADMIN_PASSWORD=$(gcloud secrets versions access latest --secret=MONGO_ADMIN_PASSWORD)
export MONGO_REPLICA_SET_KEY=$(gcloud secrets versions access latest --secret=MONGO_REPLICA_SET_KEY)
export DASHBOARD_ADMIN_USERNAME=$(gcloud secrets versions access latest --secret=DASHBOARD_ADMIN_USERNAME)
export DASHBOARD_ADMIN_PASSWORD=$(gcloud secrets versions access latest --secret=DASHBOARD_ADMIN_PASSWORD)
export MINIO_ACCESS_KEY=$(gcloud secrets versions access latest --secret=MINIO_ACCESS_KEY)
export MINIO_SECRET_KEY=$(gcloud secrets versions access latest --secret=MINIO_SECRET_KEY)
export GRAFANA_ADMIN_USERNAME=$(gcloud secrets versions access latest --secret=GRAFANA_ADMIN_USERNAME)
export GRAFANA_ADMIN_PASSWORD=$(gcloud secrets versions access latest --secret=GRAFANA_ADMIN_PASSWORD)
export LIVEKIT_API_KEY=$(gcloud secrets versions access latest --secret=LIVEKIT_API_KEY)
export LIVEKIT_API_SECRET=$(gcloud secrets versions access latest --secret=LIVEKIT_API_SECRET)
export MEET_INITIAL_ADMIN_USER=$(gcloud secrets versions access latest --secret=MEET_INITIAL_ADMIN_USER)
export MEET_INITIAL_ADMIN_PASSWORD=$(gcloud secrets versions access latest --secret=MEET_INITIAL_ADMIN_PASSWORD)
if [[ "${var.initialMeetApiKey}" != '' ]]; then
export MEET_INITIAL_API_KEY=$(gcloud secrets versions access latest --secret=MEET_INITIAL_API_KEY)
fi
export ENABLED_MODULES=$(gcloud secrets versions access latest --secret=ENABLED_MODULES)
# Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$REDIS_PASSWORD/" "$${MASTER_NODE_CONFIG_DIR}/master_node.env"
sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$OPENVIDU_RTC_ENGINE/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$OPENVIDU_PRO_LICENSE/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$MONGO_ADMIN_USERNAME/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_ADMIN_PASSWORD=.*/MONGO_ADMIN_PASSWORD=$MONGO_ADMIN_PASSWORD/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_REPLICA_SET_KEY=.*/MONGO_REPLICA_SET_KEY=$MONGO_REPLICA_SET_KEY/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/DASHBOARD_ADMIN_USERNAME=.*/DASHBOARD_ADMIN_USERNAME=$DASHBOARD_ADMIN_USERNAME/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/DASHBOARD_ADMIN_PASSWORD=.*/DASHBOARD_ADMIN_PASSWORD=$DASHBOARD_ADMIN_PASSWORD/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MINIO_ACCESS_KEY=.*/MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MINIO_SECRET_KEY=.*/MINIO_SECRET_KEY=$MINIO_SECRET_KEY/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/GRAFANA_ADMIN_USERNAME=.*/GRAFANA_ADMIN_USERNAME=$GRAFANA_ADMIN_USERNAME/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/GRAFANA_ADMIN_PASSWORD=.*/GRAFANA_ADMIN_PASSWORD=$GRAFANA_ADMIN_PASSWORD/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/LIVEKIT_API_KEY=.*/LIVEKIT_API_KEY=$LIVEKIT_API_KEY/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/LIVEKIT_API_SECRET=.*/LIVEKIT_API_SECRET=$LIVEKIT_API_SECRET/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MEET_INITIAL_ADMIN_USER=.*/MEET_INITIAL_ADMIN_USER=$MEET_INITIAL_ADMIN_USER/" "$${CLUSTER_CONFIG_DIR}/master_node/meet.env"
sed -i "s/MEET_INITIAL_ADMIN_PASSWORD=.*/MEET_INITIAL_ADMIN_PASSWORD=$MEET_INITIAL_ADMIN_PASSWORD/" "$${CLUSTER_CONFIG_DIR}/master_node/meet.env"
if [[ "${var.initialMeetApiKey}" != '' ]]; then
sed -i "s/MEET_INITIAL_API_KEY=.*/MEET_INITIAL_API_KEY=$MEET_INITIAL_API_KEY/" "$${CLUSTER_CONFIG_DIR}/master_node/meet.env"
fi
sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$ENABLED_MODULES/" "$${CLUSTER_CONFIG_DIR}/openvidu.env"
# Update URLs in secret
OPENVIDU_URL="https://$${DOMAIN}/"
LIVEKIT_URL="wss://$${DOMAIN}/"
DASHBOARD_URL="https://$${DOMAIN}/dashboard/"
GRAFANA_URL="https://$${DOMAIN}/grafana/"
MINIO_URL="https://$${DOMAIN}/minio-console/"
# Update shared secret
echo -n "$DOMAIN" | gcloud secrets versions add DOMAIN_NAME --data-file=-
echo -n "$OPENVIDU_URL" | gcloud secrets versions add OPENVIDU_URL --data-file=-
echo -n "$LIVEKIT_URL" | gcloud secrets versions add LIVEKIT_URL --data-file=-
echo -n "$DASHBOARD_URL" | gcloud secrets versions add DASHBOARD_URL --data-file=-
echo -n "$GRAFANA_URL" | gcloud secrets versions add GRAFANA_URL --data-file=-
echo -n "$MINIO_URL" | gcloud secrets versions add MINIO_URL --data-file=-
EOF
update_secret_from_config_script = <<-EOF
#!/bin/bash
set -e
# Configure gcloud with instance service account
gcloud auth activate-service-account --key-file=/dev/null 2>/dev/null || true
# Installation directory
INSTALL_DIR="/opt/openvidu"
CLUSTER_CONFIG_DIR="$${INSTALL_DIR}/config/cluster"
MASTER_NODE_CONFIG_DIR="$${INSTALL_DIR}/config/node"
# Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${MASTER_NODE_CONFIG_DIR}/master_node.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MINIO_ACCESS_KEY="$(/usr/local/bin/get_value_from_config.sh MINIO_ACCESS_KEY "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MINIO_SECRET_KEY="$(/usr/local/bin/get_value_from_config.sh MINIO_SECRET_KEY "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
DASHBOARD_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_USERNAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
DASHBOARD_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_PASSWORD "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
GRAFANA_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_USERNAME "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
GRAFANA_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_PASSWORD "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
LIVEKIT_API_KEY="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_KEY "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
LIVEKIT_API_SECRET="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_SECRET "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
MEET_INITIAL_ADMIN_USER="$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_ADMIN_USER "$${CLUSTER_CONFIG_DIR}/master_node/meet.env")"
MEET_INITIAL_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_ADMIN_PASSWORD "$${CLUSTER_CONFIG_DIR}/master_node/meet.env")"
if [[ "${var.initialMeetApiKey}" != '' ]]; then
MEET_INITIAL_API_KEY="$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_API_KEY "$${CLUSTER_CONFIG_DIR}/master_node/meet.env")"
fi
ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "$${CLUSTER_CONFIG_DIR}/openvidu.env")"
# Update shared secret
echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-file=-
echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=-
echo -n "$OPENVIDU_RTC_ENGINE" | gcloud secrets versions add OPENVIDU_RTC_ENGINE --data-file=-
echo -n "$OPENVIDU_PRO_LICENSE" | gcloud secrets versions add OPENVIDU_PRO_LICENSE --data-file=-
echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=-
echo -n "$MONGO_ADMIN_PASSWORD" | gcloud secrets versions add MONGO_ADMIN_PASSWORD --data-file=-
echo -n "$MONGO_REPLICA_SET_KEY" | gcloud secrets versions add MONGO_REPLICA_SET_KEY --data-file=-
echo -n "$MINIO_ACCESS_KEY" | gcloud secrets versions add MINIO_ACCESS_KEY --data-file=-
echo -n "$MINIO_SECRET_KEY" | gcloud secrets versions add MINIO_SECRET_KEY --data-file=-
echo -n "$DASHBOARD_ADMIN_USERNAME" | gcloud secrets versions add DASHBOARD_ADMIN_USERNAME --data-file=-
echo -n "$DASHBOARD_ADMIN_PASSWORD" | gcloud secrets versions add DASHBOARD_ADMIN_PASSWORD --data-file=-
echo -n "$GRAFANA_ADMIN_USERNAME" | gcloud secrets versions add GRAFANA_ADMIN_USERNAME --data-file=-
echo -n "$GRAFANA_ADMIN_PASSWORD" | gcloud secrets versions add GRAFANA_ADMIN_PASSWORD --data-file=-
echo -n "$LIVEKIT_API_KEY" | gcloud secrets versions add LIVEKIT_API_KEY --data-file=-
echo -n "$LIVEKIT_API_SECRET" | gcloud secrets versions add LIVEKIT_API_SECRET --data-file=-
echo -n "$MEET_INITIAL_ADMIN_USER" | gcloud secrets versions add MEET_INITIAL_ADMIN_USER --data-file=-
echo -n "$MEET_INITIAL_ADMIN_PASSWORD" | gcloud secrets versions add MEET_INITIAL_ADMIN_PASSWORD --data-file=-
if [[ "${var.initialMeetApiKey}" != '' ]]; then
echo -n "$MEET_INITIAL_API_KEY" | gcloud secrets versions add MEET_INITIAL_API_KEY --data-file=-
fi
echo -n "$ENABLED_MODULES" | gcloud secrets versions add ENABLED_MODULES --data-file=-
EOF
get_value_from_config_script = <<-EOF
#!/bin/bash -x
set -e
# Function to get the value of a given key from the environment file
get_value() {
local key="$1"
local file_path="$2"
# Use grep to find the line with the key, ignoring lines starting with #
# Use awk to split on '=' and print the second field, which is the value
local value=$(grep -E "^\s*$key\s*=" "$file_path" | awk -F= '{print $2}' | sed 's/#.*//; s/^\s*//; s/\s*$//')
# If the value is empty, return "none"
if [ -z "$value" ]; then
echo "none"
else
echo "$value"
fi
}
# Check if the correct number of arguments are supplied
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <key> <file_path>"
exit 1
fi
# Get the key and file path from the arguments
key="$1"
file_path="$2"
# Get and print the value
get_value "$key" "$file_path"
EOF
store_secret_script = <<-EOF store_secret_script = <<-EOF
#!/bin/bash #!/bin/bash
set -e set -e
@ -1326,6 +1461,25 @@ ${local.after_install_script}
AFTER_INSTALL_EOF AFTER_INSTALL_EOF
chmod +x /usr/local/bin/after_install.sh chmod +x /usr/local/bin/after_install.sh
# update_config_from_secret.sh
cat > /usr/local/bin/update_config_from_secret.sh << 'UPDATE_CONFIG_EOF'
${local.update_config_from_secret_script}
UPDATE_CONFIG_EOF
chmod +x /usr/local/bin/update_config_from_secret.sh
# update_secret_from_config.sh
cat > /usr/local/bin/update_secret_from_config.sh << 'UPDATE_SECRET_EOF'
${local.update_secret_from_config_script}
UPDATE_SECRET_EOF
chmod +x /usr/local/bin/update_secret_from_config.sh
# get_value_from_config.sh
cat > /usr/local/bin/get_value_from_config.sh << 'GET_VALUE_EOF'
${local.get_value_from_config_script}
GET_VALUE_EOF
chmod +x /usr/local/bin/get_value_from_config.sh
cat > /usr/local/bin/store_secret.sh << 'STORE_SECRET_EOF' cat > /usr/local/bin/store_secret.sh << 'STORE_SECRET_EOF'
${local.store_secret_script} ${local.store_secret_script}
STORE_SECRET_EOF STORE_SECRET_EOF

View File

@ -122,7 +122,7 @@ variable "minNumberOfMediaNodes" {
variable "maxNumberOfMediaNodes" { variable "maxNumberOfMediaNodes" {
description = "Maximum number of media nodes to deploy" description = "Maximum number of media nodes to deploy"
type = number type = number
default = 2 default = 5
} }
variable "scaleTargetCPU" { variable "scaleTargetCPU" {
@ -168,21 +168,3 @@ variable "additionalInstallFlags" {
error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)." error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)."
} }
} }
variable "turnDomainName" {
description = "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls"
type = string
default = ""
}
variable "turnOwnPublicCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}
variable "turnOwnPrivateCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -1,6 +1,6 @@
services: services:
caddy-proxy: caddy-proxy:
image: docker.io/openvidu/openvidu-caddy-local:3.5.0 image: docker.io/openvidu/openvidu-caddy-local:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -26,6 +26,7 @@ services:
- 7443:7443 - 7443:7443
- 7880:7880 - 7880:7880
- 9443:9443 - 9443:9443
- 9080:9080
depends_on: depends_on:
setup: setup:
condition: service_completed_successfully condition: service_completed_successfully
@ -86,7 +87,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
dashboard: dashboard:
image: docker.io/openvidu/openvidu-dashboard:3.5.0 image: docker.io/openvidu/openvidu-dashboard:main
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
@ -100,7 +101,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu: openvidu:
image: docker.io/openvidu/openvidu-server-pro:3.5.0 image: docker.io/openvidu/openvidu-server-pro:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -123,7 +124,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
ingress: ingress:
image: docker.io/openvidu/ingress:3.5.0 image: docker.io/openvidu/ingress:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -141,7 +142,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
egress: egress:
image: docker.io/openvidu/egress:3.5.0 image: docker.io/openvidu/egress:main
restart: unless-stopped restart: unless-stopped
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
@ -155,7 +156,7 @@ services:
setup: setup:
condition: service_completed_successfully condition: service_completed_successfully
operator: operator:
image: docker.io/openvidu/openvidu-operator:3.5.0 image: docker.io/openvidu/openvidu-operator:main
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
@ -179,10 +180,8 @@ services:
openvidu-meet: openvidu-meet:
image: docker.io/openvidu/openvidu-meet:3.5.0 image: docker.io/openvidu/openvidu-meet:main
restart: on-failure restart: on-failure
ports:
- 9080:6080
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
environment: environment:
@ -220,7 +219,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu-v2compatibility: openvidu-v2compatibility:
image: docker.io/openvidu/openvidu-v2compatibility:3.5.0 image: docker.io/openvidu/openvidu-v2compatibility:main
restart: unless-stopped restart: unless-stopped
entrypoint: /bin/sh /scripts/entrypoint_openvidu_v2_compat.sh entrypoint: /bin/sh /scripts/entrypoint_openvidu_v2_compat.sh
extra_hosts: extra_hosts:
@ -257,7 +256,7 @@ services:
condition: service_completed_successfully condition: service_completed_successfully
openvidu-meet-init: openvidu-meet-init:
image: docker.io/openvidu/openvidu-operator:3.5.0 image: docker.io/openvidu/openvidu-operator:main
restart: on-failure restart: on-failure
environment: environment:
- MODE=local-ready-check - MODE=local-ready-check

View File

@ -60,20 +60,6 @@ Parameters:
AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen
ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag). ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag).
TurnDomainName:
Description: '(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls'
Type: String
Default: ''
TurnOwnPublicCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
TurnOwnPrivateCertificate:
Description: "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
Type: String
Default: ''
OpenViduLicense: OpenViduLicense:
Description: "Visit https://openvidu.io/account" Description: "Visit https://openvidu.io/account"
@ -476,12 +462,6 @@ Metadata:
default: "(Optional) Additional Installer Flags" default: "(Optional) Additional Installer Flags"
Parameters: Parameters:
- AdditionalInstallFlags - AdditionalInstallFlags
- Label:
default: (Optional) TURN server configuration with TLS
Parameters:
- TurnDomainName
- TurnOwnPublicCertificate
- TurnOwnPrivateCertificate
Conditions: Conditions:
PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ] PublicElasticIPPresent: !Not [ !Equals [!Ref PublicElasticIP, ""] ]
@ -534,7 +514,6 @@ Resources:
"GRAFANA_URL": "none", "GRAFANA_URL": "none",
"MINIO_URL": "none", "MINIO_URL": "none",
"DOMAIN_NAME": "none", "DOMAIN_NAME": "none",
"LIVEKIT_TURN_DOMAIN_NAME": "none",
"OPENVIDU_PRO_LICENSE": "none", "OPENVIDU_PRO_LICENSE": "none",
"OPENVIDU_RTC_ENGINE": "none", "OPENVIDU_RTC_ENGINE": "none",
"REDIS_PASSWORD": "none", "REDIS_PASSWORD": "none",
@ -639,7 +618,7 @@ Resources:
'/usr/local/bin/install.sh': '/usr/local/bin/install.sh':
content: !Sub | content: !Sub |
#!/bin/bash -x #!/bin/bash -x
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
@ -665,7 +644,6 @@ Resources:
PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) PublicHostname=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname)
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PublicHostname" | cut -d'.' -f1 | sed 's/^ec2-//').sslip.io"
else else
DOMAIN=${DomainName} DOMAIN=${DomainName}
fi fi
@ -743,18 +721,6 @@ Resources:
done done
fi fi
if [[ "${!TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${!TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TurnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${CertificateType}" == "selfsigned" ]]; then if [[ "${CertificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -774,18 +740,6 @@ Resources:
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${TurnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${TurnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${TurnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -882,12 +836,6 @@ Resources:
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CONFIG_DIR}/openvidu.env"
fi
# Replace rest of the values # Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!CONFIG_DIR}/openvidu.env"
sed -i "s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_PRO_LICENSE)/" "${!CONFIG_DIR}/openvidu.env" sed -i "s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_PRO_LICENSE)/" "${!CONFIG_DIR}/openvidu.env"
@ -948,7 +896,6 @@ Resources:
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_REPLICA_SET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')" SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_REPLICA_SET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${!CONFIG_DIR}/openvidu.env")"'"}')"
@ -1144,7 +1091,7 @@ Resources:
BlockDeviceMappings: BlockDeviceMappings:
- DeviceName: /dev/sda1 - DeviceName: /dev/sda1
Ebs: Ebs:
VolumeType: gp2 VolumeType: gp3
DeleteOnTermination: true DeleteOnTermination: true
VolumeSize: 200 VolumeSize: 200

View File

@ -33,15 +33,6 @@ param initialMeetAdminPassword string = ''
@secure() @secure()
param initialMeetApiKey string = '' param initialMeetApiKey string = ''
@description('(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls')
param turnDomainName string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPublicCertificate string = ''
@description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified. Provide in base64 format.')
param turnOwnPrivateCertificate string = ''
@description('Visit https://openvidu.io/account') @description('Visit https://openvidu.io/account')
@secure() @secure()
param openviduLicense string param openviduLicense string
@ -170,12 +161,9 @@ resource openviduSharedInfo 'Microsoft.KeyVault/vaults@2023-07-01' = {
var stringInterpolationParams = { var stringInterpolationParams = {
publicIPId: publicIPId publicIPId: publicIPId
domainName: domainName domainName: domainName
turnDomainName: turnDomainName
certificateType: certificateType certificateType: certificateType
ownPublicCertificate: ownPublicCertificate ownPublicCertificate: ownPublicCertificate
ownPrivateCertificate: ownPrivateCertificate ownPrivateCertificate: ownPrivateCertificate
turnOwnPublicCertificate: turnOwnPublicCertificate
turnOwnPrivateCertificate: turnOwnPrivateCertificate
initialMeetAdminPassword: initialMeetAdminPassword initialMeetAdminPassword: initialMeetAdminPassword
initialMeetApiKey: initialMeetApiKey initialMeetApiKey: initialMeetApiKey
keyVaultName: keyVaultName keyVaultName: keyVaultName
@ -186,7 +174,7 @@ var stringInterpolationParams = {
var installScriptTemplate = ''' var installScriptTemplate = '''
#!/bin/bash -x #!/bin/bash -x
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout
@ -208,7 +196,6 @@ if [[ "${domainName}" == '' ]]; then
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io" DOMAIN="openvidu-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
TURN_DOMAIN_NAME_SSLIP_IO="turn-$RANDOM_DOMAIN_STRING-$(echo "$PUBLIC_IP" | tr '.' '-').sslip.io"
else else
DOMAIN=${domainName} DOMAIN=${domainName}
fi fi
@ -286,19 +273,6 @@ if [[ "${additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "${turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${TURN_DOMAIN_NAME_SSLIP_IO}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT-TURN-DOMAIN-NAME "${TURN_DOMAIN_NAME_SSLIP_IO}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${certificateType}" == "selfsigned" ]]; then if [[ "${certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -318,18 +292,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Construct the final command with all arguments # Construct the final command with all arguments
@ -387,12 +349,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)
export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv)
@ -469,7 +425,6 @@ CONFIG_DIR="${INSTALL_DIR}/config"
# Get current values of the config # Get current values of the config
REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${CONFIG_DIR}/openvidu.env")" REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${CONFIG_DIR}/openvidu.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CONFIG_DIR}/openvidu.env")"
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CONFIG_DIR}/openvidu.env")"
@ -494,7 +449,6 @@ ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${CO
# Update shared secret # Update shared secret
az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD
az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --value $LIVEKIT_TURN_DOMAIN_NAME
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE
az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE
az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME

File diff suppressed because one or more lines are too long

View File

@ -381,70 +381,6 @@
"visible": true "visible": true
} }
] ]
},
{
"name": "parameters TURN",
"label": "(Optional) TURN server configuration with TLS",
"elements": [
{
"name": "turnDomainName",
"type": "Microsoft.Common.TextBox",
"label": "Turn Domain Name",
"subLabel": "\n(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPublicCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Public Certificate",
"subLabel": "\n(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPublicCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "turnOwnPrivateCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Turn Own Private Certificate",
"subLabel": "\n(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(and(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters TURN').turnDomainName))), not(empty(steps('parameters TURN').turnOwnPrivateCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type and you've filled Turn Domain Name."
}
]
},
"infoMessages": [],
"visible": true
}
]
} }
] ]
}, },
@ -461,9 +397,6 @@
"ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]", "ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]",
"openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]", "openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]",
"rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]", "rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]",
"turnDomainName": "[steps('parameters TURN').turnDomainName]",
"turnOwnPublicCertificate": "[steps('parameters TURN').turnOwnPublicCertificate]",
"turnOwnPrivateCertificate": "[steps('parameters TURN').turnOwnPrivateCertificate]",
"initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]", "initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]",
"initialMeetApiKey": "[steps('parameters MEET').initialMeetApiKey]", "initialMeetApiKey": "[steps('parameters MEET').initialMeetApiKey]",
"instanceType": "[steps('parameters INSTANCE').instanceType]", "instanceType": "[steps('parameters INSTANCE').instanceType]",

View File

@ -13,7 +13,7 @@ resource "google_secret_manager_secret" "openvidu_shared_info" {
for_each = toset([ for_each = toset([
"OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD", "OPENVIDU_URL", "MEET_INITIAL_ADMIN_USER", "MEET_INITIAL_ADMIN_PASSWORD",
"MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET", "MEET_INITIAL_API_KEY", "LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET",
"DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME", "LIVEKIT_TURN_DOMAIN_NAME", "DASHBOARD_URL", "GRAFANA_URL", "MINIO_URL", "DOMAIN_NAME",
"OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME", "OPENVIDU_PRO_LICENSE", "OPENVIDU_RTC_ENGINE", "REDIS_PASSWORD", "MONGO_ADMIN_USERNAME",
"MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", "MONGO_ADMIN_PASSWORD", "MONGO_REPLICA_SET_KEY", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY",
"DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME", "DASHBOARD_ADMIN_USERNAME", "DASHBOARD_ADMIN_PASSWORD", "GRAFANA_ADMIN_USERNAME",
@ -117,9 +117,6 @@ resource "google_compute_instance" "openvidu_server" {
ownPublicCertificate = var.ownPublicCertificate ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate ownPrivateCertificate = var.ownPrivateCertificate
additionalInstallFlags = var.additionalInstallFlags additionalInstallFlags = var.additionalInstallFlags
turnDomainName = var.turnDomainName
turnOwnPublicCertificate = var.turnOwnPublicCertificate
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName
} }
@ -146,7 +143,7 @@ locals {
#!/bin/bash -x #!/bin/bash -x
set -e set -e
OPENVIDU_VERSION=3.5.0 OPENVIDU_VERSION=main
DOMAIN= DOMAIN=
YQ_VERSION=v4.44.5 YQ_VERSION=v4.44.5
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout
@ -178,7 +175,6 @@ if [[ "${var.domainName}" == "" ]]; then
EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip") EXTERNAL_IP=$(get_meta "instance/network-interfaces/0/access-configs/0/external-ip")
RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8) RANDOM_DOMAIN_STRING=$(tr -dc 'a-z' < /dev/urandom | head -c 8)
DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io DOMAIN=openvidu-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
TURN_DOMAIN_NAME_SSLIP_IO=turn-$RANDOM_DOMAIN_STRING-$(echo $EXTERNAL_IP | tr '.' '-').sslip.io
else else
DOMAIN="${var.domainName}" DOMAIN="${var.domainName}"
fi fi
@ -254,19 +250,6 @@ if [[ "${var.additionalInstallFlags}" != "" ]]; then
done done
fi fi
# Turn with TLS
if [[ "$TURN_DOMAIN_NAME_SSLIP_IO" != "" ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "$TURN_DOMAIN_NAME_SSLIP_IO")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
elif [[ "${var.turnDomainName}" != '' ]]; then
LIVEKIT_TURN_DOMAIN_NAME=$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${var.turnDomainName}")
COMMON_ARGS+=(
"--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME"
)
fi
# Certificate arguments # Certificate arguments
if [[ "${var.certificateType}" == "selfsigned" ]]; then if [[ "${var.certificateType}" == "selfsigned" ]]; then
CERT_ARGS=( CERT_ARGS=(
@ -285,17 +268,6 @@ else
"--owncert-public-key=$OWN_CERT_CRT" "--owncert-public-key=$OWN_CERT_CRT"
"--owncert-private-key=$OWN_CERT_KEY" "--owncert-private-key=$OWN_CERT_KEY"
) )
# Turn with TLS and own certificate
if [[ "${var.turnDomainName}" != '' ]]; then
# Use base64 encoded certificates directly
OWN_CERT_CRT_TURN=${var.turnOwnPublicCertificate}
OWN_CERT_KEY_TURN=${var.turnOwnPrivateCertificate}
CERT_ARGS+=(
"--turn-owncert-private-key=$OWN_CERT_KEY_TURN"
"--turn-owncert-public-key=$OWN_CERT_CRT_TURN"
)
fi
fi fi
# Final command # Final command
@ -394,12 +366,6 @@ else
exit 1 exit 1
fi fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(gcloud secrets versions access latest --secret=LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "$${CONFIG_DIR}/openvidu.env"
fi
# Get the rest of the values # Get the rest of the values
export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD) export REDIS_PASSWORD=$(gcloud secrets versions access latest --secret=REDIS_PASSWORD)
export OPENVIDU_PRO_LICENSE=$(gcloud secrets versions access latest --secret=OPENVIDU_PRO_LICENSE) export OPENVIDU_PRO_LICENSE=$(gcloud secrets versions access latest --secret=OPENVIDU_PRO_LICENSE)
@ -476,7 +442,6 @@ REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "$${CON
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "$${CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "$${CONFIG_DIR}/openvidu.env")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "$${CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "$${CONFIG_DIR}/openvidu.env")"
DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")"
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "$${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "$${CONFIG_DIR}/openvidu.env")"
MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "$${CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "$${CONFIG_DIR}/openvidu.env")"
MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "$${CONFIG_DIR}/openvidu.env")" MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "$${CONFIG_DIR}/openvidu.env")"
@ -500,7 +465,6 @@ echo -n "$REDIS_PASSWORD" | gcloud secrets versions add REDIS_PASSWORD --data-fi
echo -n "$OPENVIDU_PRO_LICENSE" | gcloud secrets versions add OPENVIDU_PRO_LICENSE --data-file=- echo -n "$OPENVIDU_PRO_LICENSE" | gcloud secrets versions add OPENVIDU_PRO_LICENSE --data-file=-
echo -n "$OPENVIDU_RTC_ENGINE" | gcloud secrets versions add OPENVIDU_RTC_ENGINE --data-file=- echo -n "$OPENVIDU_RTC_ENGINE" | gcloud secrets versions add OPENVIDU_RTC_ENGINE --data-file=-
echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=- echo -n "$DOMAIN_NAME" | gcloud secrets versions add DOMAIN_NAME --data-file=-
echo -n "$LIVEKIT_TURN_DOMAIN_NAME" | gcloud secrets versions add LIVEKIT_TURN_DOMAIN_NAME --data-file=-
echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=- echo -n "$MONGO_ADMIN_USERNAME" | gcloud secrets versions add MONGO_ADMIN_USERNAME --data-file=-
echo -n "$MONGO_ADMIN_PASSWORD" | gcloud secrets versions add MONGO_ADMIN_PASSWORD --data-file=- echo -n "$MONGO_ADMIN_PASSWORD" | gcloud secrets versions add MONGO_ADMIN_PASSWORD --data-file=-
echo -n "$MONGO_REPLICA_SET_KEY" | gcloud secrets versions add MONGO_REPLICA_SET_KEY --data-file=- echo -n "$MONGO_REPLICA_SET_KEY" | gcloud secrets versions add MONGO_REPLICA_SET_KEY --data-file=-

View File

@ -122,21 +122,3 @@ variable "additionalInstallFlags" {
error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)." error_message = "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)."
} }
} }
variable "turnDomainName" {
description = "(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls"
type = string
default = ""
}
variable "turnOwnPublicCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}
variable "turnOwnPrivateCertificate" {
description = "(Optional) This setting is applicable if the certificate type is set to 'owncert' and the TurnDomainName is specified. Provide in base64 format."
type = string
default = ""
}

View File

@ -3,7 +3,7 @@
set -eu set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}" export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"

View File

@ -3,7 +3,7 @@ set -eu
export INSTALL_PREFIX="${INSTALL_PREFIX:-/opt/openvidu}" export INSTALL_PREFIX="${INSTALL_PREFIX:-/opt/openvidu}"
export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}" export DOCKER_VERSION="${DOCKER_VERSION:-29.0.2}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}" export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v2.40.3}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.5.0}" export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export UPDATER_IMAGE="${UPDATER_IMAGE:-docker.io/openvidu/openvidu-updater:${OPENVIDU_VERSION}}" export UPDATER_IMAGE="${UPDATER_IMAGE:-docker.io/openvidu/openvidu-updater:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}" export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.9.7-debian-12-r3}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}" export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/minio/mc:RELEASE.2025-08-13T08-35-41Z}"
@ -19,7 +19,7 @@ export OPENVIDU_MEET_SERVER_IMAGE="${OPENVIDU_MEET_SERVER_IMAGE:-docker.io/openv
export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_PRO_IMAGE="${OPENVIDU_DASHBOARD_PRO_IMAGE:-docker.io/openvidu/openvidu-pro-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}" export OPENVIDU_DASHBOARD_IMAGE="${OPENVIDU_DASHBOARD_IMAGE:-docker.io/openvidu/openvidu-dashboard:${OPENVIDU_VERSION}}"
export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}" export OPENVIDU_V2COMPATIBILITY_IMAGE="${OPENVIDU_V2COMPATIBILITY_IMAGE:-docker.io/openvidu/openvidu-v2compatibility:${OPENVIDU_VERSION}}"
export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing:${OPENVIDU_VERSION}}" export OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE="${OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE:-docker.io/openvidu/agent-speech-processing-cloud:${OPENVIDU_VERSION}}"
export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}" export LIVEKIT_INGRESS_SERVER_IMAGE="${LIVEKIT_INGRESS_SERVER_IMAGE:-docker.io/openvidu/ingress:${OPENVIDU_VERSION}}"
export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}" export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/openvidu/egress:${OPENVIDU_VERSION}}"
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}" export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.7.1}"