331 lines
10 KiB
TypeScript
331 lines
10 KiB
TypeScript
import { HttpClient } from '@angular/common/http';
|
|
import { Component, HostListener, OnDestroy } from '@angular/core';
|
|
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
|
|
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
|
|
import { StatusBar } from '@ionic-native/status-bar/ngx';
|
|
import { AlertController, Platform } from '@ionic/angular';
|
|
import {
|
|
Device,
|
|
OpenVidu,
|
|
Publisher,
|
|
PublisherProperties,
|
|
Session,
|
|
StreamEvent,
|
|
StreamManager,
|
|
Subscriber
|
|
} from 'openvidu-browser';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: 'app.component.html',
|
|
styleUrls: ['app.component.css']
|
|
})
|
|
export class AppComponent implements OnDestroy {
|
|
|
|
APPLICATION_SERVER_URL = location.protocol + '//' + location.hostname + ':5000/';
|
|
|
|
ANDROID_PERMISSIONS = [
|
|
this.androidPermissions.PERMISSION.CAMERA,
|
|
this.androidPermissions.PERMISSION.RECORD_AUDIO,
|
|
this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS
|
|
];
|
|
|
|
// OpenVidu objects
|
|
OV: OpenVidu;
|
|
session: Session;
|
|
publisher: StreamManager; // Local
|
|
subscribers: StreamManager[] = []; // Remotes
|
|
|
|
// Join form
|
|
mySessionId: string;
|
|
myUserName: string;
|
|
|
|
cameraIcon = 'videocam';
|
|
microphoneIcon = 'mic';
|
|
|
|
private devices: Device[];
|
|
private cameras: Device[];
|
|
private microphones: Device[];
|
|
private cameraSelected: Device;
|
|
private microphoneSelected: Device;
|
|
private isFrontCamera: boolean = false;
|
|
|
|
constructor(
|
|
private httpClient: HttpClient,
|
|
private platform: Platform,
|
|
private splashScreen: SplashScreen,
|
|
private statusBar: StatusBar,
|
|
private androidPermissions: AndroidPermissions,
|
|
private alertController: AlertController
|
|
) {
|
|
this.initializeApp();
|
|
this.generateParticipantInfo();
|
|
}
|
|
|
|
initializeApp() {
|
|
this.platform.ready().then(() => {
|
|
this.statusBar.overlaysWebView(false);
|
|
this.splashScreen.hide();
|
|
});
|
|
}
|
|
|
|
@HostListener('window:beforeunload')
|
|
beforeunloadHandler() {
|
|
// On window closed leave session
|
|
this.leaveSession();
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
// On component destroyed leave session
|
|
this.leaveSession();
|
|
}
|
|
|
|
async joinSession() {
|
|
// --- 1) Get an OpenVidu object ---
|
|
|
|
this.OV = new OpenVidu();
|
|
|
|
// --- 2) Init a session ---
|
|
|
|
this.session = this.OV.initSession();
|
|
|
|
// --- 3) Specify the actions when events take place in the session ---
|
|
|
|
// On every new Stream received...
|
|
this.session.on('streamCreated', (event: StreamEvent) => {
|
|
// Subscribe to the Stream to receive it. Second parameter is undefined
|
|
// so OpenVidu doesn't create an HTML video on its own
|
|
const subscriber: Subscriber = this.session.subscribe(event.stream, undefined);
|
|
this.subscribers.push(subscriber);
|
|
});
|
|
|
|
// On every Stream destroyed...
|
|
this.session.on('streamDestroyed', (event: StreamEvent) => {
|
|
// Remove the stream from 'subscribers' array
|
|
this.deleteSubscriber(event.stream.streamManager);
|
|
});
|
|
|
|
// On every asynchronous exception...
|
|
this.session.on('exception', (exception) => {
|
|
console.warn(exception);
|
|
});
|
|
|
|
// --- 4) Connect to the session with a valid user token ---
|
|
|
|
try {
|
|
// Get a token from the OpenVidu deployment
|
|
const token = await this.getToken();
|
|
// First param is the token got from OpenVidu deployment. Second param will be used by every user on event
|
|
// 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname
|
|
await this.session.connect(token, { clientData: this.myUserName });
|
|
|
|
// --- 5) Requesting and Checking Android Permissions
|
|
if (this.platform.is('cordova')) {
|
|
// Ionic platform
|
|
if (this.platform.is('android')) {
|
|
console.log('Android platform');
|
|
await this.checkAndroidPermissions();
|
|
this.initPublisher();
|
|
} else if (this.platform.is('ios')) {
|
|
console.log('iOS platform');
|
|
this.initPublisher();
|
|
}
|
|
} else {
|
|
this.initPublisher();
|
|
}
|
|
} catch (error) {
|
|
console.log('There was an error connecting to the session:', error.code, error.message);
|
|
}
|
|
}
|
|
|
|
async initPublisher() {
|
|
// Init a publisher passing undefined as targetElement (we don't want OpenVidu to insert a video
|
|
// element: we will manage it on our own) and with the desired properties
|
|
const publisher: Publisher = await this.OV.initPublisherAsync(undefined, {
|
|
audioSource: undefined, // The source of audio. If undefined default microphone
|
|
videoSource: undefined, // The source of video. If undefined default webcam
|
|
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
|
|
publishVideo: true, // Whether you want to start publishing with your video enabled or not
|
|
resolution: '640x480', // The resolution of your video
|
|
frameRate: 30, // The frame rate of your video
|
|
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
|
|
mirror: this.isFrontCamera // Whether to mirror your local video or not
|
|
});
|
|
|
|
publisher.on('accessAllowed', () => this.initDevices());
|
|
|
|
// --- 6) Publish your stream ---
|
|
|
|
await this.session.publish(publisher);
|
|
// Store our Publisher
|
|
this.publisher = publisher;
|
|
}
|
|
|
|
leaveSession() {
|
|
// --- 7) Leave the session by calling 'disconnect' method over the Session object ---
|
|
|
|
if (this.session) {
|
|
this.session.disconnect();
|
|
}
|
|
|
|
// Empty all properties...
|
|
this.subscribers = [];
|
|
delete this.publisher;
|
|
delete this.session;
|
|
delete this.OV;
|
|
this.generateParticipantInfo();
|
|
}
|
|
|
|
async swapCamera() {
|
|
try {
|
|
const newCamera = this.cameras.find(cam => cam.deviceId !== this.cameraSelected.deviceId);
|
|
if (!!newCamera) {
|
|
this.isFrontCamera = !this.isFrontCamera;
|
|
const pp: PublisherProperties = {
|
|
videoSource: newCamera.deviceId,
|
|
audioSource: false,
|
|
mirror: this.isFrontCamera
|
|
};
|
|
|
|
// Stopping the video tracks before request for another MediaStream
|
|
// Only one unique device can be used at same time
|
|
this.publisher.stream.getMediaStream().getVideoTracks()[0].stop();
|
|
const newTrack = await this.OV.getUserMedia(pp);
|
|
const videoTrack: MediaStreamTrack = newTrack.getVideoTracks()[0];
|
|
await (this.publisher as Publisher).replaceTrack(videoTrack);
|
|
this.cameraSelected = newCamera;
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
toggleCamera() {
|
|
const publish = !this.publisher.stream.videoActive;
|
|
(this.publisher as Publisher).publishVideo(publish, true);
|
|
this.cameraIcon = publish ? 'videocam' : 'eye-off';
|
|
}
|
|
|
|
toggleMicrophone() {
|
|
const publish = !this.publisher.stream.audioActive;
|
|
(this.publisher as Publisher).publishAudio(publish);
|
|
this.microphoneIcon = publish ? 'mic' : 'mic-off';
|
|
}
|
|
|
|
private async initDevices() {
|
|
this.devices = await this.OV.getDevices();
|
|
|
|
this.cameras = this.devices.filter(d => d.kind === 'videoinput');
|
|
this.microphones = this.devices.filter(d => d.kind === 'audioinput' && d.label !== 'Default');
|
|
|
|
this.cameraSelected = this.cameras[0];
|
|
this.microphoneSelected = this.microphones[0];
|
|
}
|
|
|
|
private async checkAndroidPermissions(): Promise<void> {
|
|
await this.platform.ready();
|
|
try {
|
|
await this.androidPermissions.requestPermissions(this.ANDROID_PERMISSIONS);
|
|
const camera = await this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.CAMERA);
|
|
const audio = await this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.RECORD_AUDIO);
|
|
const modifyAudio = await this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS);
|
|
|
|
if (!camera.hasPermission || !audio.hasPermission || !modifyAudio.hasPermission) {
|
|
throw new Error('Permissions denied: \n CAMERA = ' + camera.hasPermission +
|
|
'\n AUDIO = ' + audio.hasPermission +
|
|
'\n AUDIO_SETTINGS = ' + modifyAudio.hasPermission
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error requesting or checking permissions: ', error);
|
|
throw (error);
|
|
}
|
|
}
|
|
|
|
private generateParticipantInfo() {
|
|
// Random user nickname and sessionId
|
|
this.mySessionId = 'SessionA';
|
|
this.myUserName = 'Participant' + Math.floor(Math.random() * 100);
|
|
}
|
|
|
|
private deleteSubscriber(streamManager: StreamManager): void {
|
|
const index = this.subscribers.indexOf(streamManager, 0);
|
|
if (index > -1) {
|
|
this.subscribers.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
async presentSettingsAlert() {
|
|
const alert = await this.alertController.create({
|
|
header: 'OpenVidu deployment',
|
|
inputs: [
|
|
{
|
|
name: 'url',
|
|
type: 'text',
|
|
value: 'https://demos.openvidu.io/',
|
|
placeholder: 'URL',
|
|
}
|
|
],
|
|
buttons: [
|
|
{
|
|
text: 'Cancel',
|
|
role: 'cancel',
|
|
cssClass: 'secondary',
|
|
},
|
|
{
|
|
text: 'Ok',
|
|
handler: (data) => {
|
|
this.APPLICATION_SERVER_URL = data.url;
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await alert.present();
|
|
}
|
|
|
|
|
|
/**
|
|
* --------------------------------------------
|
|
* GETTING A TOKEN FROM YOUR APPLICATION SERVER
|
|
* --------------------------------------------
|
|
* The methods below request the creation of a Session and a Token to
|
|
* your application server. This keeps your OpenVidu deployment secure.
|
|
*
|
|
* In this sample code, there is no user control at all. Anybody could
|
|
* access your application server endpoints! In a real production
|
|
* environment, your application server must identify the user to allow
|
|
* access to the endpoints.
|
|
*
|
|
* Visit https://docs.openvidu.io/en/stable/application-server to learn
|
|
* more about the integration of OpenVidu in your application server.
|
|
*/
|
|
async getToken(): Promise<string> {
|
|
if (
|
|
this.platform.is('ios') &&
|
|
this.platform.is('cordova') &&
|
|
this.APPLICATION_SERVER_URL === 'http://localhost:5000/'
|
|
) {
|
|
// To make easier first steps with iOS apps, use demos OpenVidu deployment when no custom deployment is configured
|
|
this.APPLICATION_SERVER_URL = 'https://demos.openvidu.io/';
|
|
}
|
|
const sessionId = await this.createSession(this.mySessionId);
|
|
return await this.createToken(sessionId);
|
|
}
|
|
|
|
createSession(sessionId) {
|
|
return this.httpClient.post(
|
|
this.APPLICATION_SERVER_URL + 'api/sessions',
|
|
{ customSessionId: sessionId },
|
|
{ headers: { 'Content-Type': 'application/json' }, responseType: 'text' }
|
|
).toPromise();
|
|
}
|
|
|
|
createToken(sessionId) {
|
|
return this.httpClient.post(
|
|
this.APPLICATION_SERVER_URL + 'api/sessions/' + sessionId + '/connections',
|
|
{},
|
|
{ headers: { 'Content-Type': 'application/json' }, responseType: 'text' }
|
|
).toPromise();
|
|
}
|
|
}
|