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/flex-layout": "^13.0.0-beta.36",
|
||||
"autolinker": "^3.14.3",
|
||||
"buffer": "^6.0.3",
|
||||
"openvidu-browser": "^2.20.0"
|
||||
},
|
||||
"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 { ParticipantItemComponent } from './components/participants-panel/participant-item/participant-item.component';
|
||||
import { ParticipantPanelComponent } from './components/participants-panel/participant-panel/participant-panel.component';
|
||||
import { VideoconferenceComponent } from './components/videoconference/videoconference.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
UserSettingsComponent,
|
||||
VideoComponent,
|
||||
ToolbarComponent,
|
||||
ChatComponent,
|
||||
RoomComponent,
|
||||
LayoutComponent,
|
||||
ParticipantComponent,
|
||||
DialogTemplateComponent,
|
||||
LinkifyPipe,
|
||||
TooltipListPipe,
|
||||
ParticipantConnectionsPipe,
|
||||
ConnectionsEnabledPipe,
|
||||
NicknamePipe,
|
||||
ParticipantItemComponent,
|
||||
ParticipantPanelComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
declarations: [
|
||||
UserSettingsComponent,
|
||||
VideoComponent,
|
||||
ToolbarComponent,
|
||||
ChatComponent,
|
||||
RoomComponent,
|
||||
LayoutComponent,
|
||||
ParticipantComponent,
|
||||
DialogTemplateComponent,
|
||||
LinkifyPipe,
|
||||
TooltipListPipe,
|
||||
ParticipantConnectionsPipe,
|
||||
ConnectionsEnabledPipe,
|
||||
NicknamePipe,
|
||||
ParticipantItemComponent,
|
||||
ParticipantPanelComponent,
|
||||
VideoconferenceComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule.forRoot([]),
|
||||
RouterModule.forRoot([]),
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatToolbarModule,
|
||||
|
@ -99,53 +101,46 @@ import { ParticipantPanelComponent } from './components/participants-panel/parti
|
|||
MatSnackBarModule,
|
||||
FlexLayoutModule,
|
||||
MatMenuModule,
|
||||
MatDividerModule,
|
||||
MatListModule
|
||||
],
|
||||
providers: [
|
||||
ActionService,
|
||||
CdkOverlayContainer,
|
||||
MatDividerModule,
|
||||
MatListModule
|
||||
],
|
||||
providers: [
|
||||
ActionService,
|
||||
CdkOverlayContainer,
|
||||
{ provide: OverlayContainer, useClass: CdkOverlayContainer },
|
||||
ChatService,
|
||||
SidenavMenuService,
|
||||
DeviceService,
|
||||
DocumentService,
|
||||
LayoutService,
|
||||
LoggerService,
|
||||
PlatformService,
|
||||
ParticipantService,
|
||||
StorageService,
|
||||
TokenService,
|
||||
WebrtcService
|
||||
|
||||
],
|
||||
exports: [
|
||||
UserSettingsComponent,
|
||||
ToolbarComponent,
|
||||
ChatComponent,
|
||||
RoomComponent,
|
||||
LayoutComponent,
|
||||
ParticipantComponent,
|
||||
VideoComponent,
|
||||
ParticipantConnectionsPipe,
|
||||
CommonModule
|
||||
],
|
||||
entryComponents: [
|
||||
DialogTemplateComponent
|
||||
]
|
||||
ChatService,
|
||||
SidenavMenuService,
|
||||
DeviceService,
|
||||
DocumentService,
|
||||
LayoutService,
|
||||
LoggerService,
|
||||
PlatformService,
|
||||
ParticipantService,
|
||||
StorageService,
|
||||
TokenService,
|
||||
WebrtcService
|
||||
],
|
||||
exports: [
|
||||
VideoconferenceComponent,
|
||||
UserSettingsComponent,
|
||||
ToolbarComponent,
|
||||
ChatComponent,
|
||||
RoomComponent,
|
||||
LayoutComponent,
|
||||
ParticipantComponent,
|
||||
VideoComponent,
|
||||
ParticipantConnectionsPipe,
|
||||
CommonModule
|
||||
],
|
||||
entryComponents: [DialogTemplateComponent]
|
||||
})
|
||||
|
||||
|
||||
export class OpenviduAngularModule {
|
||||
static forRoot(environment): ModuleWithProviders<OpenviduAngularModule> {
|
||||
|
||||
// console.log(`${library.name} config: ${environment}`);
|
||||
const libConfig: LibConfig = { environment };
|
||||
return {
|
||||
ngModule: OpenviduAngularModule,
|
||||
providers: [LibraryConfigService , {provide: 'LIB_CONFIG', useValue: libConfig}]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
static forRoot(environment): ModuleWithProviders<OpenviduAngularModule> {
|
||||
// console.log(`${library.name} config: ${environment}`);
|
||||
const libConfig: LibConfig = { environment };
|
||||
return {
|
||||
ngModule: OpenviduAngularModule,
|
||||
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';
|
||||
|
||||
// Components
|
||||
export * from './lib/components/videoconference/videoconference.component';
|
||||
export * from './lib/components/user-settings/user-settings.component';
|
||||
export * from './lib/components/toolbar/toolbar.component';
|
||||
export * from './lib/components/chat/chat.component';
|
||||
|
|
Loading…
Reference in New Issue