mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Added openvidu videoconference components
parent
673058381f
commit
3c97ceac37
|
@ -9,6 +9,7 @@
|
||||||
"@angular/forms": "^13.0.0",
|
"@angular/forms": "^13.0.0",
|
||||||
"@angular/flex-layout": "^13.0.0-beta.36",
|
"@angular/flex-layout": "^13.0.0-beta.36",
|
||||||
"autolinker": "^3.14.3",
|
"autolinker": "^3.14.3",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"openvidu-browser": "^2.20.0"
|
"openvidu-browser": "^2.20.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#call-container, #room-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-settings-container {
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-icon {
|
||||||
|
color: var(--ov-warn-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
text-align: -webkit-center;
|
||||||
|
text-align: -moz-center;
|
||||||
|
color: var(--ov-primary-color);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<div id="call-container">
|
||||||
|
<div id="user-settings-container" *ngIf="!joinSessionClicked && !closeClicked">
|
||||||
|
<ov-user-settings (onJoinClicked)="onJoinClicked()" (onCloseClicked)="onLeaveSessionClicked()"></ov-user-settings>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && !error">
|
||||||
|
<mat-spinner [diameter]="50"></mat-spinner>
|
||||||
|
<span>Joining the room ...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="spinner" *ngIf="joinSessionClicked && !isSessionAlive && error">
|
||||||
|
<mat-icon class="error-icon">error</mat-icon>
|
||||||
|
<span>{{errorMessage}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="room-container" *ngIf="joinSessionClicked && isSessionAlive && !error">
|
||||||
|
<ov-room [tokens]="_tokens">
|
||||||
|
<ng-template #toolbar>
|
||||||
|
<ov-toolbar
|
||||||
|
(onCamClicked)="onCamClicked()"
|
||||||
|
(onMicClicked)="onMicClicked()"
|
||||||
|
(onScreenShareClicked)="onScreenShareClicked()"
|
||||||
|
(onSpeakerLayoutClicked)="onSpeakerLayoutClicked()"
|
||||||
|
(onLeaveSessionClicked)="onLeaveSessionClicked()"
|
||||||
|
></ov-toolbar>
|
||||||
|
</ng-template>
|
||||||
|
<ov-layout layout></ov-layout>
|
||||||
|
</ov-room>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { VideoconferenceComponent } from './videoconference.component';
|
||||||
|
|
||||||
|
describe('VideoconferenceComponent', () => {
|
||||||
|
let component: VideoconferenceComponent;
|
||||||
|
let fixture: ComponentFixture<VideoconferenceComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ VideoconferenceComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(VideoconferenceComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { Component, Input, OnInit, Output } from '@angular/core';
|
||||||
|
import { RestService } from '../../services/rest/rest.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ov-videoconference',
|
||||||
|
templateUrl: './videoconference.component.html',
|
||||||
|
styleUrls: ['./videoconference.component.css']
|
||||||
|
})
|
||||||
|
export class VideoconferenceComponent implements OnInit {
|
||||||
|
@Input() sessionName: string;
|
||||||
|
@Input() userName: string;
|
||||||
|
@Input() openviduServerUrl: string;
|
||||||
|
@Input() openviduSecret: string;
|
||||||
|
@Input() tokens: string[];
|
||||||
|
|
||||||
|
joinSessionClicked: boolean = false;
|
||||||
|
closeClicked: boolean = false;
|
||||||
|
isSessionAlive: boolean = false;
|
||||||
|
_tokens: { webcam: string; screen: string };
|
||||||
|
error: boolean = false;
|
||||||
|
errorMessage: string = '';
|
||||||
|
|
||||||
|
constructor(private restService: RestService) {}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
async onJoinClicked() {
|
||||||
|
|
||||||
|
if (!this.tokens || this.tokens?.length === 0) {
|
||||||
|
//No tokens received
|
||||||
|
|
||||||
|
if (!!this.sessionName && !!this.openviduServerUrl && !!this.openviduSecret) {
|
||||||
|
// Generate tokens
|
||||||
|
this._tokens = {
|
||||||
|
webcam: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret),
|
||||||
|
screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// No tokens received and can't generate them
|
||||||
|
this.error = true;
|
||||||
|
this.errorMessage = `Cannot access to OpenVidu Server with url '${this.openviduServerUrl}' to genere tokens for session '${this.sessionName}'`;
|
||||||
|
throw this.errorMessage;
|
||||||
|
}
|
||||||
|
} else if (this.tokens?.length < 2) {
|
||||||
|
// 1 token received
|
||||||
|
this._tokens = {
|
||||||
|
webcam: this.tokens[0],
|
||||||
|
screen: await this.restService.getToken(this.sessionName, this.openviduServerUrl, this.openviduSecret)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 2 tokens received.
|
||||||
|
this._tokens = {
|
||||||
|
webcam: this.tokens[0],
|
||||||
|
screen: this.tokens[1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.joinSessionClicked = true;
|
||||||
|
this.isSessionAlive = true;
|
||||||
|
}
|
||||||
|
onLeaveSessionClicked() {
|
||||||
|
this.isSessionAlive = false;
|
||||||
|
this.closeClicked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMicClicked() {}
|
||||||
|
|
||||||
|
onCamClicked() {}
|
||||||
|
|
||||||
|
onScreenShareClicked() {}
|
||||||
|
|
||||||
|
onSpeakerLayoutClicked() {}
|
||||||
|
}
|
|
@ -56,31 +56,33 @@ import { SidenavMenuService } from './services/sidenav-menu/sidenav-menu.service
|
||||||
import { ParticipantService } from './services/participant/participant.service';
|
import { ParticipantService } from './services/participant/participant.service';
|
||||||
import { ParticipantItemComponent } from './components/participants-panel/participant-item/participant-item.component';
|
import { ParticipantItemComponent } from './components/participants-panel/participant-item/participant-item.component';
|
||||||
import { ParticipantPanelComponent } from './components/participants-panel/participant-panel/participant-panel.component';
|
import { ParticipantPanelComponent } from './components/participants-panel/participant-panel/participant-panel.component';
|
||||||
|
import { VideoconferenceComponent } from './components/videoconference/videoconference.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
UserSettingsComponent,
|
UserSettingsComponent,
|
||||||
VideoComponent,
|
VideoComponent,
|
||||||
ToolbarComponent,
|
ToolbarComponent,
|
||||||
ChatComponent,
|
ChatComponent,
|
||||||
RoomComponent,
|
RoomComponent,
|
||||||
LayoutComponent,
|
LayoutComponent,
|
||||||
ParticipantComponent,
|
ParticipantComponent,
|
||||||
DialogTemplateComponent,
|
DialogTemplateComponent,
|
||||||
LinkifyPipe,
|
LinkifyPipe,
|
||||||
TooltipListPipe,
|
TooltipListPipe,
|
||||||
ParticipantConnectionsPipe,
|
ParticipantConnectionsPipe,
|
||||||
ConnectionsEnabledPipe,
|
ConnectionsEnabledPipe,
|
||||||
NicknamePipe,
|
NicknamePipe,
|
||||||
ParticipantItemComponent,
|
ParticipantItemComponent,
|
||||||
ParticipantPanelComponent
|
ParticipantPanelComponent,
|
||||||
],
|
VideoconferenceComponent
|
||||||
imports: [
|
],
|
||||||
CommonModule,
|
imports: [
|
||||||
HttpClientModule,
|
CommonModule,
|
||||||
FormsModule,
|
HttpClientModule,
|
||||||
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
RouterModule.forRoot([]),
|
RouterModule.forRoot([]),
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
|
@ -99,53 +101,46 @@ import { ParticipantPanelComponent } from './components/participants-panel/parti
|
||||||
MatSnackBarModule,
|
MatSnackBarModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatDividerModule,
|
MatDividerModule,
|
||||||
MatListModule
|
MatListModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ActionService,
|
ActionService,
|
||||||
CdkOverlayContainer,
|
CdkOverlayContainer,
|
||||||
{ provide: OverlayContainer, useClass: CdkOverlayContainer },
|
{ provide: OverlayContainer, useClass: CdkOverlayContainer },
|
||||||
ChatService,
|
ChatService,
|
||||||
SidenavMenuService,
|
SidenavMenuService,
|
||||||
DeviceService,
|
DeviceService,
|
||||||
DocumentService,
|
DocumentService,
|
||||||
LayoutService,
|
LayoutService,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
PlatformService,
|
PlatformService,
|
||||||
ParticipantService,
|
ParticipantService,
|
||||||
StorageService,
|
StorageService,
|
||||||
TokenService,
|
TokenService,
|
||||||
WebrtcService
|
WebrtcService
|
||||||
|
],
|
||||||
],
|
exports: [
|
||||||
exports: [
|
VideoconferenceComponent,
|
||||||
UserSettingsComponent,
|
UserSettingsComponent,
|
||||||
ToolbarComponent,
|
ToolbarComponent,
|
||||||
ChatComponent,
|
ChatComponent,
|
||||||
RoomComponent,
|
RoomComponent,
|
||||||
LayoutComponent,
|
LayoutComponent,
|
||||||
ParticipantComponent,
|
ParticipantComponent,
|
||||||
VideoComponent,
|
VideoComponent,
|
||||||
ParticipantConnectionsPipe,
|
ParticipantConnectionsPipe,
|
||||||
CommonModule
|
CommonModule
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [DialogTemplateComponent]
|
||||||
DialogTemplateComponent
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export class OpenviduAngularModule {
|
export class OpenviduAngularModule {
|
||||||
static forRoot(environment): ModuleWithProviders<OpenviduAngularModule> {
|
static forRoot(environment): ModuleWithProviders<OpenviduAngularModule> {
|
||||||
|
// console.log(`${library.name} config: ${environment}`);
|
||||||
// console.log(`${library.name} config: ${environment}`);
|
const libConfig: LibConfig = { environment };
|
||||||
const libConfig: LibConfig = { environment };
|
return {
|
||||||
return {
|
ngModule: OpenviduAngularModule,
|
||||||
ngModule: OpenviduAngularModule,
|
providers: [LibraryConfigService, { provide: 'LIB_CONFIG', useValue: libConfig }]
|
||||||
providers: [LibraryConfigService , {provide: 'LIB_CONFIG', useValue: libConfig}]
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RestService } from './rest.service';
|
||||||
|
|
||||||
|
describe('RestService', () => {
|
||||||
|
let service: RestService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(RestService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
import { catchError, throwError } from 'rxjs';
|
||||||
|
import { Buffer} from 'buffer';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class RestService {
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
async getToken(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise<string> {
|
||||||
|
if (!!openviduServerUrl && !!openviduSecret) {
|
||||||
|
const _sessionId = await this.createSession(sessionId, openviduServerUrl, openviduSecret);
|
||||||
|
return await this.createToken(_sessionId, openviduServerUrl, openviduSecret);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(`Error requesting a token to ${openviduServerUrl} with session id: ${sessionId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createSession(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const body = JSON.stringify({ customSessionId: sessionId });
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
Authorization: 'Basic ' + this.btoa('OPENVIDUAPP:' + openviduSecret),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return this.http
|
||||||
|
.post<any>(openviduServerUrl + '/openvidu/api/sessions', body, options)
|
||||||
|
.pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
if (error.status === 409) {
|
||||||
|
resolve(sessionId);
|
||||||
|
}
|
||||||
|
if (error.statusText === 'Unknown Error') {
|
||||||
|
reject({ status: 401, message: 'ERR_CERT_AUTHORITY_INVALID' });
|
||||||
|
}
|
||||||
|
return throwError(() => new Error(error));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((response) => {
|
||||||
|
resolve(response.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createToken(sessionId: string, openviduServerUrl: string, openviduSecret: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const body = JSON.stringify({});
|
||||||
|
const options = {
|
||||||
|
headers: new HttpHeaders({
|
||||||
|
Authorization: 'Basic ' + this.btoa('OPENVIDUAPP:' + openviduSecret),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return this.http
|
||||||
|
.post<any>(openviduServerUrl + '/openvidu/api/sessions/' + sessionId + '/connection', body, options)
|
||||||
|
.pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
reject(error);
|
||||||
|
return throwError(() => new Error(error));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((response) => {
|
||||||
|
resolve(response.token);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private btoa(str: string): string {
|
||||||
|
return Buffer.from(str).toString('base64');
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ export * from './lib/services/cdk-overlay/cdk-overlay.service';
|
||||||
export * from './lib/services/storage/storage.service';
|
export * from './lib/services/storage/storage.service';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
export * from './lib/components/videoconference/videoconference.component';
|
||||||
export * from './lib/components/user-settings/user-settings.component';
|
export * from './lib/components/user-settings/user-settings.component';
|
||||||
export * from './lib/components/toolbar/toolbar.component';
|
export * from './lib/components/toolbar/toolbar.component';
|
||||||
export * from './lib/components/chat/chat.component';
|
export * from './lib/components/chat/chat.component';
|
||||||
|
|
Loading…
Reference in New Issue