Compare commits

...

12 Commits

Author SHA1 Message Date
pabloFuente 10e643ce86 openvidu-test-e2e: refactor FFMPEG_VIDEO_CODEC_NAMES to use Triple for enhanced codec information 2026-03-18 18:27:30 +01:00
pabloFuente b704a9ceea Update action versions 2026-03-18 15:51:40 +01:00
CSantosM 2d2da5a659 ov-components: replace @Input decorators with input() for toolbar media buttons 2026-03-17 16:26:55 +01:00
CSantosM ebac5b30c2 ov-components: enhance local participant update logic with snapshot tracking 2026-03-16 15:52:20 +01:00
CSantosM 34e34ee078 ov-components: enhance screenshare button functionality for Firefox
- Updated the screenshare button in the toolbar to behave differently based on the browser type (Firefox vs others).
- Added an input property `isFirefoxBrowser` to the `ToolbarMediaButtonsComponent` to determine the browser type.
- Modified the HTML template to conditionally render the screenshare button and its menu based on the browser.
- Improved the `switchScreenshare` method in the `ParticipantService` to handle errors more gracefully and ensure the local participant is defined before proceeding.
- Refactored the `switchScreenshare` method in the `ParticipantModel` to reject promises with meaningful messages when no active screen share track is found.
2026-03-16 13:39:29 +01:00
cruizba 87b3ac3716 openvidu-deployment: add custom application ingress rules for media and master nodes 2026-03-13 17:46:29 +01:00
cruizba 677862f5eb openvidu-deployment: fix AWS HA HTTP listener target 2026-03-12 21:19:15 +01:00
Carlos Ruiz Ballesteros 28ca0c95cf
Merge pull request #864 from OpenVidu/chore/update-third-party-versions-8
chore: update third-party versions
2026-03-12 17:31:24 +01:00
cruizba 8e643670f4 chore: update third-party versions 2026-03-12 13:15:13 +00:00
Carlos Ruiz Ballesteros 3bed3c13f9
Merge pull request #863 from OpenVidu/chore/update-third-party-versions-7
chore: update third-party versions
2026-03-11 11:20:16 +01:00
cruizba 234a155894 chore: update third-party versions 2026-03-11 10:14:44 +00:00
GitHub Actions 6f1486493a Revert "Bump version to 3.6.0"
This reverts commit 885cac991b.
2026-03-06 10:45:29 +00:00
36 changed files with 3097 additions and 1736 deletions

View File

@ -27,11 +27,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
- name: Commit URL
@ -86,11 +86,11 @@ jobs:
script: e2e:lib-virtual-backgrounds
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install wait-on package

View File

@ -26,14 +26,14 @@ jobs:
curl -sSL https://get.livekit.io/cli | bash
- name: Checkout current repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: openvidu
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
- name: Install dependencies
working-directory: ./openvidu/openvidu-test-integration
@ -44,7 +44,7 @@ jobs:
run: npm run test:ci
- name: Upload report
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: always()
with:
name: openvidu-integration-tests-report

File diff suppressed because it is too large Load Diff

View File

