openvidu-browser fix: streamDestroyed only triggered by Publisher after streamCreated

pull/73/head
pabloFuente 2018-05-31 13:08:34 +02:00
parent 37cdb9ccf0
commit 2f2a42c439
11 changed files with 325 additions and 186 deletions

View File

@ -795,9 +795,12 @@ export class Session implements EventDispatcher {
} }
if (!!this.connection.stream) { if (!!this.connection.stream) {
// Make Publisher object dispatch 'streamDestroyed' event (if there's a local stream) // Dispose Publisher's local stream
this.connection.stream.disposeWebRtcPeer(); this.connection.stream.disposeWebRtcPeer();
this.connection.stream.ee.emitEvent('local-stream-destroyed-by-disconnect', [reason]); if (this.connection.stream.isLocalStreamPublished) {
// Make Publisher object dispatch 'streamDestroyed' event if the Stream was published
this.connection.stream.ee.emitEvent('local-stream-destroyed-by-disconnect', [reason]);
}
} }
if (!this.connection.disposed) { if (!this.connection.disposed) {

View File

@ -14,14 +14,15 @@ import { VideoComponent } from './components/video/video.component';
import { OpenViduVideoComponent } from './components/video/ov-video.component'; import { OpenViduVideoComponent } from './components/video/ov-video.component';
import { ExtensionDialogComponent } from './components/dialogs/extension-dialog.component'; import { ExtensionDialogComponent } from './components/dialogs/extension-dialog.component';
import { LocalRecordingDialogComponent } from './components/dialogs/local-recording-dialog.component'; import { LocalRecordingDialogComponent } from './components/dialogs/local-recording-dialog.component';
import { SessionPropertiesDialogComponent } from './components/dialogs/session-properties-dialog.component';
import { SessionApiDialogComponent } from './components/dialogs/session-api-dialog.component';
import { EventsDialogComponent } from './components/dialogs/events-dialog.component';
import { PublisherPropertiesDialogComponent } from './components/dialogs/publisher-properties-dialog.component';
import { OpenviduRestService } from './services/openvidu-rest.service'; import { OpenviduRestService } from './services/openvidu-rest.service';
import { OpenviduParamsService } from './services/openvidu-params.service'; import { OpenviduParamsService } from './services/openvidu-params.service';
import { TestFeedService } from './services/test-feed.service'; import { TestFeedService } from './services/test-feed.service';
import { MuteSubscribersService } from './services/mute-subscribers.service'; import { MuteSubscribersService } from './services/mute-subscribers.service';
import { SessionPropertiesDialogComponent } from './components/dialogs/session-properties-dialog.component';
import { SessionApiDialogComponent } from './components/dialogs/session-api-dialog.component';
import { EventsDialogComponent } from './components/dialogs/events-dialog.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -35,7 +36,8 @@ import { EventsDialogComponent } from './components/dialogs/events-dialog.compon
SessionPropertiesDialogComponent, SessionPropertiesDialogComponent,
SessionApiDialogComponent, SessionApiDialogComponent,
EventsDialogComponent, EventsDialogComponent,
LocalRecordingDialogComponent LocalRecordingDialogComponent,
PublisherPropertiesDialogComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -56,7 +58,8 @@ import { EventsDialogComponent } from './components/dialogs/events-dialog.compon
SessionPropertiesDialogComponent, SessionPropertiesDialogComponent,
SessionApiDialogComponent, SessionApiDialogComponent,
EventsDialogComponent, EventsDialogComponent,
LocalRecordingDialogComponent LocalRecordingDialogComponent,
PublisherPropertiesDialogComponent
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -0,0 +1,31 @@
mat-chip {
margin: 0 0 5px 0 !important;
height: 12px;
max-width: 180px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
cursor: pointer !important;
}
mat-divider {
margin-top: 10px !important;
margin-bottom: 5px !important;
}
button.device-btn {
height: 30px;
width: 30px;
line-height: 30px;
}
button.device-btn mat-icon {
width: 20px;
height: 20px;
font-size: 20px;
line-height: 20px;
}
div.mat-form-field-subscript-wrapper {
margin-top: 0 !important;
}

View File

@ -0,0 +1,48 @@
<div>
<h2 mat-dialog-title>Publisher properties</h2>
<mat-dialog-content>
<mat-checkbox (click)="toggleAudio()" [checked]="publisherProperties.audioSource !== false">Audio
</mat-checkbox>
<mat-checkbox (click)="toggleVideo()" [checked]="publisherProperties.videoSource !== false">Video
</mat-checkbox>
<mat-checkbox [(ngModel)]="publisherProperties.publishAudio" (click)="publisherProperties.publishAudio = !publisherProperties.publishAudio">Publish audio</mat-checkbox>
<mat-checkbox [(ngModel)]="publisherProperties.publishVideo" (click)="publisherProperties.publishVideo = !publisherProperties.publishVideo">Publish video</mat-checkbox>
<mat-checkbox [(ngModel)]="publisherProperties.mirror" (click)="publisherProperties.mirror = !publisherProperties.mirror">Mirror</mat-checkbox>
<mat-divider></mat-divider>
<mat-form-field>
<input matInput placeholder="Audio source" [(ngModel)]="audioSource" [disabled]="(publisherProperties.audioSource === false)">
</mat-form-field>
<button mat-icon-button class="device-btn" title="List audio devices" (click)="listAudioDevices()">
<mat-icon aria-label="List audio devices">add_circle</mat-icon>
</button>
<button *ngIf="audioDevices.length > 0" mat-icon-button class="device-btn" title="Clear audio devices" (click)="audioDevices = []">
<mat-icon aria-label="Clear audio devices">clear</mat-icon>
</button>
<mat-chip-list *ngIf="audioDevices.length > 0">
<mat-chip *ngFor="let audioDevice of audioDevices" (click)="audioSource=audioDevice.deviceId">{{audioDevice.label}}</mat-chip>
</mat-chip-list>
<mat-form-field>
<input matInput placeholder="Video source" [(ngModel)]="videoSource" [disabled]="(publisherProperties.videoSource === false)">
</mat-form-field>
<button mat-icon-button class="device-btn" title="List video devices" (click)="listVideoDevices()">
<mat-icon aria-label="List video devices">add_circle</mat-icon>
</button>
<button *ngIf="videoDevices.length > 0" mat-icon-button class="device-btn" title="Clear video devices" (click)="videoDevices = []">
<mat-icon aria-label="Clear video devices">clear</mat-icon>
</button>
<mat-chip-list *ngIf="videoDevices.length > 0">
<mat-chip *ngFor="let videoDevice of videoDevices" (click)="videoSource=videoDevice.deviceId">{{videoDevice.label}}</mat-chip>
</mat-chip-list>
<mat-divider></mat-divider>
<mat-form-field>
<input matInput placeholder="Resolution" [(ngModel)]="publisherProperties.resolution">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Framerate" [(ngModel)]="publisherProperties.frameRate" type="number">
</mat-form-field>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [mat-dialog-close]="initValue">CANCEL</button>
<button mat-button [mat-dialog-close]="setCloseValue()">SAVE</button>
</mat-dialog-actions>
</div>

View File

@ -0,0 +1,105 @@
import { Component, Inject } from '@angular/core';
import { MatDialog, MAT_DIALOG_DATA, MatDialogConfig, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
import { SessionProperties, MediaMode, RecordingMode, RecordingLayout } from 'openvidu-node-client';
import { PublisherProperties, OpenVidu } from 'openvidu-browser';
@Component({
selector: 'app-publisher-properties-dialog',
templateUrl: './publisher-properties-dialog.component.html',
styleUrls: ['./publisher-properties-dialog.component.css'],
providers: [
{ provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' }
]
})
export class PublisherPropertiesDialogComponent {
OV: OpenVidu;
publisherProperties: PublisherProperties;
initValue: PublisherProperties;
audioSource;
videoSource;
audioSourceAux;
videoSourceAux;
audioDevices = [];
videoDevices = [];
private mediaMode = MediaMode;
private recordingMode = RecordingMode;
private defaultRecordingLayout = RecordingLayout;
constructor(public dialogRef: MatDialogRef<PublisherPropertiesDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: PublisherProperties) {
this.publisherProperties = data;
this.OV = new OpenVidu();
this.initValue = Object.assign({}, this.publisherProperties);
this.audioSource = typeof this.publisherProperties.audioSource === 'string' ? this.publisherProperties.audioSource : undefined;
this.videoSource = typeof this.publisherProperties.videoSource === 'string' ? this.publisherProperties.videoSource : undefined;
}
toggleAudio(): void {
if (this.publisherProperties.audioSource === false) {
this.publisherProperties.audioSource = this.audioSource;
this.audioSource = this.audioSourceAux;
} else {
this.audioSourceAux = this.audioSource;
this.audioSource = undefined;
this.publisherProperties.audioSource = false;
}
}
toggleVideo(): void {
if (this.publisherProperties.videoSource === false) {
this.publisherProperties.videoSource = this.videoSource;
this.videoSource = this.videoSourceAux;
} else {
this.videoSourceAux = this.videoSource;
this.videoSource = undefined;
this.publisherProperties.videoSource = false;
}
}
setCloseValue(): PublisherProperties {
if (typeof this.audioSource === 'string') {
if (!!this.audioSource) {
this.publisherProperties.audioSource = this.audioSource;
} else {
this.publisherProperties.audioSource = undefined;
}
}
if (typeof this.videoSource === 'string') {
if (!!this.videoSource) {
this.publisherProperties.videoSource = this.videoSource;
} else {
this.publisherProperties.videoSource = undefined;
}
}
return this.publisherProperties;
}
listAudioDevices() {
this.audioDevices = [];
this.OV.getDevices().then(devices => {
devices.forEach(device => {
if (device.kind === 'audioinput') {
this.audioDevices.push(device);
}
});
});
}
listVideoDevices() {
this.videoDevices = [];
this.OV.getDevices().then(devices => {
devices.forEach(device => {
if (device.kind === 'videoinput') {
this.videoDevices.push(device);
}
});
});
}
}

View File

@ -48,9 +48,9 @@ export class SessionPropertiesDialogComponent {
sessionProperties: SessionProperties; sessionProperties: SessionProperties;
private mediaMode = MediaMode; mediaMode = MediaMode;
private recordingMode = RecordingMode; recordingMode = RecordingMode;
private defaultRecordingLayout = RecordingLayout; defaultRecordingLayout = RecordingLayout;
constructor(public dialogRef: MatDialogRef<SessionPropertiesDialogComponent>, constructor(public dialogRef: MatDialogRef<SessionPropertiesDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: SessionProperties) { @Inject(MAT_DIALOG_DATA) public data: SessionProperties) {

View File

@ -33,10 +33,8 @@
<div> <div>
<button class="join-btn" mat-button (click)="joinSession()" [disabled]="session">JOIN</button> <button class="join-btn" mat-button (click)="joinSession()" [disabled]="session">JOIN</button>
<mat-checkbox class="subscribe-checkbox" name="subscribeTo" (change)="toggleSubscribeTo()" [checked]="subscribeTo && checkSubscribeTo" <mat-checkbox class="subscribe-checkbox" name="subscribeTo" (click)="toggleSubscribeTo()" [checked]="subscribeTo" [disabled]="session">Subscribe</mat-checkbox>
[disabled]="session">Subscribe</mat-checkbox> <mat-checkbox class="publish-checkbox" name="publishTo" (click)="togglePublishTo()" [checked]="publishTo" [disabled]="session">Publish</mat-checkbox>
<mat-checkbox class="publish-checkbox" name="publishTo" (change)="togglePublishTo()" [checked]="publishTo && checkPublishTo"
[disabled]="session || disablePublishTo">Publish</mat-checkbox>
</div> </div>
<div class="inner-card" fxLayout="row" fxLayoutAlign="start start"> <div class="inner-card" fxLayout="row" fxLayoutAlign="start start">
@ -45,45 +43,45 @@
<div> <div>
<h4>Send</h4> <h4>Send</h4>
<div> <div>
<mat-checkbox class="send-audio-checkbox" name="sendAudio" (change)="toggleSendAudio()" [checked]="sendAudio && checkSendAudio" <mat-checkbox class="send-audio-checkbox" name="sendAudio" (click)="toggleSendAudio()" [checked]="publisherProperties.audioSource !== false"
[disabled]="session || disableSendAudio">Audio</mat-checkbox> [disabled]="session || !publishTo">Audio</mat-checkbox>
<mat-checkbox class="send-video-checkbox" name="sendVideo" (change)="toggleSendVideo()" [checked]="sendVideo && checkSendVideo" <mat-checkbox class="send-video-checkbox" name="sendVideo" (click)="toggleSendVideo()" [checked]="publisherProperties.videoSource !== false"
[disabled]="session || disableSendVideo">Video</mat-checkbox> [disabled]="session || !publishTo">Video</mat-checkbox>
</div> </div>
</div> </div>
<div style="padding-top: 5px;"> <div style="padding-top: 5px;">
<h4>Enter active</h4> <h4>Enter active</h4>
<div> <div>
<mat-checkbox class="active-audio-checkbox" name="activeAudio" (change)="toggleActiveAudio()" [checked]="activeAudio && checkActiveAudio" <mat-checkbox class="active-audio-checkbox" name="activeAudio" [(ngModel)]="publisherProperties.publishAudio" (click)="publisherProperties.publishAudio = !publisherProperties.publishAudio"
[disabled]="session || disableActiveAudio">Audio</mat-checkbox> [disabled]="session || !publishTo">Audio</mat-checkbox>
<mat-checkbox class="active-video-checkbox" name="activeVideo" (change)="toggleActiveVideo()" [checked]="activeVideo && checkActiveVideo" <mat-checkbox class="active-video-checkbox" name="activeVideo" [(ngModel)]="publisherProperties.publishVideo" (click)="publisherProperties.publishVideo = !publisherProperties.publishVideo"
[disabled]="session || disableActiveVideo">Video</mat-checkbox> [disabled]="session || !publishTo">Video</mat-checkbox>
</div> </div>
</div> </div>
</div> </div>
<div fxFlex="35"> <div fxFlex="35">
<mat-radio-group [(ngModel)]="optionsVideo" [disabled]="session || disableRadioButtons" [ngModelOptions]="{standalone: true}"> <mat-radio-group [(ngModel)]="optionsVideo" (change)="updateOptionsVideo($event)" [disabled]="session || !publishTo" [ngModelOptions]="{standalone: true}">
<div> <div>
<mat-radio-button class="video-radio" value="video" [checked]="checkRadioVideo && optionsVideo==='video'">Video</mat-radio-button> <mat-radio-button class="video-radio" value="video">Video</mat-radio-button>
</div> </div>
<div> <div>
<mat-radio-button class="screen-radio" value="screen" [checked]="checkRadioScreen && optionsVideo==='screen'">Screen</mat-radio-button> <mat-radio-button class="screen-radio" value="screen">Screen</mat-radio-button>
</div> </div>
</mat-radio-group> </mat-radio-group>
<mat-checkbox class="subscribe-remote-check" name="subscribeToRemote" (change)="subscribeToRemote = !subscribeToRemote" [disabled]="(!sendAudio && !sendVideo) || !publishTo || session" <mat-checkbox class="subscribe-remote-check" name="subscribeToRemote" (click)="subscribeToRemote = !subscribeToRemote" [disabled]="!publishTo || session"
[checked]="(sendAudio || sendVideo) && publishTo && subscribeToRemote">Subscribe [checked]="publishTo && subscribeToRemote">Subscribe
<br>to remote</mat-checkbox> <br>to remote</mat-checkbox>
</div> </div>
<div fxFlex="10"> <div fxFlex="10">
<div fxLayout="column" class="publisher-btns-div"> <div fxLayout="column" class="publisher-btns-div">
<button mat-icon-button title="Publisher properties" [id]="'session-settings-btn-' + index" class="mat-icon-custom" (click)="advancedPublisherOptions()" <button mat-icon-button title="Publisher properties" [id]="'publisher-settings-btn-' + index" class="mat-icon-custom" (click)="openPublisherPropertiesDialog()"
[disabled]="(!sendAudio && !sendVideo) || !publishTo"> [disabled]="!publishTo">
<mat-icon class="mat-icon-custom-ic" aria-label="Session properties button">settings</mat-icon> <mat-icon class="mat-icon-custom-ic" aria-label="Session properties button">settings</mat-icon>
</button> </button>
<button mat-icon-button title="Add new publisher to running session" [id]="'session-api-btn-' + index" class="mat-icon-custom" (click)="addNewPublisher()" <button mat-icon-button title="Add new publisher to running session" [id]="'session-api-btn-' + index" class="mat-icon-custom"
[disabled]="!session || ((!sendAudio && !sendVideo) || !publishTo)"> (click)="addNewPublisher()" [disabled]="!session || ((!sendAudio && !sendVideo) || !publishTo)">
<mat-icon class="mat-icon-custom-ic" aria-label="Session API button">add_circle</mat-icon> <mat-icon class="mat-icon-custom-ic" aria-label="Session API button">add_circle</mat-icon>
</button> </button>
</div> </div>
@ -129,9 +127,10 @@
</div> </div>
<div [attr.id]="'remote-vid-' + session.connection.connectionId" fxFlex="240px" class="video-container"> <div [attr.id]="'remote-vid-' + session.connection.connectionId" fxFlex="240px" class="video-container">
<div [attr.id]="'local-vid-' + session.connection.connectionId"></div> <div [attr.id]="'local-vid-' + session.connection.connectionId"></div>
<app-video *ngIf="this.publisher" [streamManager]="this.publisher" [OV]="OV" (updateEventListInParent)="udpateEventFromChild($event)"> <app-video *ngIf="this.publisher" [streamManager]="this.publisher" [OV]="OV" [properties]="publisherProperties" (updateEventListInParent)="udpateEventFromChild($event)">
</app-video> </app-video>
<app-video *ngFor="let subscriber of this.subscribers" [streamManager]="subscriber" [OV]="OV" (updateEventListInParent)="udpateEventFromChild($event)" (reSubbed)="updateSubscriberFromChild($event)"> <app-video *ngFor="let subscriber of this.subscribers" [streamManager]="subscriber" [OV]="OV" (updateEventListInParent)="udpateEventFromChild($event)"
(reSubbed)="updateSubscriberFromChild($event)">
</app-video> </app-video>
</div> </div>
</div> </div>

View File

@ -7,7 +7,8 @@ import { Subscription } from 'rxjs/Subscription';
import { import {
OpenVidu, Session, Subscriber, Publisher, Stream, Connection, OpenVidu, Session, Subscriber, Publisher, Stream, Connection,
LocalRecorder, VideoInsertMode, StreamEvent, ConnectionEvent, LocalRecorder, VideoInsertMode, StreamEvent, ConnectionEvent,
SessionDisconnectedEvent, SignalEvent, RecordingEvent, VideoElementEvent, PublisherSpeakingEvent, StreamManagerEvent, StreamManager SessionDisconnectedEvent, SignalEvent, RecordingEvent, VideoElementEvent,
PublisherSpeakingEvent, StreamManagerEvent, StreamManager, PublisherProperties
} from 'openvidu-browser'; } from 'openvidu-browser';
import { import {
OpenVidu as OpenViduAPI, OpenVidu as OpenViduAPI,
@ -17,21 +18,19 @@ import {
RecordingMode, RecordingMode,
RecordingLayout RecordingLayout
} from 'openvidu-node-client'; } from 'openvidu-node-client';
import { MatDialog, MatDialogRef } from '@angular/material'; import { MatDialog, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component'; import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component';
import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component'; import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component';
import { TestFeedService } from '../../services/test-feed.service'; import { TestFeedService } from '../../services/test-feed.service';
import { MuteSubscribersService } from '../../services/mute-subscribers.service';
import { EventsDialogComponent } from '../dialogs/events-dialog.component'; import { EventsDialogComponent } from '../dialogs/events-dialog.component';
import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog.component'; import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog.component';
import { SessionApiDialogComponent } from '../dialogs/session-api-dialog.component'; import { SessionApiDialogComponent } from '../dialogs/session-api-dialog.component';
import { PublisherPropertiesDialogComponent } from '../dialogs/publisher-properties-dialog.component';
export interface SessionConf { export interface SessionConf {
subscribeTo: boolean; subscribeTo: boolean;
publishTo: boolean; publishTo: boolean;
sendAudio: boolean;
sendVideo: boolean;
startSession: boolean; startSession: boolean;
} }
@ -43,7 +42,10 @@ export interface OpenViduEvent {
@Component({ @Component({
selector: 'app-openvidu-instance', selector: 'app-openvidu-instance',
templateUrl: './openvidu-instance.component.html', templateUrl: './openvidu-instance.component.html',
styleUrls: ['./openvidu-instance.component.css'] styleUrls: ['./openvidu-instance.component.css'],
providers: [
{ provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' }
]
}) })
export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
@ -66,30 +68,10 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
// Session options // Session options
subscribeTo; subscribeTo;
publishTo; publishTo;
sendAudio;
sendVideo;
activeAudio = true;
activeVideo = true;
sendVideoRadio = true; sendVideoRadio = true;
subscribeToRemote = false; subscribeToRemote = false;
optionsVideo = 'video'; optionsVideo = 'video';
// Form 'check' and 'disable' attributes
checkSubscribeTo = true;
checkPublishTo = true;
checkSendAudio = true;
checkSendVideo = true;
checkActiveAudio = true;
checkActiveVideo = true;
checkRadioVideo = true;
checkRadioScreen = false;
disablePublishTo = false;
disableSendAudio = false;
disableSendVideo = false;
disableActiveAudio = false;
disableActiveVideo = false;
disableRadioButtons = false;
// OpenVidu Browser objects // OpenVidu Browser objects
OV: OpenVidu; OV: OpenVidu;
session: Session; session: Session;
@ -105,6 +87,18 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
customSessionId: '' customSessionId: ''
}; };
publisherProperties: PublisherProperties = {
audioSource: undefined,
videoSource: undefined,
frameRate: 30,
resolution: '640x480',
mirror: true,
publishAudio: true,
publishVideo: true
};
publisherPropertiesAux: PublisherProperties;
sessionEvents = { sessionEvents = {
connectionCreated: true, connectionCreated: true,
connectionDestroyed: true, connectionDestroyed: true,
@ -139,14 +133,11 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
ngOnInit() { ngOnInit() {
this.subscribeTo = this.sessionConf.subscribeTo; this.subscribeTo = this.sessionConf.subscribeTo;
this.publishTo = this.sessionConf.publishTo; this.publishTo = this.sessionConf.publishTo;
this.sendAudio = this.sessionConf.sendAudio; this.publisherPropertiesAux = Object.assign({}, this.publisherProperties);
this.sendVideo = this.sessionConf.sendVideo;
if (!this.publishTo) { if (!this.publishTo) {
this.publishTo = !this.publishTo; this.publishTo = !this.publishTo;
this.togglePublishTo(); this.togglePublishTo();
} }
if (this.sessionConf.startSession) { if (this.sessionConf.startSession) {
this.joinSession(); this.joinSession();
} }
@ -242,76 +233,49 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
togglePublishTo(): void { togglePublishTo(): void {
this.publishTo = !this.publishTo; this.publishTo = !this.publishTo;
this.sendAudio = this.publishTo;
this.sendVideo = this.publishTo;
this.activeAudio = this.publishTo;
this.activeVideo = this.publishTo;
this.checkPublishTo = this.publishTo;
this.checkSendAudio = this.publishTo;
this.checkSendVideo = this.publishTo;
this.checkActiveAudio = this.publishTo;
this.checkActiveVideo = this.publishTo;
if (this.publishTo) { if (this.publishTo) {
this.checkRadioVideo = true; this.publisherProperties = this.publisherPropertiesAux;
this.optionsVideo = 'video';
} else { } else {
this.checkRadioVideo = false; this.publisherPropertiesAux = Object.assign({}, this.publisherProperties);
this.optionsVideo = ''; this.publisherProperties.publishAudio = false;
this.publisherProperties.publishVideo = false;
this.publisherProperties.audioSource = false;
this.publisherProperties.videoSource = false;
} }
this.disableSendAudio = !this.publishTo; if (this.publishTo) {
this.disableSendVideo = !this.publishTo; this.optionsVideo = 'video';
this.disableActiveAudio = !this.publishTo; } else {
this.disableActiveVideo = !this.publishTo; this.optionsVideo = '';
this.disableRadioButtons = !this.publishTo; }
this.subscribeToRemote = false; this.subscribeToRemote = false;
} }
toggleSendAudio(): void { toggleSendAudio(): void {
this.sendAudio = !this.sendAudio; if (this.publisherProperties.audioSource === false) {
this.publisherProperties.audioSource = this.publisherPropertiesAux.audioSource;
this.activeAudio = this.sendAudio; } else {
this.checkActiveAudio = this.sendAudio; this.publisherPropertiesAux.audioSource = this.publisherProperties.audioSource;
this.disableActiveAudio = !this.sendAudio; this.publisherProperties.audioSource = false;
if (!this.sendAudio && !this.sendVideo && this.publishTo) {
this.togglePublishTo();
} }
} }
toggleSendVideo(): void { toggleSendVideo(): void {
this.sendVideo = !this.sendVideo; if (this.publisherProperties.videoSource === false) {
this.publisherProperties.videoSource = this.publisherPropertiesAux.videoSource;
this.activeVideo = this.sendVideo;
this.checkActiveVideo = this.sendVideo;
this.checkRadioScreen = false;
if (this.sendVideo) {
this.checkRadioVideo = true;
this.optionsVideo = 'video';
} else { } else {
this.checkRadioVideo = false; this.publisherPropertiesAux.videoSource = this.publisherProperties.videoSource;
this.optionsVideo = ''; this.publisherProperties.videoSource = false;
}
this.disableActiveVideo = !this.sendVideo;
this.disableRadioButtons = !this.sendVideo;
if (!this.sendAudio && !this.sendVideo && this.publishTo) {
this.togglePublishTo();
} }
} }
toggleActiveAudio(): void { toggleActiveAudio(): void {
this.activeAudio = !this.activeAudio; this.publisherProperties.publishAudio = !this.publisherProperties.publishAudio;
} }
toggleActiveVideo(): void { toggleActiveVideo(): void {
this.activeVideo = !this.activeVideo; this.publisherProperties.publishVideo = !this.publisherProperties.publishVideo;
} }
sendMessage(): void { sendMessage(): void {
@ -438,14 +402,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
syncInitPublisher() { syncInitPublisher() {
this.publisher = this.OV.initPublisher( this.publisher = this.OV.initPublisher(
undefined, undefined,
{ this.publisherProperties,
audioSource: this.sendAudio ? undefined : false,
videoSource: this.sendVideo ? (this.optionsVideo === 'screen' ? 'screen' : undefined) : false,
publishAudio: this.activeAudio,
publishVideo: this.activeVideo,
resolution: '640x480',
frameRate: 30
},
(err) => { (err) => {
if (err) { if (err) {
console.warn(err); console.warn(err);
@ -467,46 +424,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.session.publish(this.publisher); this.session.publish(this.publisher);
} }
asyncInitPublisher() {
this.OV.initPublisherAsync(
'local-vid-' + this.session.connection.connectionId,
{
audioSource: this.sendAudio ? undefined : false,
videoSource: this.sendVideo ? (this.optionsVideo === 'screen' ? 'screen' : undefined) : false,
publishAudio: this.activeAudio,
publishVideo: this.activeVideo,
resolution: '640x480',
frameRate: 30,
insertMode: VideoInsertMode.APPEND
})
.then(publisher => {
this.publisher = publisher;
if (this.subscribeToRemote) {
this.publisher.subscribeToRemote();
}
this.session.publish(this.publisher)
.then(() => {
console.log(this.publisher);
})
.catch(e => {
console.error(e);
});
})
.catch(err => {
if (err) {
console.error(err);
this.openviduError = err;
if (err.name === 'SCREEN_EXTENSION_NOT_INSTALLED') {
this.dialog.open(ExtensionDialogComponent, {
data: { url: err.message },
disableClose: true,
width: '250px'
});
}
}
});
}
syncSubscribe(session: Session, event) { syncSubscribe(session: Session, event) {
this.subscribers.push(session.subscribe(event.stream, undefined)); this.subscribers.push(session.subscribe(event.stream, undefined));
} }
@ -575,7 +492,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
openVidu: new OpenViduAPI(this.openviduUrl, this.openviduSecret), openVidu: new OpenViduAPI(this.openviduUrl, this.openviduSecret),
sessionId: !!this.session ? this.session.sessionId : this.sessionName sessionId: !!this.session ? this.session.sessionId : this.sessionName
}, },
width: '280px' width: '280px',
disableClose: true
}); });
dialogRef.afterClosed().subscribe((result: string) => { dialogRef.afterClosed().subscribe((result: string) => {
@ -630,6 +548,22 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
openPublisherPropertiesDialog() {
const dialogRef = this.dialog.open(PublisherPropertiesDialogComponent, {
data: this.publisherProperties,
width: '300px',
disableClose: true
});
dialogRef.afterClosed().subscribe((result: PublisherProperties) => {
if (!!result) {
this.publisherProperties = result;
this.optionsVideo = this.publisherProperties.videoSource === 'screen' ? 'screen' : 'video';
}
document.getElementById('publisher-settings-btn-' + this.index).classList.remove('cdk-program-focused');
});
}
getToken(): Promise<string> { getToken(): Promise<string> {
const OV_NodeClient = new OpenViduAPI(this.openviduUrl, this.openviduSecret); const OV_NodeClient = new OpenViduAPI(this.openviduUrl, this.openviduSecret);
if (!this.sessionProperties.customSessionId) { if (!this.sessionProperties.customSessionId) {
@ -652,4 +586,19 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.subscribers[this.subscribers.indexOf(oldSubscriber)] = newSubscriber; this.subscribers[this.subscribers.indexOf(oldSubscriber)] = newSubscriber;
} }
updateOptionsVideo(change) {
if (change.value === 'screen') {
this.publisherPropertiesAux.videoSource = this.publisherProperties.videoSource;
this.publisherProperties.videoSource = 'screen';
} else {
this.publisherProperties.videoSource = this.publisherPropertiesAux.videoSource;
}
}
isVideo(): boolean {
return (this.publisherProperties.videoSource === undefined ||
typeof this.publisherProperties.videoSource === 'string' &&
this.publisherProperties.videoSource !== 'screen');
}
} }

View File

@ -50,8 +50,6 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
this.users.push({ this.users.push({
subscribeTo: true, subscribeTo: true,
publishTo: true, publishTo: true,
sendAudio: true,
sendVideo: true,
startSession: false startSession: false
}); });
} }
@ -69,8 +67,6 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
this.users.push({ this.users.push({
subscribeTo: true, subscribeTo: true,
publishTo: true, publishTo: true,
sendAudio: true,
sendVideo: true,
startSession: this.autoJoin startSession: this.autoJoin
}); });
} }
@ -81,8 +77,6 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
this.users.push({ this.users.push({
subscribeTo: true, subscribeTo: true,
publishTo: false, publishTo: false,
sendAudio: false,
sendVideo: false,
startSession: this.autoJoin startSession: this.autoJoin
}); });
} }
@ -93,8 +87,6 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
this.users.push({ this.users.push({
subscribeTo: false, subscribeTo: false,
publishTo: true, publishTo: true,
sendAudio: true,
sendVideo: true,
startSession: this.autoJoin startSession: this.autoJoin
}); });
} }

View File

@ -10,10 +10,10 @@
<button class="video-btn pub-btn" title="Publish/Unpublish" (click)="pubUnpub()"> <button class="video-btn pub-btn" title="Publish/Unpublish" (click)="pubUnpub()">
<mat-icon aria-label="Publish or unpublish" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubIcon}}</mat-icon> <mat-icon aria-label="Publish or unpublish" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubIcon}}</mat-icon>
</button> </button>
<button class="video-btn pub-video-btn" title="Publish/Unpublish Video" (click)="pubUnpubVideo()"> <button *ngIf="streamManager.stream.hasVideo" class="video-btn pub-video-btn" title="Publish/Unpublish Video" (click)="pubUnpubVideo()">
<mat-icon aria-label="Publish or unpublish video" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubVideoIcon}}</mat-icon> <mat-icon aria-label="Publish or unpublish video" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubVideoIcon}}</mat-icon>
</button> </button>
<button class="video-btn pub-audio-btn" title="Publish/Unpublish Audio" (click)="pubUnpubAudio()"> <button *ngIf="streamManager.stream.hasAudio" class="video-btn pub-audio-btn" title="Publish/Unpublish Audio" (click)="pubUnpubAudio()">
<mat-icon aria-label="Publish or unpublish audio" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubAudioIcon}}</mat-icon> <mat-icon aria-label="Publish or unpublish audio" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubAudioIcon}}</mat-icon>
</button> </button>
<button class="video-btn change-publisher-btn" title="Change publisher" (click)="changePub()"> <button class="video-btn change-publisher-btn" title="Change publisher" (click)="changePub()">
@ -41,10 +41,10 @@
<button class="video-btn sub-btn" title="Subscribe/Unsubscribe" (click)="subUnsub()"> <button class="video-btn sub-btn" title="Subscribe/Unsubscribe" (click)="subUnsub()">
<mat-icon aria-label="Subscribe or unsubscribe" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubIcon}}</mat-icon> <mat-icon aria-label="Subscribe or unsubscribe" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubIcon}}</mat-icon>
</button> </button>
<button *ngIf="!!pubSubVideoIcon" class="video-btn sub-video-btn" title="Subscribe/Unsubscribe Video" (click)="subUnsubVideo()"> <button *ngIf="streamManager.stream.hasVideo && !!pubSubVideoIcon" class="video-btn sub-video-btn" title="Subscribe/Unsubscribe Video" (click)="subUnsubVideo()">
<mat-icon aria-label="Subscribe or unsubscribe video" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubVideoIcon}}</mat-icon> <mat-icon aria-label="Subscribe or unsubscribe video" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubVideoIcon}}</mat-icon>
</button> </button>
<button *ngIf="!!pubSubAudioIcon" class="video-btn sub-audio-btn" title="Subscribe/Unsubscribe Audio" (click)="subUnsubAudio()"> <button *ngIf="streamManager.stream.hasAudio && !!pubSubAudioIcon" class="video-btn sub-audio-btn" title="Subscribe/Unsubscribe Audio" (click)="subUnsubAudio()">
<mat-icon aria-label="Subscribe or unsubscribe audio" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubAudioIcon}}</mat-icon> <mat-icon aria-label="Subscribe or unsubscribe audio" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubAudioIcon}}</mat-icon>
</button> </button>
<button *ngIf="!!recordIcon" class="video-btn rec-btn" title="Record" (click)="record()"> <button *ngIf="!!recordIcon" class="video-btn rec-btn" title="Record" (click)="record()">

