ionic-cordova: Added mute and toggle media devices
This commit is contained in:
parent
cc87de9541
commit
e4cf86bc22
@ -26,7 +26,7 @@
|
||||
"@ionic/angular": "6.1.11",
|
||||
"@ionic/cordova-builders": "^6.1.0",
|
||||
"compare-func": "2.0.0",
|
||||
"cordova-android": "10.1.2",
|
||||
"cordova-android": "11.0.0",
|
||||
"cordova-ios": "6.2.0",
|
||||
"cordova-plugin-android-permissions": "1.1.3",
|
||||
"cordova-plugin-device": "2.1.0",
|
||||
|
||||
@ -23,4 +23,8 @@
|
||||
|
||||
ion-col {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
padding: 5px;
|
||||
}
|
||||
@ -1,72 +1,103 @@
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar color="dark">
|
||||
<img
|
||||
class="demo-logo"
|
||||
src="assets/images/openvidu_vert_white_bg_trans_cropped.png"
|
||||
/>
|
||||
<ion-buttons slot="primary">
|
||||
<ion-button
|
||||
color="light"
|
||||
href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-ionic"
|
||||
>
|
||||
<ion-icon slot="icon-only" name="logo-github"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar color="dark">
|
||||
<img class="demo-logo" src="assets/images/openvidu_vert_white_bg_trans_cropped.png" />
|
||||
<ion-buttons slot="primary">
|
||||
<ion-button color="light" href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-ionic">
|
||||
<ion-icon slot="icon-only" name="logo-github"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding" *ngIf="!session">
|
||||
<div id="img-div">
|
||||
<img src="assets/images/openvidu_grey_bg_transp_cropped.png" />
|
||||
</div>
|
||||
<h1 align="center" id="title">Join a video session</h1>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Participant</ion-label>
|
||||
<ion-input [(ngModel)]="myUserName"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Session</ion-label>
|
||||
<ion-input [(ngModel)]="mySessionId"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-content class="ion-padding" *ngIf="!session">
|
||||
<ion-button
|
||||
id="join-button"
|
||||
[disabled]="!mySessionId && !myUserName"
|
||||
(click)="joinSession()"
|
||||
expand="block"
|
||||
shape="round"
|
||||
color="primary"
|
||||
>
|
||||
<ion-icon slot="start" name="videocam"></ion-icon>
|
||||
Join
|
||||
</ion-button>
|
||||
|
||||
<div id="img-div"><img src="assets/images/openvidu_grey_bg_transp_cropped.png" /></div>
|
||||
<h1 align="center" id="title">Join a video session</h1>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Participant</ion-label>
|
||||
<ion-input [(ngModel)]="myUserName"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Session</ion-label>
|
||||
<ion-input [(ngModel)]="mySessionId"></ion-input>
|
||||
</ion-item>
|
||||
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
|
||||
<ion-fab-button
|
||||
id="settings-button"
|
||||
[disabled]="!mySessionId && !myUserName"
|
||||
(click)="presentSettingsAlert()"
|
||||
size="small"
|
||||
color="dark"
|
||||
>
|
||||
<ion-icon name="settings"></ion-icon>
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
</ion-content>
|
||||
|
||||
<ion-button id="join-button" [disabled]="!mySessionId && !myUserName" (click)="joinSession()" expand="block" shape="round"
|
||||
color="primary">
|
||||
<ion-icon slot="start" name="videocam"></ion-icon>
|
||||
Join
|
||||
</ion-button>
|
||||
<ion-content *ngIf="session">
|
||||
<div id="session-header">
|
||||
<h1 id="session-title">{{ mySessionId }}</h1>
|
||||
</div>
|
||||
|
||||
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
|
||||
<ion-fab-button id="settings-button" [disabled]="!mySessionId && !myUserName" (click)="presentSettingsAlert()"
|
||||
size="small" color="dark">
|
||||
<ion-icon name="settings"></ion-icon>
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
<ion-grid>
|
||||
<!--Subscribers and Publishers-->
|
||||
<ion-row>
|
||||
<ion-col size="6">
|
||||
<div *ngIf="publisher" class="stream-container">
|
||||
<user-video [streamManager]="publisher"></user-video>
|
||||
</div>
|
||||
</ion-col>
|
||||
|
||||
</ion-content>
|
||||
<ion-col size="6" *ngFor="let sub of subscribers">
|
||||
<div class="stream-container">
|
||||
<user-video [streamManager]="sub"></user-video>
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
<ion-content *ngIf="session">
|
||||
<div id="session-header">
|
||||
<h1 id="session-title">{{mySessionId}}</h1>
|
||||
</div>
|
||||
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
|
||||
<ion-fab-button
|
||||
class="action-button"
|
||||
color="light"
|
||||
id="camButton"
|
||||
(click)="swapCamera()"
|
||||
>
|
||||
<ion-icon name="camera-reverse-sharp"></ion-icon>
|
||||
</ion-fab-button>
|
||||
|
||||
<ion-grid>
|
||||
<!--Subscribers and Publishers-->
|
||||
<ion-row>
|
||||
<ion-col size="6">
|
||||
<div *ngIf="publisher" class="stream-container">
|
||||
<user-video [streamManager]="publisher"></user-video>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-fab-button (click)="toggleMicrophone()" class="action-button">
|
||||
<ion-icon name="{{microphoneIcon}}"></ion-icon>
|
||||
</ion-fab-button>
|
||||
|
||||
<ion-col size="6" *ngFor="let sub of subscribers">
|
||||
<div class="stream-container">
|
||||
<user-video [streamManager]="sub"></user-video>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-fab-button (click)="toggleCamera()" class="action-button">
|
||||
<ion-icon name="{{cameraIcon}}"></ion-icon>
|
||||
</ion-fab-button>
|
||||
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
<ion-fab vertical="bottom" horizontal="center" slot="fixed">
|
||||
<ion-fab-button size="small" color="danger" (click)="leaveSession()">
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
</ion-content>
|
||||
|
||||
</ion-app>
|
||||
<ion-fab-button color="danger" (click)="leaveSession()" class="action-button">
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</ion-fab-button>
|
||||
</ion-fab>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
@ -1,363 +1,472 @@
|
||||
import { HttpClient, HttpHeaders } 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 { Platform, AlertController } from '@ionic/angular';
|
||||
import { OpenVidu, Publisher, Session, StreamEvent, StreamManager, Subscriber } from 'openvidu-browser';
|
||||
import { throwError as observableThrowError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { HttpClient, HttpHeaders } 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 { Platform, AlertController } from "@ionic/angular";
|
||||
import {
|
||||
Device,
|
||||
OpenVidu,
|
||||
Publisher,
|
||||
PublisherProperties,
|
||||
Session,
|
||||
StreamEvent,
|
||||
StreamManager,
|
||||
Subscriber,
|
||||
} from "openvidu-browser";
|
||||
import { throwError as observableThrowError } from "rxjs";
|
||||
import { catchError } from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.css'],
|
||||
selector: "app-root",
|
||||
templateUrl: "app.component.html",
|
||||
styleUrls: ["app.component.css"],
|
||||
})
|
||||
export class AppComponent implements OnDestroy {
|
||||
OPENVIDU_SERVER_URL = "https://" + location.hostname + ":4443";
|
||||
OPENVIDU_SERVER_SECRET = "MY_SECRET";
|
||||
|
||||
OPENVIDU_SERVER_URL = 'https://' + location.hostname + ':4443';
|
||||
OPENVIDU_SERVER_SECRET = 'MY_SECRET';
|
||||
ANDROID_PERMISSIONS = [
|
||||
this.androidPermissions.PERMISSION.CAMERA,
|
||||
this.androidPermissions.PERMISSION.RECORD_AUDIO,
|
||||
this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS,
|
||||
];
|
||||
|
||||
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
|
||||
|
||||
// OpenVidu objects
|
||||
OV: OpenVidu;
|
||||
session: Session;
|
||||
publisher: StreamManager; // Local
|
||||
subscribers: StreamManager[] = []; // Remotes
|
||||
// Join form
|
||||
mySessionId: string;
|
||||
myUserName: string;
|
||||
|
||||
// Join form
|
||||
mySessionId: string;
|
||||
myUserName: string;
|
||||
cameraIcon = "videocam";
|
||||
microphoneIcon = "mic";
|
||||
|
||||
constructor(
|
||||
private platform: Platform,
|
||||
private splashScreen: SplashScreen,
|
||||
private statusBar: StatusBar,
|
||||
private httpClient: HttpClient,
|
||||
private androidPermissions: AndroidPermissions,
|
||||
public alertController: AlertController
|
||||
) {
|
||||
this.initializeApp();
|
||||
this.generateParticipantInfo();
|
||||
}
|
||||
private devices: Device[];
|
||||
private cameras: Device[];
|
||||
private microphones: Device[];
|
||||
private cameraSelected: Device;
|
||||
private microphoneSelected: Device;
|
||||
private isFrontCamera: boolean = false;
|
||||
|
||||
initializeApp() {
|
||||
this.platform.ready().then(() => {
|
||||
this.statusBar.overlaysWebView(false);
|
||||
this.splashScreen.hide();
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
private platform: Platform,
|
||||
private splashScreen: SplashScreen,
|
||||
private statusBar: StatusBar,
|
||||
private httpClient: HttpClient,
|
||||
private androidPermissions: AndroidPermissions,
|
||||
public alertController: AlertController
|
||||
) {
|
||||
this.initializeApp();
|
||||
this.generateParticipantInfo();
|
||||
}
|
||||
|
||||
@HostListener('window:beforeunload')
|
||||
beforeunloadHandler() {
|
||||
// On window closed leave session
|
||||
this.leaveSession();
|
||||
}
|
||||
initializeApp() {
|
||||
this.platform.ready().then(() => {
|
||||
this.statusBar.overlaysWebView(false);
|
||||
this.splashScreen.hide();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// On component destroyed leave session
|
||||
this.leaveSession();
|
||||
}
|
||||
@HostListener("window:beforeunload")
|
||||
beforeunloadHandler() {
|
||||
// On window closed leave session
|
||||
this.leaveSession();
|
||||
}
|
||||
|
||||
joinSession() {
|
||||
// --- 1) Get an OpenVidu object ---
|
||||
ngOnDestroy() {
|
||||
// On component destroyed leave session
|
||||
this.leaveSession();
|
||||
}
|
||||
|
||||
this.OV = new OpenVidu();
|
||||
async joinSession() {
|
||||
// --- 1) Get an OpenVidu object ---
|
||||
|
||||
// --- 2) Init a session ---
|
||||
this.OV = new OpenVidu();
|
||||
|
||||
this.session = this.OV.initSession();
|
||||
this.initDevices();
|
||||
|
||||
// --- 3) Specify the actions when events take place in the session ---
|
||||
// --- 2) Init a 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);
|
||||
});
|
||||
this.session = this.OV.initSession();
|
||||
|
||||
// On every Stream destroyed...
|
||||
this.session.on('streamDestroyed', (event: StreamEvent) => {
|
||||
// Remove the stream from 'subscribers' array
|
||||
this.deleteSubscriber(event.stream.streamManager);
|
||||
});
|
||||
// --- 3) Specify the actions when events take place in the session ---
|
||||
|
||||
// On every asynchronous exception...
|
||||
this.session.on('exception', (exception) => {
|
||||
console.warn(exception);
|
||||
});
|
||||
// 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);
|
||||
});
|
||||
|
||||
// --- 4) Connect to the session with a valid user token ---
|
||||
// On every Stream destroyed...
|
||||
this.session.on("streamDestroyed", (event: StreamEvent) => {
|
||||
// Remove the stream from 'subscribers' array
|
||||
this.deleteSubscriber(event.stream.streamManager);
|
||||
});
|
||||
|
||||
// 'getToken' method is simulating what your server-side should do.
|
||||
// 'token' parameter should be retrieved and returned by your own backend
|
||||
this.getToken().then((token) => {
|
||||
// First param is the token got from OpenVidu Server. 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
|
||||
this.session
|
||||
.connect(token, { clientData: this.myUserName })
|
||||
.then(() => {
|
||||
// --- 5) Requesting and Checking Android Permissions
|
||||
if (this.platform.is('cordova')) {
|
||||
// Ionic platform
|
||||
if (this.platform.is('android')) {
|
||||
console.log('Android platform');
|
||||
this.checkAndroidPermissions()
|
||||
.then(() => this.initPublisher())
|
||||
.catch(err => console.error(err));
|
||||
} 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
// On every asynchronous exception...
|
||||
this.session.on("exception", (exception) => {
|
||||
console.warn(exception);
|
||||
});
|
||||
|
||||
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 = this.OV.initPublisher(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: true // Whether to mirror your local video or not
|
||||
});
|
||||
// --- 4) Connect to the session with a valid user token ---
|
||||
|
||||
// --- 6) Publish your stream ---
|
||||
try {
|
||||
// 'getToken' method is simulating what your server-side should do.
|
||||
// 'token' parameter should be retrieved and returned by your own backend
|
||||
const token = await this.getToken();
|
||||
// First param is the token got from OpenVidu Server. 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 });
|
||||
|
||||
this.session.publish(publisher).then(() => {
|
||||
// Store our Publisher
|
||||
this.publisher = publisher;
|
||||
});
|
||||
}
|
||||
// --- 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
leaveSession() {
|
||||
// --- 7) Leave the session by calling 'disconnect' method over the Session object ---
|
||||
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: this.microphones[0].deviceId, // The source of audio. If undefined default microphone
|
||||
videoSource: this.cameras[0].deviceId, // 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
|
||||
});
|
||||
|
||||
if (this.session) {
|
||||
this.session.disconnect();
|
||||
}
|
||||
// --- 6) Publish your stream ---
|
||||
|
||||
// Empty all properties...
|
||||
this.subscribers = [];
|
||||
delete this.publisher;
|
||||
delete this.session;
|
||||
delete this.OV;
|
||||
this.generateParticipantInfo();
|
||||
}
|
||||
await this.session.publish(publisher);
|
||||
// Store our Publisher
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
private checkAndroidPermissions(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.platform.ready().then(() => {
|
||||
this.androidPermissions
|
||||
.requestPermissions(this.ANDROID_PERMISSIONS)
|
||||
.then(() => {
|
||||
this.androidPermissions
|
||||
.checkPermission(this.androidPermissions.PERMISSION.CAMERA)
|
||||
.then(camera => {
|
||||
this.androidPermissions
|
||||
.checkPermission(this.androidPermissions.PERMISSION.RECORD_AUDIO)
|
||||
.then(audio => {
|
||||
this.androidPermissions
|
||||
.checkPermission(this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS)
|
||||
.then(modifyAudio => {
|
||||
if (camera.hasPermission && audio.hasPermission && modifyAudio.hasPermission) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
'Permissions denied: ' +
|
||||
'\n' +
|
||||
' CAMERA = ' +
|
||||
camera.hasPermission +
|
||||
'\n' +
|
||||
' AUDIO = ' +
|
||||
audio.hasPermission +
|
||||
'\n' +
|
||||
' AUDIO_SETTINGS = ' +
|
||||
modifyAudio.hasPermission,
|
||||
),
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(
|
||||
'Checking permission ' +
|
||||
this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS +
|
||||
' failed',
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(
|
||||
'Checking permission ' + this.androidPermissions.PERMISSION.RECORD_AUDIO + ' failed',
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Checking permission ' + this.androidPermissions.PERMISSION.CAMERA + ' failed');
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(err => console.error('Error requesting permissions: ', err));
|
||||
});
|
||||
});
|
||||
}
|
||||
leaveSession() {
|
||||
// --- 7) Leave the session by calling 'disconnect' method over the Session object ---
|
||||
|
||||
private generateParticipantInfo() {
|
||||
// Random user nickname and sessionId
|
||||
this.mySessionId = 'SessionA';
|
||||
this.myUserName = 'Participant' + Math.floor(Math.random() * 100);
|
||||
}
|
||||
if (this.session) {
|
||||
this.session.disconnect();
|
||||
}
|
||||
|
||||
private deleteSubscriber(streamManager: StreamManager): void {
|
||||
const index = this.subscribers.indexOf(streamManager, 0);
|
||||
if (index > -1) {
|
||||
this.subscribers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
// Empty all properties...
|
||||
this.subscribers = [];
|
||||
delete this.publisher;
|
||||
delete this.session;
|
||||
delete this.OV;
|
||||
this.generateParticipantInfo();
|
||||
}
|
||||
|
||||
async presentSettingsAlert() {
|
||||
const alert = await this.alertController.create({
|
||||
header: 'OpenVidu Server config',
|
||||
inputs: [
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
value: 'https://demos.openvidu.io',
|
||||
placeholder: 'URL'
|
||||
},
|
||||
{
|
||||
name: 'secret',
|
||||
type: 'text',
|
||||
value: 'MY_SECRET',
|
||||
placeholder: 'Secret'
|
||||
}
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
role: 'cancel',
|
||||
cssClass: 'secondary'
|
||||
}, {
|
||||
text: 'Ok',
|
||||
handler: data => {
|
||||
this.OPENVIDU_SERVER_URL = data.url;
|
||||
this.OPENVIDU_SERVER_SECRET = data.secret;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
async swapCamera() {
|
||||
try {
|
||||
const newCamera = this.cameras.find(
|
||||
(cam) => cam.deviceId !== this.cameraSelected.deviceId
|
||||
);
|
||||
if (!!newCamera) {
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
this.isFrontCamera = !this.isFrontCamera;
|
||||
const pp: PublisherProperties = {
|
||||
videoSource: newCamera.deviceId,
|
||||
audioSource: false,
|
||||
mirror: this.isFrontCamera
|
||||
};
|
||||
|
||||
/*
|
||||
* --------------------------
|
||||
* SERVER-SIDE RESPONSIBILITY
|
||||
* --------------------------
|
||||
* This method retrieve the mandatory user token from OpenVidu Server,
|
||||
* in this case making use Angular http API.
|
||||
* This behaviour MUST BE IN YOUR SERVER-SIDE IN PRODUCTION. In this case:
|
||||
* 1) Initialize a Session in OpenVidu Server (POST /openvidu/api/sessions)
|
||||
* 2) Create a Connection in OpenVidu Server (POST /openvidu/api/sessions/<SESSION_ID>/connection)
|
||||
* 3) The Connection.token must be consumed in Session.connect() method
|
||||
*/
|
||||
// Stopping the video tracks after request for another MediaStream
|
||||
this.publisher.stream.getMediaStream().getVideoTracks().forEach((track) => {
|
||||
track.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";
|
||||
}
|
||||
|
||||
getToken(): Promise<string> {
|
||||
if (this.platform.is('ios') && this.platform.is('cordova') && this.OPENVIDU_SERVER_URL === 'https://localhost:4443') {
|
||||
// To make easier first steps with iOS apps, use demos OpenVidu Sever if no custom valid server is configured
|
||||
this.OPENVIDU_SERVER_URL = 'https://demos.openvidu.io';
|
||||
}
|
||||
return this.createSession(this.mySessionId).then((sessionId) => {
|
||||
return this.createToken(sessionId);
|
||||
});
|
||||
}
|
||||
toggleMicrophone() {
|
||||
const publish = !this.publisher.stream.audioActive;
|
||||
(this.publisher as Publisher).publishAudio(publish);
|
||||
this.microphoneIcon = publish ? "mic" : "mic-off";
|
||||
}
|
||||
|
||||
createSession(sessionId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify({ customSessionId: sessionId });
|
||||
const options = {
|
||||
headers: new HttpHeaders({
|
||||
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + this.OPENVIDU_SERVER_SECRET),
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
};
|
||||
return this.httpClient
|
||||
.post(this.OPENVIDU_SERVER_URL + '/openvidu/api/sessions', body, options)
|
||||
.pipe(
|
||||
catchError((error) => {
|
||||
if (error.status === 409) {
|
||||
resolve(sessionId);
|
||||
} else {
|
||||
console.warn(
|
||||
'No connection to OpenVidu Server. This may be a certificate error at ' +
|
||||
this.OPENVIDU_SERVER_URL,
|
||||
);
|
||||
if (
|
||||
window.confirm(
|
||||
'No connection to OpenVidu Server. This may be a certificate error at "' +
|
||||
this.OPENVIDU_SERVER_URL +
|
||||
// tslint:disable-next-line:max-line-length
|
||||
'"\n\nClick OK to navigate and accept it. If no certificate warning is shown, then check that your OpenVidu Server' +
|
||||
'is up and running at "' +
|
||||
this.OPENVIDU_SERVER_URL +
|
||||
'"',
|
||||
)
|
||||
) {
|
||||
location.assign(this.OPENVIDU_SERVER_URL + '/accept-certificate');
|
||||
}
|
||||
}
|
||||
return observableThrowError(error);
|
||||
}),
|
||||
)
|
||||
.subscribe((response) => {
|
||||
console.log(response);
|
||||
resolve(response['id']);
|
||||
});
|
||||
});
|
||||
}
|
||||
private async initDevices() {
|
||||
this.devices = await this.OV.getDevices();
|
||||
|
||||
createToken(sessionId): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify({});
|
||||
const options = {
|
||||
headers: new HttpHeaders({
|
||||
Authorization: 'Basic ' + btoa('OPENVIDUAPP:' + this.OPENVIDU_SERVER_SECRET),
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
};
|
||||
return this.httpClient
|
||||
.post(this.OPENVIDU_SERVER_URL + '/openvidu/api/sessions/' + sessionId + '/connection', body, options)
|
||||
.pipe(
|
||||
catchError((error) => {
|
||||
reject(error);
|
||||
return observableThrowError(error);
|
||||
}),
|
||||
)
|
||||
.subscribe((response) => {
|
||||
console.log(response);
|
||||
resolve(response['token']);
|
||||
});
|
||||
});
|
||||
}
|
||||
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 checkAndroidPermissions(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.platform.ready().then(() => {
|
||||
this.androidPermissions
|
||||
.requestPermissions(this.ANDROID_PERMISSIONS)
|
||||
.then(() => {
|
||||
this.androidPermissions
|
||||
.checkPermission(this.androidPermissions.PERMISSION.CAMERA)
|
||||
.then((camera) => {
|
||||
this.androidPermissions
|
||||
.checkPermission(
|
||||
this.androidPermissions.PERMISSION.RECORD_AUDIO
|
||||
)
|
||||
.then((audio) => {
|
||||
this.androidPermissions
|
||||
.checkPermission(
|
||||
this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS
|
||||
)
|
||||
.then((modifyAudio) => {
|
||||
if (
|
||||
camera.hasPermission &&
|
||||
audio.hasPermission &&
|
||||
modifyAudio.hasPermission
|
||||
) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(
|
||||
new Error(
|
||||
"Permissions denied: " +
|
||||
"\n" +
|
||||
" CAMERA = " +
|
||||
camera.hasPermission +
|
||||
"\n" +
|
||||
" AUDIO = " +
|
||||
audio.hasPermission +
|
||||
"\n" +
|
||||
" AUDIO_SETTINGS = " +
|
||||
modifyAudio.hasPermission
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(
|
||||
"Checking permission " +
|
||||
this.androidPermissions.PERMISSION
|
||||
.MODIFY_AUDIO_SETTINGS +
|
||||
" failed"
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(
|
||||
"Checking permission " +
|
||||
this.androidPermissions.PERMISSION.RECORD_AUDIO +
|
||||
" failed"
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(
|
||||
"Checking permission " +
|
||||
this.androidPermissions.PERMISSION.CAMERA +
|
||||
" failed"
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => console.error("Error requesting permissions: ", err));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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 Server config",
|
||||
inputs: [
|
||||
{
|
||||
name: "url",
|
||||
type: "text",
|
||||
value: "https://demos.openvidu.io",
|
||||
placeholder: "URL",
|
||||
},
|
||||
{
|
||||
name: "secret",
|
||||
type: "text",
|
||||
value: "MY_SECRET",
|
||||
placeholder: "Secret",
|
||||
},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
text: "Cancel",
|
||||
role: "cancel",
|
||||
cssClass: "secondary",
|
||||
},
|
||||
{
|
||||
text: "Ok",
|
||||
handler: (data) => {
|
||||
this.OPENVIDU_SERVER_URL = data.url;
|
||||
this.OPENVIDU_SERVER_SECRET = data.secret;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
/*
|
||||
* --------------------------
|
||||
* SERVER-SIDE RESPONSIBILITY
|
||||
* --------------------------
|
||||
* This method retrieve the mandatory user token from OpenVidu Server,
|
||||
* in this case making use Angular http API.
|
||||
* This behaviour MUST BE IN YOUR SERVER-SIDE IN PRODUCTION. In this case:
|
||||
* 1) Initialize a Session in OpenVidu Server (POST /openvidu/api/sessions)
|
||||
* 2) Create a Connection in OpenVidu Server (POST /openvidu/api/sessions/<SESSION_ID>/connection)
|
||||
* 3) The Connection.token must be consumed in Session.connect() method
|
||||
*/
|
||||
|
||||
getToken(): Promise<string> {
|
||||
if (
|
||||
this.platform.is("ios") &&
|
||||
this.platform.is("cordova") &&
|
||||
this.OPENVIDU_SERVER_URL === "https://localhost:4443"
|
||||
) {
|
||||
// To make easier first steps with iOS apps, use demos OpenVidu Sever if no custom valid server is configured
|
||||
this.OPENVIDU_SERVER_URL = "https://demos.openvidu.io";
|
||||
}
|
||||
return this.createSession(this.mySessionId).then((sessionId) => {
|
||||
return this.createToken(sessionId);
|
||||
});
|
||||
}
|
||||
|
||||
createSession(sessionId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify({ customSessionId: sessionId });
|
||||
const options = {
|
||||
headers: new HttpHeaders({
|
||||
Authorization:
|
||||
"Basic " + btoa("OPENVIDUAPP:" + this.OPENVIDU_SERVER_SECRET),
|
||||
"Content-Type": "application/json",
|
||||
}),
|
||||
};
|
||||
return this.httpClient
|
||||
.post(
|
||||
this.OPENVIDU_SERVER_URL + "/openvidu/api/sessions",
|
||||
body,
|
||||
options
|
||||
)
|
||||
.pipe(
|
||||
catchError((error) => {
|
||||
if (error.status === 409) {
|
||||
resolve(sessionId);
|
||||
} else {
|
||||
console.warn(
|
||||
"No connection to OpenVidu Server. This may be a certificate error at " +
|
||||
this.OPENVIDU_SERVER_URL
|
||||
);
|
||||
if (
|
||||
window.confirm(
|
||||
'No connection to OpenVidu Server. This may be a certificate error at "' +
|
||||
this.OPENVIDU_SERVER_URL +
|
||||
// tslint:disable-next-line:max-line-length
|
||||
'"\n\nClick OK to navigate and accept it. If no certificate warning is shown, then check that your OpenVidu Server' +
|
||||
'is up and running at "' +
|
||||
this.OPENVIDU_SERVER_URL +
|
||||
'"'
|
||||
)
|
||||
) {
|
||||
location.assign(
|
||||
this.OPENVIDU_SERVER_URL + "/accept-certificate"
|
||||
);
|
||||
}
|
||||
}
|
||||
return observableThrowError(error);
|
||||
})
|
||||
)
|
||||
.subscribe((response) => {
|
||||
console.log(response);
|
||||
resolve(response["id"]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createToken(sessionId): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify({});
|
||||
const options = {
|
||||
headers: new HttpHeaders({
|
||||
Authorization:
|
||||
"Basic " + btoa("OPENVIDUAPP:" + this.OPENVIDU_SERVER_SECRET),
|
||||
"Content-Type": "application/json",
|
||||
}),
|
||||
};
|
||||
return this.httpClient
|
||||
.post(
|
||||
this.OPENVIDU_SERVER_URL +
|
||||
"/openvidu/api/sessions/" +
|
||||
sessionId +
|
||||
"/connection",
|
||||
body,
|
||||
options
|
||||
)
|
||||
.pipe(
|
||||
catchError((error) => {
|
||||
reject(error);
|
||||
return observableThrowError(error);
|
||||
})
|
||||
)
|
||||
.subscribe((response) => {
|
||||
console.log(response);
|
||||
resolve(response["token"]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user