@ -4,14 +4,14 @@
id="camera-btn"
mat-icon-button
(click)="onCameraToggle()"
[disabled]="isConnectionLost || !hasVideoDevices || cameraMuteChanging"
[class.disabled]="!isCameraEnabled"
[disabled]="isConnectionLost() || !hasVideoDevices() || cameraMuteChanging()"
[class.disabled]="!isCameraEnabled()"
[class.mobile-btn]="isMobileView()"
[matTooltip]="isCameraEnabled ? ('TOOLBAR.STOP_VIDEO' | translate) : ('TOOLBAR.START_VIDEO' | translate)"
[matTooltipDisabled]="!hasVideoDevices"
[matTooltip]="isCameraEnabled() ? ('TOOLBAR.STOP_VIDEO' | translate) : ('TOOLBAR.START_VIDEO' | translate)"
[matTooltipDisabled]="!hasVideoDevices()"
>
<mat-icon [id]="isCameraEnabled ? 'videocam' : 'videocam_off'">
{{ isCameraEnabled ? 'videocam' : 'videocam_off' }}
<mat-icon [id]="isCameraEnabled() ? 'videocam' : 'videocam_off'">
{{ isCameraEnabled() ? 'videocam' : 'videocam_off' }}
</mat-icon>
</button>
}
@ -22,50 +22,64 @@
id="mic-btn"
mat-icon-button
(click)="onMicrophoneToggle()"
[disabled]="isConnectionLost || !hasAudioDevices || microphoneMuteChanging"
[class.disabled]="!isMicrophoneEnabled"
[disabled]="isConnectionLost() || !hasAudioDevices() || microphoneMuteChanging()"
[class.disabled]="!isMicrophoneEnabled()"
[class.mobile-btn]="isMobileView()"
[matTooltip]="isMicrophoneEnabled ? ('TOOLBAR.MUTE_AUDIO' | translate) : ('TOOLBAR.UNMUTE_AUDIO' | translate)"
[matTooltipDisabled]="!hasAudioDevices"
[matTooltip]="isMicrophoneEnabled() ? ('TOOLBAR.MUTE_AUDIO' | translate) : ('TOOLBAR.UNMUTE_AUDIO' | translate)"
[matTooltipDisabled]="!hasAudioDevices()"
>
<mat-icon [id]="isMicrophoneEnabled ? 'mic' : 'mic_off'">
{{ isMicrophoneEnabled ? 'mic' : 'mic_off' }}
<mat-icon [id]="isMicrophoneEnabled() ? 'mic' : 'mic_off'">
{{ isMicrophoneEnabled() ? 'mic' : 'mic_off' }}
</mat-icon>
</button>
}
<!-- Screenshare button - Only visible on tablet+ or when active -->
@if (showScreenshareButtonDirect()) {
<button
mat-icon-button
id="screenshare-btn"
[matMenuTriggerFor]="isScreenShareEnabled ? screenshareMenu : null"
[disabled]="isConnectionLost"
[class.active-btn]="isScreenShareEnabled"
[class.mobile-btn]="isMobileView()"
matTooltip="{{ isScreenShareEnabled ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
(click)="!isScreenShareEnabled && onScreenShareToggle()"
>
<mat-icon>screen_share</mat-icon>
</button>
@if (isFirefoxBrowser()) {
<button
mat-icon-button
id="screenshare-btn"
[disabled]="isConnectionLost()"
[class.active-btn]="isScreenShareEnabled()"
[class.mobile-btn]="isMobileView()"
matTooltip="{{ isScreenShareEnabled() ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
(click)="onScreenShareToggle()"
>
<mat-icon>screen_share</mat-icon>
</button>
} @else {
<button
mat-icon-button
id="screenshare-btn"
[matMenuTriggerFor]="isScreenShareEnabled() ? screenshareMenu : null"
[disabled]="isConnectionLost()"
[class.active-btn]="isScreenShareEnabled()"
[class.mobile-btn]="isMobileView()"
matTooltip="{{ isScreenShareEnabled() ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
(click)="!isScreenShareEnabled() && onScreenShareToggle()"
>
<mat-icon>screen_share</mat-icon>
</button>
<!-- Screenshare button menu -->
<mat-menu #screenshareMenu="matMenu" id="screenshare-menu">
<button mat-menu-item (click)="onScreenTrackReplace()" id="replace-screen-button">
<mat-icon>picture_in_picture</mat-icon>
<span>{{ 'STREAM.REPLACE_SCREEN' | translate }}</span>
</button>
<mat-divider class="divider"></mat-divider>
<button mat-menu-item (click)="onScreenShareToggle()" id="disable-screen-button">
<mat-icon>stop_screen_share</mat-icon>
<span>{{ 'TOOLBAR.DISABLE_SCREEN' | translate }}</span>
</button>
</mat-menu>
<!-- Screenshare button menu -->
<mat-menu #screenshareMenu="matMenu" id="screenshare-menu">
<button mat-menu-item (click)="onScreenTrackReplace()" id="replace-screen-button">
<mat-icon>picture_in_picture</mat-icon>
<span>{{ 'STREAM.REPLACE_SCREEN' | translate }}</span>
</button>
<mat-divider class="divider"></mat-divider>
<button mat-menu-item (click)="onScreenShareToggle()" id="disable-screen-button">
<mat-icon>stop_screen_share</mat-icon>
<span>{{ 'TOOLBAR.DISABLE_SCREEN' | translate }}</span>
</button>
</mat-menu>
}
}
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
@if (showAdditionalButtonsOutside() && additionalButtonsPosition === 'beforeMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }"></ng-container>
@if (showAdditionalButtonsOutside() && additionalButtonsPosition() === 'beforeMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate(); context: { $implicit: additionalButtonsPosition() }"></ng-container>
}
<!-- More options button -->
@ -74,7 +88,7 @@
mat-icon-button
id="more-options-btn"
[matMenuTriggerFor]="settingsMenu"
[disabled]="isConnectionLost"
[disabled]="isConnectionLost()"
[class.mobile-btn]="isMobileView()"
matTooltip="{{ 'TOOLBAR.MORE_OPTIONS' | translate }}"
>
@ -82,38 +96,38 @@
</button>
<mat-menu #settingsMenu="matMenu" id="more-options-menu">
<!-- Additional buttons injection inside menu (mobile only) -->
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'beforeMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }">
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition() === 'beforeMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate(); context: { $implicit: additionalButtonsPosition() }">
</ng-container>
}
<!-- Fullscreen button -->
@if (showFullscreenButton) {
@if (showFullscreenButton()) {
<button mat-menu-item id="fullscreen-btn" (click)="onFullscreenToggle()">
<mat-icon>{{ isFullscreenActive ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
<span>{{ isFullscreenActive ? ('TOOLBAR.EXIT_FULLSCREEN' | translate) : ('TOOLBAR.FULLSCREEN' | translate) }}</span>
<mat-icon>{{ isFullscreenActive() ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
<span>{{ isFullscreenActive() ? ('TOOLBAR.EXIT_FULLSCREEN' | translate) : ('TOOLBAR.FULLSCREEN' | translate) }}</span>
</button>
}
<!-- Recording button -->
@if (showRecordingButton) {
@if (showRecordingButton()) {
<button
mat-menu-item
id="recording-btn"
[disabled]="
recordingStatus === _recordingStatus.STARTING ||
recordingStatus === _recordingStatus.STOPPING ||
!hasRoomTracksPublished
recordingStatus() === _recordingStatus.STARTING ||
recordingStatus() === _recordingStatus.STOPPING ||
!hasRoomTracksPublished()
"
[matTooltip]="!hasRoomTracksPublished ? ('TOOLBAR.NO_TRACKS_PUBLISHED' | translate) : ''"
[matTooltip]="!hasRoomTracksPublished() ? ('TOOLBAR.NO_TRACKS_PUBLISHED' | translate) : ''"
(click)="onRecordingToggle()"
>
<mat-icon color="warn">radio_button_checked</mat-icon>
<span>
{{
recordingStatus === _recordingStatus.STOPPED ||
recordingStatus === _recordingStatus.STOPPING ||
recordingStatus === _recordingStatus.FAILED
recordingStatus() === _recordingStatus.STOPPED ||
recordingStatus() === _recordingStatus.STOPPING ||
recordingStatus() === _recordingStatus.FAILED
? ('TOOLBAR.START_RECORDING' | translate)
: ('TOOLBAR.STOP_RECORDING' | translate)
}}
@ -122,7 +136,7 @@
}
<!-- View recordings button -->
@if (showViewRecordingsButton) {
@if (showViewRecordingsButton()) {
<button mat-menu-item id="view-recordings-btn" (click)="onViewRecordingsClick()">
<mat-icon>video_library</mat-icon>
<span>{{ 'TOOLBAR.VIEW_RECORDINGS' | translate }}</span>
@ -130,19 +144,19 @@
}
<!-- Broadcasting button -->
@if (showBroadcastingButton) {
@if (showBroadcastingButton()) {
<button
mat-menu-item
id="toolbar-broadcasting-btn"
[disabled]="broadcastingStatus === _broadcastingStatus.STARTING || recordingStatus === _broadcastingStatus.STOPPING"
[disabled]="broadcastingStatus() === _broadcastingStatus.STARTING || recordingStatus() === _broadcastingStatus.STOPPING"
(click)="onBroadcastingToggle()"
>
<mat-icon>sensors</mat-icon>
<span>
{{
broadcastingStatus === _broadcastingStatus.STOPPED ||
broadcastingStatus === _broadcastingStatus.STOPPING ||
broadcastingStatus === _broadcastingStatus.FAILED
broadcastingStatus() === _broadcastingStatus.STOPPED ||
broadcastingStatus() === _broadcastingStatus.STOPPING ||
broadcastingStatus() === _broadcastingStatus.FAILED
? ('PANEL.STREAMING.START' | translate)
: ('PANEL.STREAMING.STOP' | translate)
}}
@ -151,8 +165,8 @@
}
<!-- Virtual background button -->
@if (showBackgroundEffectsButton) {
<button mat-menu-item id="virtual-bg-btn" [disabled]="!isCameraEnabled" (click)="onBackgroundEffectsToggle()">
@if (showBackgroundEffectsButton()) {
<button mat-menu-item id="virtual-bg-btn" [disabled]="!isCameraEnabled()" (click)="onBackgroundEffectsToggle()">
<mat-icon><span class="material-symbols-outlined">background_replace</span></mat-icon>
<span>{{ 'TOOLBAR.BACKGROUND' | translate }}</span>
</button>
@ -172,8 +186,8 @@
</button> -->
<!-- Additional buttons injection inside menu (mobile only) -->
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'afterMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }">
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition() === 'afterMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate(); context: { $implicit: additionalButtonsPosition() }">
</ng-container>
}
@ -183,12 +197,12 @@
}
<!-- Divider before settings -->
@if (showSettingsButton) {
@if (showSettingsButton()) {
<mat-divider class="divider"></mat-divider>
}
<!-- Settings button -->
@if (showSettingsButton) {
@if (showSettingsButton()) {
<button mat-menu-item id="toolbar-settings-btn" (click)="onSettingsToggle()">
<mat-icon>settings</mat-icon>
<span>{{ 'TOOLBAR.SETTINGS' | translate }}</span>
@ -198,12 +212,12 @@
}
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
@if (showAdditionalButtonsOutside() && additionalButtonsPosition === 'afterMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate"></ng-container>
@if (showAdditionalButtonsOutside() && additionalButtonsPosition() === 'afterMenu') {
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate()"></ng-container>
}
<!-- Leave session button -->
@if (showLeaveButton) {
@if (showLeaveButton()) {
<button
mat-icon-button
(click)="onLeaveClick()"
@ -215,6 +229,6 @@
</button>
}
@if (toolbarLeaveButtonTemplate) {
<ng-container *ngTemplateOutlet="toolbarLeaveButtonTemplate"></ng-container>
@if (toolbarLeaveButtonTemplate()) {
<ng-container *ngTemplateOutlet="toolbarLeaveButtonTemplate()"></ng-container>
}

View File

@ -1,4 +1,4 @@
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef, computed, inject } from '@angular/core';
import { Component, ContentChild, EventEmitter, Output, TemplateRef, computed, inject, input } from '@angular/core';
import { RecordingStatus } from '../../../models/recording.model';
import { BroadcastingStatus } from '../../../models/broadcasting.model';
import { ToolbarAdditionalButtonsPosition } from '../../../models/toolbar.model';
@ -16,61 +16,62 @@ import { ToolbarMoreOptionsAdditionalMenuItemsDirective } from '../../../directi
})
export class ToolbarMediaButtonsComponent {
// Camera related inputs
@Input() showCameraButton: boolean = true;
@Input() isCameraEnabled: boolean = true;
@Input() cameraMuteChanging: boolean = false;
showCameraButton = input<boolean>(true);
isCameraEnabled = input<boolean>(true);
cameraMuteChanging = input<boolean>(false);
// Microphone related inputs
@Input() showMicrophoneButton: boolean = true;
@Input() isMicrophoneEnabled: boolean = true;
@Input() microphoneMuteChanging: boolean = false;
showMicrophoneButton = input<boolean>(true);
isMicrophoneEnabled = input<boolean>(true);
microphoneMuteChanging = input<boolean>(false);
// Screenshare related inputs
@Input() showScreenshareButton: boolean = true;
@Input() isScreenShareEnabled: boolean = false;
showScreenshareButton = input<boolean>(true);
isScreenShareEnabled = input<boolean>(false);
isFirefoxBrowser = input<boolean>(false);
// Device availability inputs
@Input() hasVideoDevices: boolean = true;
@Input() hasAudioDevices: boolean = true;
hasVideoDevices = input<boolean>(true);
hasAudioDevices = input<boolean>(true);
// Connection state inputs
@Input() isConnectionLost: boolean = false;
isConnectionLost = input<boolean>(false);
// UI state inputs
@Input() isMinimal: boolean = false;
isMinimal = input<boolean>(false);
// More options menu inputs
@Input() showMoreOptionsButton: boolean = true;
@Input() showFullscreenButton: boolean = true;
@Input() showRecordingButton: boolean = true;
@Input() showViewRecordingsButton: boolean = false;
@Input() showBroadcastingButton: boolean = true;
@Input() showBackgroundEffectsButton: boolean = true;
@Input() showCaptionsButton: boolean = true;
@Input() showSettingsButton: boolean = true;
showMoreOptionsButton = input<boolean>(true);
showFullscreenButton = input<boolean>(true);
showRecordingButton = input<boolean>(true);
showViewRecordingsButton = input<boolean>(false);
showBroadcastingButton = input<boolean>(true);
showBackgroundEffectsButton = input<boolean>(true);
showCaptionsButton = input<boolean>(true);
showSettingsButton = input<boolean>(true);
// Fullscreen state
@Input() isFullscreenActive: boolean = false;
isFullscreenActive = input<boolean>(false);
// Recording related inputs
@Input() recordingStatus: RecordingStatus = RecordingStatus.STOPPED;
@Input() hasRoomTracksPublished: boolean = false;
recordingStatus = input<RecordingStatus>(RecordingStatus.STOPPED);
hasRoomTracksPublished = input<boolean>(false);
// Broadcasting related inputs
@Input() broadcastingStatus: BroadcastingStatus = BroadcastingStatus.STOPPED;
broadcastingStatus = input<BroadcastingStatus>(BroadcastingStatus.STOPPED);
// Captions
@Input() captionsEnabled: boolean = false;
captionsEnabled = input<boolean>(false);
// Leave button
@Input() showLeaveButton: boolean = true;
showLeaveButton = input<boolean>(true);
// Additional buttons template
@Input() toolbarAdditionalButtonsTemplate: TemplateRef<any> | null = null;
@Input() additionalButtonsPosition: ToolbarAdditionalButtonsPosition | undefined;
toolbarAdditionalButtonsTemplate = input<TemplateRef<any> | null>(null);
additionalButtonsPosition = input<ToolbarAdditionalButtonsPosition | undefined>(undefined);
// Leave button template
@Input() toolbarLeaveButtonTemplate: TemplateRef<any> | null = null;
toolbarLeaveButtonTemplate = input<TemplateRef<any> | null>(null);
/**
* @internal
@ -100,34 +101,34 @@ export class ToolbarMediaButtonsComponent {
readonly isDesktopView = computed(() => this.viewportService.isDesktop());
// Essential buttons that always stay visible
readonly showCameraButtonDirect = computed(() => this.showCameraButton && !this.isMinimal);
readonly showCameraButtonDirect = computed(() => this.showCameraButton() && !this.isMinimal());
readonly showMicrophoneButtonDirect = computed(() => this.showMicrophoneButton && !this.isMinimal);
readonly showMicrophoneButtonDirect = computed(() => this.showMicrophoneButton() && !this.isMinimal());
// Screenshare button - visible on tablet+ or when already active
readonly showScreenshareButtonDirect = computed(
() => this.showScreenshareButton && !this.isMinimal && (!this.isMobileView() || this.isScreenShareEnabled)
() => this.showScreenshareButton() && !this.isMinimal() && (!this.isMobileView() || this.isScreenShareEnabled())
);
// More options button - always visible when not minimal
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton && !this.isMinimal);
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton() && !this.isMinimal());
// Check if there are active features that should show a badge on More Options
readonly hasActiveFeatures = computed(
() =>
this.isScreenShareEnabled ||
this.recordingStatus === this._recordingStatus.STARTED ||
this.broadcastingStatus === this._broadcastingStatus.STARTED
this.isScreenShareEnabled() ||
this.recordingStatus() === this._recordingStatus.STARTED ||
this.broadcastingStatus() === this._broadcastingStatus.STARTED
);
// Check if additional buttons should be shown outside (desktop/tablet) or inside More Options (mobile)
readonly showAdditionalButtonsOutside = computed(() => {
return !this.isMobileView() && this.toolbarAdditionalButtonsTemplate;
return !this.isMobileView() && this.toolbarAdditionalButtonsTemplate();
});
// Check if additional buttons should be shown inside More Options menu (mobile only)
readonly showAdditionalButtonsInsideMenu = computed(() => {
return this.isMobileView() && this.toolbarAdditionalButtonsTemplate;
return this.isMobileView() && this.toolbarAdditionalButtonsTemplate();
});
// Media button outputs

View File

@ -49,6 +49,7 @@
[microphoneMuteChanging]="microphoneMuteChanging"
[showScreenshareButton]="showScreenshareButton"
[isScreenShareEnabled]="isScreenShareEnabled"
[isFirefoxBrowser]="isFirefoxBrowser"
[hasVideoDevices]="hasVideoDevices"
[hasAudioDevices]="hasAudioDevices"
[isConnectionLost]="isConnectionLost"

View File

@ -344,6 +344,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
*/
showCaptionsButton: boolean = true;
/**
* @internal
*/
isFirefoxBrowser: boolean = false;
/**
* @ignore
*/
@ -450,6 +455,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
private templateManagerService: TemplateManagerService
) {
this.log = this.loggerSrv.get('ToolbarComponent');
this.isFirefoxBrowser = this.platformService.isFirefox();
// Effect to react to local participant changes
effect(() => {

View File

@ -436,14 +436,19 @@ export class ParticipantModel {
* @internal
*/
async switchScreenshare(newTrack: LocalTrack): Promise<void> {
if (this.participant instanceof LocalParticipant) {
const screenTrack = this.tracks.find((track) => track.source === Track.Source.ScreenShare);
if (screenTrack) {
await (screenTrack.videoTrack as LocalTrack).replaceTrack(newTrack.mediaStreamTrack);
return Promise.resolve();
}
if (!(this.participant instanceof LocalParticipant)) {
return Promise.reject("Remote participant can't switch screen share");
}
const screenTrack = this.tracks.find((track) => track.source === Track.Source.ScreenShare);
if (!screenTrack || !screenTrack.videoTrack) {
return Promise.reject('No active screen share track to switch');
}
const currentTrack = screenTrack.videoTrack as LocalTrack;
await currentTrack.replaceTrack(newTrack.mediaStreamTrack);
return Promise.resolve();
}
/**

View File

@ -55,6 +55,7 @@ export class ParticipantService {
private remoteParticipantsWritableSignal: WritableSignal<ParticipantModel[]> = signal<ParticipantModel[]>([]);
private localParticipant: ParticipantModel | undefined;
private lastLocalParticipantSnapshot: ParticipantModel | undefined;
private remoteParticipants: ParticipantModel[] = [];
private log: ILogger;
@ -85,6 +86,7 @@ export class ParticipantService {
*/
clear(): void {
this.localParticipant = undefined;
this.lastLocalParticipantSnapshot = undefined;
this.remoteParticipants = [];
this.localParticipantWritableSignal.set(undefined);
this.remoteParticipantsWritableSignal.set([]);
@ -189,19 +191,26 @@ export class ParticipantService {
* Switches the active screen share track showing a native browser dialog to select a screen or window.
*/
async switchScreenShare(): Promise<void> {
if (this.localParticipant) {
const options = this.getScreenCaptureOptions();
const [newTrack] = await this.localParticipant.createScreenTracks(options);
if (newTrack) {
newTrack?.addListener('ended', async () => {
this.log.d('Clicked native stop button. Stopping screen sharing');
await this.setScreenShareEnabled(false);
});
await this.localParticipant.switchScreenshare(newTrack);
}
} else {
if (!this.localParticipant) {
this.log.e('Local participant is undefined when switching screenshare');
return;
}
// Chrome / Safari: seamless replaceTrack keeps the same publication SID.
const options = this.getScreenCaptureOptions();
const [newTrack] = await this.localParticipant.createScreenTracks(options);
if (newTrack) {
newTrack.addListener('ended', async () => {
this.log.d('Clicked native stop button. Stopping screen sharing');
await this.setScreenShareEnabled(false);
});
try {
await this.localParticipant.switchScreenshare(newTrack);
} catch (error) {
newTrack.stop();
throw error;
}
}
// this.updateLocalParticipant();
@ -403,18 +412,55 @@ export class ParticipantService {
* Forces to update the local participant object and fire a new `localParticipant$` Observable event.
*/
updateLocalParticipant() {
const localParticipantFromSignal = this.localParticipantWritableSignal();
// Backward compatibility: if the consumer mutated the last emitted snapshot and
// then calls updateLocalParticipant(), keep the internal source of truth in sync.
// Only sync when the emitted snapshot actually diverged after emission; otherwise,
// a stale observable/signal snapshot would overwrite newer canonical state.
if (
this.localParticipant &&
localParticipantFromSignal &&
this.lastLocalParticipantSnapshot &&
localParticipantFromSignal !== this.localParticipant &&
localParticipantFromSignal.sid === this.localParticipant.sid &&
this.hasParticipantSnapshotMutations(localParticipantFromSignal, this.lastLocalParticipantSnapshot)
) {
Object.assign(this.localParticipant, localParticipantFromSignal);
}
// Update Signal - create new reference to trigger reactivity
// The Observable will automatically emit via toObservable()
if (this.localParticipant) {
const updatedParticipant = Object.assign(Object.create(Object.getPrototypeOf(this.localParticipant)), {
...this.localParticipant
});
const updatedParticipant = this.cloneParticipant(this.localParticipant);
this.localParticipantWritableSignal.set(updatedParticipant);
this.lastLocalParticipantSnapshot = this.cloneParticipant(updatedParticipant);
} else {
this.localParticipantWritableSignal.set(undefined);
this.lastLocalParticipantSnapshot = undefined;
}
}
private hasParticipantSnapshotMutations(current: ParticipantModel, previous: ParticipantModel): boolean {
const currentState = current as unknown as Record<string, unknown>;
const previousState = previous as unknown as Record<string, unknown>;
const keys = new Set([...Object.keys(currentState), ...Object.keys(previousState)]);
for (const key of keys) {
if (!Object.is(currentState[key], previousState[key])) {
return true;
}
}
return false;
}
private cloneParticipant<T extends ParticipantModel>(participant: T): T {
return Object.assign(Object.create(Object.getPrototypeOf(participant)), {
...participant
});
}
/**
* Sets the last screen element as pinned
* @internal
@ -459,7 +505,6 @@ export class ParticipantService {
}
}
/**
* Returns the participant with the given identity.
* @param identity

View File

@ -1,7 +1,7 @@
services:
caddy-proxy:
image: docker.io/openvidu/openvidu-caddy-local:3.6.0
image: docker.io/openvidu/openvidu-caddy-local:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -49,7 +49,7 @@ services:
condition: service_completed_successfully
minio:
image: docker.io/openvidu/minio:2025.10.15-debian-12-r9
image: docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0
restart: unless-stopped
ports:
- 9000:9000
@ -69,7 +69,7 @@ services:
condition: service_completed_successfully
mongo:
image: docker.io/openvidu/mongodb:8.0.19-r1
image: docker.io/openvidu/mongodb:8.0.19-r2
restart: unless-stopped
ports:
- 27017:27017
@ -88,7 +88,7 @@ services:
condition: service_completed_successfully
dashboard:
image: docker.io/openvidu/openvidu-dashboard:3.6.0
image: docker.io/openvidu/openvidu-dashboard:main
restart: unless-stopped
environment:
- SERVER_PORT=5000
@ -102,7 +102,7 @@ services:
condition: service_completed_successfully
openvidu:
image: docker.io/openvidu/openvidu-server:3.6.0
image: docker.io/openvidu/openvidu-server:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -123,7 +123,7 @@ services:
condition: service_completed_successfully
ingress:
image: docker.io/openvidu/ingress:3.6.0
image: docker.io/openvidu/ingress:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -141,7 +141,7 @@ services:
condition: service_completed_successfully
egress:
image: docker.io/openvidu/egress:3.6.0
image: docker.io/openvidu/egress:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -156,7 +156,7 @@ services:
condition: service_completed_successfully
operator:
image: docker.io/openvidu/openvidu-operator:3.6.0
image: docker.io/openvidu/openvidu-operator:main
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
@ -179,7 +179,7 @@ services:
condition: service_completed_successfully
openvidu-meet:
image: docker.io/openvidu/openvidu-meet:3.6.0
image: docker.io/openvidu/openvidu-meet:main
restart: on-failure
extra_hosts:
- host.docker.internal:host-gateway
@ -219,7 +219,7 @@ services:
condition: service_completed_successfully
openvidu-meet-init:
image: docker.io/openvidu/openvidu-operator:3.6.0
image: docker.io/openvidu/openvidu-operator:main
restart: on-failure
environment:
- MODE=local-ready-check

View File

@ -680,7 +680,7 @@ Resources:
'/usr/local/bin/install.sh':
content: !Sub |
#!/bin/bash -x
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4

View File

@ -163,7 +163,7 @@ var stringInterpolationParams = {
var installScriptTemplate = '''
#!/bin/bash -x
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout

File diff suppressed because one or more lines are too long

View File

@ -148,7 +148,7 @@ locals {
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -1441,7 +1441,7 @@ Resources:
content: !Sub |
#!/bin/bash
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4
@ -2496,6 +2496,15 @@ Resources:
ToPort: 9080
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMediaNodeToMasterCustomAppIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt OpenViduMasterNodeSG.GroupId
IpProtocol: tcp
FromPort: 6080
ToPort: 6080
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMediaNodeSG:
Type: AWS::EC2::SecurityGroup
Properties:

View File

@ -211,7 +211,7 @@ var stringInterpolationParamsMaster = {
var installScriptTemplateMaster = '''
#!/bin/bash -x
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
# Assume azure cli is installed
@ -1708,6 +1708,29 @@ resource mediaToMasterMeetWebhookIngress 'Microsoft.Network/networkSecurityGroup
}
}
resource mediaToMasterCustomAppWebhookIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMasterNodeNSG
name: 'mediaNode_to_masterNode_CUSTOM_APP_WEBHOOK_INGRESS'
properties: {
protocol: 'Tcp'
sourceApplicationSecurityGroups: [
{
id: openviduMediaNodeASG.id
}
]
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMasterNodeASG.id
}
]
destinationPortRange: '6080'
access: 'Allow'
priority: 220
direction: 'Inbound'
}
}
resource openviduMediaNodeNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {
name: '${stackName}-mediaNoderNSG'
location: location

File diff suppressed because one or more lines are too long

View File

@ -87,7 +87,7 @@ resource "google_compute_firewall" "firewall_media_to_master" {
allow {
protocol = "tcp"
ports = ["7000", "9100", "20000", "3100", "9009", "4443", "9080"]
ports = ["7000", "9100", "20000", "3100", "9009", "4443", "9080", "6080"]
}
source_tags = [
@ -155,19 +155,19 @@ resource "google_compute_instance" "openvidu_master_node" {
metadata = {
# metadata values are accessible from the instance
publicIpAddress = var.publicIpAddress == "" ? google_compute_address.public_ip_address[0].address : var.publicIpAddress
region = var.region
stackName = var.stackName
certificateType = var.certificateType
domainName = var.domainName
ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate
openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword
initialMeetApiKey = var.initialMeetApiKey
additionalInstallFlags = var.additionalInstallFlags
bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName
publicIpAddress = var.publicIpAddress == "" ? google_compute_address.public_ip_address[0].address : var.publicIpAddress
region = var.region
stackName = var.stackName
certificateType = var.certificateType
domainName = var.domainName
ownPublicCertificate = var.ownPublicCertificate
ownPrivateCertificate = var.ownPrivateCertificate
openviduLicense = var.openviduLicense
rtcEngine = var.rtcEngine
initialMeetAdminPassword = var.initialMeetAdminPassword
initialMeetApiKey = var.initialMeetApiKey
additionalInstallFlags = var.additionalInstallFlags
bucketName = local.isEmpty ? google_storage_bucket.bucket[0].name : var.bucketName
}
service_account {
@ -617,7 +617,7 @@ locals {
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -1544,7 +1544,7 @@ Resources:
content: !Sub |
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4
@ -2918,6 +2918,15 @@ Resources:
ToPort: 9080
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterCustomAppIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 6080
ToPort: 6080
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterMeetWebhookIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
@ -2927,6 +2936,15 @@ Resources:
ToPort: 9080
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMediaNodeToMasterCustomAppIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt OpenViduMasterNodeSG.GroupId
IpProtocol: tcp
FromPort: 6080
ToPort: 6080
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMediaNodeSG:
Type: AWS::EC2::SecurityGroup
Properties:
@ -3094,7 +3112,7 @@ Resources:
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMasterNodeWithTurnTLSTG
TargetGroupArn: !Ref OpenViduMasterNodeHTTPTG
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: TCP

View File

@ -287,7 +287,7 @@ var stringInterpolationParamsMaster4 = {
var installScriptTemplateMaster = '''
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
# Assume azure cli is installed
@ -2591,6 +2591,29 @@ resource masterToMasterMeet 'Microsoft.Network/networkSecurityGroups/securityRul
}
}
resource masterToMasterCustomApp 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMasterNodeNSG
name: 'masterNode_to_masterNode_CUSTOM_APP_INGRESS'
properties: {
protocol: 'Tcp'
sourceApplicationSecurityGroups: [
{
id: openviduMasterNodeASG.id
}
]
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMasterNodeASG.id
}
]
destinationPortRange: '6080'
access: 'Allow'
priority: 310
direction: 'Inbound'
}
}
resource mediaToMasterMeetWebhookIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMasterNodeNSG
name: 'mediaNode_to_masterNode_MEET_WEBHOOK_INGRESS'
@ -2614,6 +2637,29 @@ resource mediaToMasterMeetWebhookIngress 'Microsoft.Network/networkSecurityGroup
}
}
resource mediaToMasterCustomAppWebhookIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
parent: openviduMasterNodeNSG
name: 'mediaNode_to_masterNode_CUSTOM_APP_WEBHOOK_INGRESS'
properties: {
protocol: 'Tcp'
sourceApplicationSecurityGroups: [
{
id: openviduMediaNodeASG.id
}
]
sourcePortRange: '*'
destinationApplicationSecurityGroups: [
{
id: openviduMasterNodeASG.id
}
]
destinationPortRange: '6080'
access: 'Allow'
priority: 320
direction: 'Inbound'
}
}
resource openviduMediaNodeNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {
name: '${stackName}-mediaNodeNSG'
location: location

File diff suppressed because one or more lines are too long

View File

@ -122,7 +122,8 @@ resource "google_compute_firewall" "master_to_master_internal" {
"5000",
"3000",
"4443",
"9080"
"9080",
"6080"
]
}
@ -145,7 +146,8 @@ resource "google_compute_firewall" "media_to_master_services" {
"9009",
"3100",
"4443",
"9080"
"9080",
"6080"
]
}
@ -940,7 +942,7 @@ locals {
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -1,6 +1,6 @@
services:
caddy-proxy:
image: docker.io/openvidu/openvidu-caddy-local:3.6.0
image: docker.io/openvidu/openvidu-caddy-local:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -49,7 +49,7 @@ services:
condition: service_completed_successfully
minio:
image: docker.io/openvidu/minio:2025.10.15-debian-12-r9
image: docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0
restart: unless-stopped
ports:
- 9000:9000
@ -69,7 +69,7 @@ services:
condition: service_completed_successfully
mongo:
image: docker.io/openvidu/mongodb:8.0.19-r1
image: docker.io/openvidu/mongodb:8.0.19-r2
restart: unless-stopped
ports:
- 27017:27017
@ -88,7 +88,7 @@ services:
condition: service_completed_successfully
dashboard:
image: docker.io/openvidu/openvidu-dashboard:3.6.0
image: docker.io/openvidu/openvidu-dashboard:main
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
@ -102,7 +102,7 @@ services:
condition: service_completed_successfully
openvidu:
image: docker.io/openvidu/openvidu-server-pro:3.6.0
image: docker.io/openvidu/openvidu-server-pro:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -125,7 +125,7 @@ services:
condition: service_completed_successfully
ingress:
image: docker.io/openvidu/ingress:3.6.0
image: docker.io/openvidu/ingress:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -143,7 +143,7 @@ services:
condition: service_completed_successfully
egress:
image: docker.io/openvidu/egress:3.6.0
image: docker.io/openvidu/egress:main
restart: unless-stopped
extra_hosts:
- host.docker.internal:host-gateway
@ -157,7 +157,7 @@ services:
setup:
condition: service_completed_successfully
operator:
image: docker.io/openvidu/openvidu-operator:3.6.0
image: docker.io/openvidu/openvidu-operator:main
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
@ -181,7 +181,7 @@ services:
openvidu-meet:
image: docker.io/openvidu/openvidu-meet:3.6.0
image: docker.io/openvidu/openvidu-meet:main
restart: on-failure
extra_hosts:
- host.docker.internal:host-gateway
@ -221,7 +221,7 @@ services:
condition: service_completed_successfully
openvidu-v2compatibility:
image: docker.io/openvidu/openvidu-v2compatibility:3.6.0
image: docker.io/openvidu/openvidu-v2compatibility:main
restart: unless-stopped
entrypoint: /bin/sh /scripts/entrypoint_openvidu_v2_compat.sh
extra_hosts:
@ -258,7 +258,7 @@ services:
condition: service_completed_successfully
openvidu-meet-init:
image: docker.io/openvidu/openvidu-operator:3.6.0
image: docker.io/openvidu/openvidu-operator:main
restart: on-failure
environment:
- MODE=local-ready-check

View File

@ -782,7 +782,7 @@ Resources:
'/usr/local/bin/install.sh':
content: !Sub |
#!/bin/bash -x
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4

View File

@ -174,7 +174,7 @@ var stringInterpolationParams = {
var installScriptTemplate = '''
#!/bin/bash -x
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout

File diff suppressed because one or more lines are too long

View File

@ -148,7 +148,7 @@ locals {
#!/bin/bash -x
set -e
OPENVIDU_VERSION=3.6.0
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.52.4
echo "DPkg::Lock::Timeout \"-1\";" > /etc/apt/apt.conf.d/99timeout

View File

@ -3,10 +3,10 @@
set -eu
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export INSTALLER_IMAGE="${INSTALLER_IMAGE:-docker.io/openvidu/openvidu-installer:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
# Function to compare two version strings

View File

@ -3,10 +3,10 @@ set -eu
export INSTALL_PREFIX="${INSTALL_PREFIX:-/opt/openvidu}"
export DOCKER_VERSION="${DOCKER_VERSION:-29.2.1}"
export DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-v5.0.2}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-3.6.0}"
export OPENVIDU_VERSION="${OPENVIDU_VERSION:-main}"
export UPDATER_IMAGE="${UPDATER_IMAGE:-docker.io/openvidu/openvidu-updater:${OPENVIDU_VERSION}}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:2025.10.15-debian-12-r9}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:2025.8.13-debian-12-r12}"
export MINIO_SERVER_IMAGE="${MINIO_SERVER_IMAGE:-docker.io/openvidu/minio:RELEASE.2026-03-04T16-04-53Z-r0}"
export MINIO_CLIENT_IMAGE="${MINIO_CLIENT_IMAGE:-docker.io/openvidu/minio-client:RELEASE.2026-03-12T12-23-10Z}"
export MONGO_SERVER_IMAGE="${MONGO_SERVER_IMAGE:-docker.io/mongo:8.0.19}"
export REDIS_SERVER_IMAGE="${REDIS_SERVER_IMAGE:-docker.io/redis:8.6.1-alpine}"
export BUSYBOX_IMAGE="${BUSYBOX_IMAGE:-docker.io/busybox:1.37.0}"
@ -26,7 +26,7 @@ export LIVEKIT_EGRESS_SERVER_IMAGE="${LIVEKIT_EGRESS_SERVER_IMAGE:-docker.io/ope
export PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-docker.io/prom/prometheus:v3.9.1}"
export PROMTAIL_IMAGE="${PROMTAIL_IMAGE:-docker.io/grafana/promtail:3.5.11}"
export LOKI_IMAGE="${LOKI_IMAGE:-docker.io/grafana/loki:3.5.11}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.3}"
export MIMIR_IMAGE="${MIMIR_IMAGE:-docker.io/openvidu/grafana-mimir:3.0.4-r0}"
export GRAFANA_IMAGE="${GRAFANA_IMAGE:-docker.io/grafana/grafana:12.3.4}"
get_next_version() {

View File

@ -24,7 +24,6 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@ -60,16 +59,20 @@ public class OpenViduTestE2e {
// Value is a pair with:
// 1. The flag value of the ffmpeg command
// 2. Any extra flags needed for that codec to work
final protected static Map<String, Pair<String, ?>> FFMPEG_VIDEO_CODEC_NAMES = new HashMap<>() {
// Key is the common name of the video codec. Value is a triple with:
// 1. The flag value of the ffmpeg command
// 2. Any extra flags needed for that codec to work
// 3. The string expected to appear on the server log when the stream starts
final protected static Map<String, Triple<String, String, String>> FFMPEG_VIDEO_CODEC_NAMES = new HashMap<>() {
{
put("H264", Pair.of("libx264", ""));
put("VP8", Pair.of("libvpx", ""));
put("VP9", Pair.of("libvpx-vp9", ""));
put("MPEG-4", Pair.of("mpeg4", ""));
put("M-JPEG", Pair.of("mjpeg", "-force_duplicated_matrix:v 1 -huffman:v 0"));
// put("AV1", Pair.of("libaom-av1", "")); // NOT SUPPORTED BY THE RTSP SERVER
put("H264", Triple.of("libx264", "", "H264"));
put("VP8", Triple.of("libvpx", "", "VP8"));
put("VP9", Triple.of("libvpx-vp9", "", "VP9"));
put("MPEG-4", Triple.of("mpeg4", "", "MPEG-4"));
put("M-JPEG", Triple.of("mjpeg", "-force_duplicated_matrix:v 1 -huffman:v 0", "MJPEG"));
// put("AV1", Triple.of("libaom-av1", "", "AV1")); // NOT SUPPORTED BY THE RTSP SERVER
// (maybe gstreamer?)
// put("H265", Pair.of("libx265", "")); // NOT SUPPORTED BY INGRESS
// put("H265", Triple.of("libx265", "", "H265")); // NOT SUPPORTED BY INGRESS
}
};
@ -84,7 +87,7 @@ public class OpenViduTestE2e {
put("AAC", Triple.of("aac", "-ac 2 -b:a 128k", "MPEG-4 Audio"));
put("AC3", Triple.of("ac3", "-b:a 128k", null));
put("OPUS", Triple.of("libopus", "-ac 2", "Opus"));
put("MP3", Triple.of("libmp3lame", "", "MPEG-1/2 Audio"));
put("MP3", Triple.of("libmp3lame", "", "MPEG-1 Audio"));
put("VORBIS", Triple.of("libvorbis", "", null));
put("G711", Triple.of("pcm_mulaw", "", "G711"));
}
@ -222,7 +225,8 @@ public class OpenViduTestE2e {
// e.g. "[path live] stream is available and online, 2 tracks (H264, Opus)"
if (videoCodec != null) {
String regex = ".*\\[path " + RTSP_PATH + "\\] stream is available.*\\(.*(?i)(" + videoCodec + ").*\\).*";
String expectedVideoCodecLogValue = FFMPEG_VIDEO_CODEC_NAMES.get(videoCodec).getRight();
String regex = ".*\\[path " + RTSP_PATH + "\\] stream is available.*\\(.*(?i)(" + expectedVideoCodecLogValue + ").*\\).*";
waitUntilLog(rtspServerContainer, regex, 15);
}
if (audioCodec != null) {
@ -289,7 +293,7 @@ public class OpenViduTestE2e {
if (videoCodec != null) {
String ffmpegVideoCodecFlag = FFMPEG_VIDEO_CODEC_NAMES.get(videoCodec).getLeft();
codecs += " -vcodec " + ffmpegVideoCodecFlag + " ";
codecs += FFMPEG_VIDEO_CODEC_NAMES.get(videoCodec).getRight() + " ";
codecs += FFMPEG_VIDEO_CODEC_NAMES.get(videoCodec).getMiddle() + " ";
}
if (audioCodec != null) {
String ffmpegAudioCodecFlag = FFMPEG_AUDIO_CODEC_NAMES.get(audioCodec).getLeft();