View File

@ -29,6 +29,7 @@ import { OpenViduVideoComponent } from './ov-video.component';
export class VideoComponent implements OnInit, OnDestroy { export class VideoComponent implements OnInit, OnDestroy {
@Input() streamManager: StreamManager; @Input() streamManager: StreamManager;
@Input() properties: any;
@Input() OV: OpenVidu; @Input() OV: OpenVidu;
@Input() eventCollection: any; @Input() eventCollection: any;
@ -111,13 +112,19 @@ export class VideoComponent implements OnInit, OnDestroy {
}); });
this.sendAudio = this.streamManager.stream.hasAudio; this.sendAudio = this.streamManager.stream.hasAudio;
this.sendVideo = this.streamManager.stream.hasVideo; this.sendVideo = this.streamManager.stream.hasVideo;
this.audioMuted = !this.properties.publishAudio;
this.videoMuted = !this.properties.publishVideo;
this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
this.optionsVideo = this.streamManager.stream.typeOfVideo; this.optionsVideo = this.streamManager.stream.typeOfVideo;
} }
this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(muteOrUnmute => { this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(muteOrUnmute => {
this.streamManager.videos.forEach(v => { if (this.streamManager.remote) {
v.video.muted = muteOrUnmute; this.streamManager.videos.forEach(v => {
}); v.video.muted = muteOrUnmute;
});
}
}); });
} }
@ -196,15 +203,15 @@ export class VideoComponent implements OnInit, OnDestroy {
pubUnpubVideo() { pubUnpubVideo() {
const publisher: Publisher = <Publisher>this.streamManager; const publisher: Publisher = <Publisher>this.streamManager;
publisher.publishVideo(this.videoMuted);
this.videoMuted = !this.videoMuted; this.videoMuted = !this.videoMuted;
publisher.publishVideo(!this.videoMuted);
this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam'; this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam';
} }
pubUnpubAudio() { pubUnpubAudio() {
const publisher: Publisher = <Publisher>this.streamManager; const publisher: Publisher = <Publisher>this.streamManager;
publisher.publishAudio(this.audioMuted);
this.audioMuted = !this.audioMuted; this.audioMuted = !this.audioMuted;
publisher.publishAudio(!this.audioMuted);
this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic'; this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic';
} }
@ -233,8 +240,6 @@ export class VideoComponent implements OnInit, OnDestroy {
screenChange = this.optionsVideo === 'SCREEN' ? true : false; screenChange = this.optionsVideo === 'SCREEN' ? true : false;
} }
this.audioMuted = false;
this.videoMuted = false;
this.unpublished = false; this.unpublished = false;
const otherPublisher = this.OV.initPublisher( const otherPublisher = this.OV.initPublisher(
@ -242,8 +247,8 @@ export class VideoComponent implements OnInit, OnDestroy {
{ {
audioSource: this.sendAudioChange ? undefined : false, audioSource: this.sendAudioChange ? undefined : false,
videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false, videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false,
publishAudio: (!this.publisherChanged) ? true : !this.audioMuted, publishAudio: !this.audioMuted,
publishVideo: (!this.publisherChanged) ? true : !this.videoMuted, publishVideo: !this.videoMuted,
resolution: '640x480', resolution: '640x480',
frameRate: 30, frameRate: 30,
insertMode: VideoInsertMode.APPEND insertMode: VideoInsertMode.APPEND
@ -272,13 +277,17 @@ export class VideoComponent implements OnInit, OnDestroy {
streamDestroyed: !this.eventCollection.streamDestroyed streamDestroyed: !this.eventCollection.streamDestroyed
}); });
const oldPublisher = <Publisher>this.streamManager;
if (oldPublisher.isSubscribedToRemote) {
otherPublisher.subscribeToRemote(true);
}
otherPublisher.once('accessAllowed', () => { otherPublisher.once('accessAllowed', () => {
if (!this.unpublished) { if (!this.unpublished) {
this.streamManager.stream.session.unpublish(<Publisher>this.streamManager); this.streamManager.stream.session.unpublish(oldPublisher);
this.streamManager = otherPublisher;
} }
this.streamManager.stream.session.publish(otherPublisher).then(() => { this.streamManager.stream.session.publish(otherPublisher).then(() => {
console.log(this.streamManager); this.streamManager = otherPublisher;
}); });
}); });
@ -455,7 +464,7 @@ export class VideoComponent implements OnInit, OnDestroy {
this.showButtons = true; this.showButtons = true;
this.updateEventListInParent.emit({ this.updateEventListInParent.emit({
event: 'streamPlaying', event: 'streamPlaying',
content: this.streamManager.stream.streamId content: pub.stream.streamId
}); });
}); });
} }