mirror of https://github.com/OpenVidu/openvidu.git
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.master
parent
87b3ac3716
commit
34e34ee078
File diff suppressed because it is too large
Load Diff
|
|
@ -36,31 +36,45 @@
|
||||||
|
|
||||||
<!-- Screenshare button - Only visible on tablet+ or when active -->
|
<!-- Screenshare button - Only visible on tablet+ or when active -->
|
||||||
@if (showScreenshareButtonDirect()) {
|
@if (showScreenshareButtonDirect()) {
|
||||||
<button
|
@if (isFirefoxBrowser) {
|
||||||
mat-icon-button
|
<button
|
||||||
id="screenshare-btn"
|
mat-icon-button
|
||||||
[matMenuTriggerFor]="isScreenShareEnabled ? screenshareMenu : null"
|
id="screenshare-btn"
|
||||||
[disabled]="isConnectionLost"
|
[disabled]="isConnectionLost"
|
||||||
[class.active-btn]="isScreenShareEnabled"
|
[class.active-btn]="isScreenShareEnabled"
|
||||||
[class.mobile-btn]="isMobileView()"
|
[class.mobile-btn]="isMobileView()"
|
||||||
matTooltip="{{ isScreenShareEnabled ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
|
matTooltip="{{ isScreenShareEnabled ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
|
||||||
(click)="!isScreenShareEnabled && onScreenShareToggle()"
|
(click)="onScreenShareToggle()"
|
||||||
>
|
>
|
||||||
<mat-icon>screen_share</mat-icon>
|
<mat-icon>screen_share</mat-icon>
|
||||||
</button>
|
</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 -->
|
<!-- Screenshare button menu -->
|
||||||
<mat-menu #screenshareMenu="matMenu" id="screenshare-menu">
|
<mat-menu #screenshareMenu="matMenu" id="screenshare-menu">
|
||||||
<button mat-menu-item (click)="onScreenTrackReplace()" id="replace-screen-button">
|
<button mat-menu-item (click)="onScreenTrackReplace()" id="replace-screen-button">
|
||||||
<mat-icon>picture_in_picture</mat-icon>
|
<mat-icon>picture_in_picture</mat-icon>
|
||||||
<span>{{ 'STREAM.REPLACE_SCREEN' | translate }}</span>
|
<span>{{ 'STREAM.REPLACE_SCREEN' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<mat-divider class="divider"></mat-divider>
|
<mat-divider class="divider"></mat-divider>
|
||||||
<button mat-menu-item (click)="onScreenShareToggle()" id="disable-screen-button">
|
<button mat-menu-item (click)="onScreenShareToggle()" id="disable-screen-button">
|
||||||
<mat-icon>stop_screen_share</mat-icon>
|
<mat-icon>stop_screen_share</mat-icon>
|
||||||
<span>{{ 'TOOLBAR.DISABLE_SCREEN' | translate }}</span>
|
<span>{{ 'TOOLBAR.DISABLE_SCREEN' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
|
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export class ToolbarMediaButtonsComponent {
|
||||||
// Screenshare related inputs
|
// Screenshare related inputs
|
||||||
@Input() showScreenshareButton: boolean = true;
|
@Input() showScreenshareButton: boolean = true;
|
||||||
@Input() isScreenShareEnabled: boolean = false;
|
@Input() isScreenShareEnabled: boolean = false;
|
||||||
|
@Input() isFirefoxBrowser: boolean = false;
|
||||||
|
|
||||||
// Device availability inputs
|
// Device availability inputs
|
||||||
@Input() hasVideoDevices: boolean = true;
|
@Input() hasVideoDevices: boolean = true;
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
[microphoneMuteChanging]="microphoneMuteChanging"
|
[microphoneMuteChanging]="microphoneMuteChanging"
|
||||||
[showScreenshareButton]="showScreenshareButton"
|
[showScreenshareButton]="showScreenshareButton"
|
||||||
[isScreenShareEnabled]="isScreenShareEnabled"
|
[isScreenShareEnabled]="isScreenShareEnabled"
|
||||||
|
[isFirefoxBrowser]="isFirefoxBrowser"
|
||||||
[hasVideoDevices]="hasVideoDevices"
|
[hasVideoDevices]="hasVideoDevices"
|
||||||
[hasAudioDevices]="hasAudioDevices"
|
[hasAudioDevices]="hasAudioDevices"
|
||||||
[isConnectionLost]="isConnectionLost"
|
[isConnectionLost]="isConnectionLost"
|
||||||
|
|
|
||||||
|
|
@ -344,6 +344,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
*/
|
*/
|
||||||
showCaptionsButton: boolean = true;
|
showCaptionsButton: boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
isFirefoxBrowser: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
|
|
@ -450,6 +455,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private templateManagerService: TemplateManagerService
|
private templateManagerService: TemplateManagerService
|
||||||
) {
|
) {
|
||||||
this.log = this.loggerSrv.get('ToolbarComponent');
|
this.log = this.loggerSrv.get('ToolbarComponent');
|
||||||
|
this.isFirefoxBrowser = this.platformService.isFirefox();
|
||||||
|
|
||||||
// Effect to react to local participant changes
|
// Effect to react to local participant changes
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
|
|
||||||
|
|
@ -436,14 +436,19 @@ export class ParticipantModel {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async switchScreenshare(newTrack: LocalTrack): Promise<void> {
|
async switchScreenshare(newTrack: LocalTrack): Promise<void> {
|
||||||
if (this.participant instanceof LocalParticipant) {
|
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();
|
|
||||||
}
|
|
||||||
return Promise.reject("Remote participant can't switch screen share");
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -189,19 +189,26 @@ export class ParticipantService {
|
||||||
* Switches the active screen share track showing a native browser dialog to select a screen or window.
|
* Switches the active screen share track showing a native browser dialog to select a screen or window.
|
||||||
*/
|
*/
|
||||||
async switchScreenShare(): Promise<void> {
|
async switchScreenShare(): Promise<void> {
|
||||||
if (this.localParticipant) {
|
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 {
|
|
||||||
this.log.e('Local participant is undefined when switching screenshare');
|
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();
|
// this.updateLocalParticipant();
|
||||||
|
|
@ -459,7 +466,6 @@ export class ParticipantService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the participant with the given identity.
|
* Returns the participant with the given identity.
|
||||||
* @param identity
|
* @param identity
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue