I am using kurento-magic-mirror client sources to connect to kurento server.
I have migrated this source to ionic
import { Component, OnInit,ViewChild,ElementRef, HostListener, NgZone } from '@angular/core'
import { AuthServiceProvider } from 'src/app/providers/auth-service/auth-service'
import { v4 as uuidv4 } from 'uuid'
import { environment } from 'src/environments/environment'
import { WebRtcPeer, KurentoClient } from 'kurento-utils'
import { QueueingSubject } from 'queueing-subject'
import { Subscription, Observable, timer } from 'rxjs'
import { Timer } from 'interval-timer'
import { share, switchMap, retryWhen, delay, map } from 'rxjs/operators'
import { PushEvent, Status } from 'src/app/providers/common/common'
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
import makeWebSocketObservalbe, {
normalClosureMessage,
WebSocketOptions,
GetWebSocketResponses,
} from 'rxjs-websockets'
//https://github.com/webhacking/WebRTC-Example-With-Typescript/blob/aa5b76c054db9e099e24d4787cd01320de6ae916/src/main.ts
@Component({
selector: 'app-webrtc',
templateUrl: './webrtc.page.html',
styleUrls: ['./webrtc.page.scss'],
})
export class WebrtcPage implements OnInit {
isRecording = false
@ViewChild('videoInput',{read: ElementRef,static: true}) videoInput: ElementRef<HTMLElement>
@ViewChild('videoOutput',{read: ElementRef,static: true}) videoOutput: ElementRef<HTMLElement>
webRtcPeer: WebRtcPeer = null
input: QueueingSubject<string> = null
websocket: Observable<GetWebSocketResponses<string>> = null
messages: Observable<string | ArrayBuffer | Blob> = null
messagesSubscription: Subscription = null
isSubscribed: boolean = false
status: Status = Status.I_CAN_START
loading = null
I_CAN_START = 0
I_CAN_STOP = 1
I_AM_STARTING = 2
constructor(public auth: AuthServiceProvider, public androidPermissions: AndroidPermissions) {
}
ngOnInit() {
}
IonViewDidEnter() {
}
IonViewWillLeave() {
if( this.webRtcPeer ) {
this.stop()
}
if( this.isConnected() ) {
this.close()
}
}
isConnected(): boolean {
return this.messagesSubscription && !this.messagesSubscription.closed
}
send(message: any): void {
try {
this.input.next(JSON.stringify(message))
} catch(e) {
console.log('socket send error : ' + JSON.stringify(e))
this.closeWebsocket()
}
}
closeWebsocket() {
try { this.messagesSubscription.unsubscribe() } catch(e) {}
this.messagesSubscription = null
this.messages = null
this.websocket = null
try { this.input.unsubscribe() } catch(e) {}
this.input = null
this.isSubscribed = false
}
startResponse(message) {
this.status = Status.I_CAN_STOP
console.log("SDP answer received from server. Processing ...");
this.webRtcPeer.processAnswer(message.sdpAnswer, function(error) {
if (error)
return console.error(error);
});
}
connect() {
if( this.isConnected() ) {
this.closeWebsocket()
}
this.isSubscribed = false
// this.eventList = []
// this.eventList.unshift({path: 'SEND', message: PushEvent.CONNECT})
this.input = new QueueingSubject<string>()
this.websocket = makeWebSocketObservalbe(environment.wsHostUrl)
this.messages = this.websocket.pipe(
switchMap((getResponses: GetWebSocketResponses) => {
console.log('websocket opened')
return getResponses(this.input)
}),
retryWhen((errors) => {
errors.subscribe(sourceError => {
console.log(JSON.stringify(sourceError))
})
return Observable.create(obs => obs.eror(PushEvent.DISCONNECTED))
}),
share(),
)
this.messagesSubscription = this.messages.subscribe(
(message: string | ArrayBuffer | Blob ) => {
try {
let received: any = null
if( message instanceof ArrayBuffer ) {
received = JSON.parse(String.fromCharCode.apply(null, new Uint16Array(message)))
} else if( message instanceof Blob ) {
throw new Error('Blob message is not allowed')
} else {
console.log('received message:', message)
received = JSON.parse(message)
}
var parsedMessage = received.data
console.info('Received message: ' + parsedMessage)
switch (parsedMessage.id) {
case 'startResponse':
this.startResponse(parsedMessage);
break;
case 'error':
if (this.status === Status.I_AM_STARTING) {
this.status = Status.I_CAN_START
}
console.log("Error message from server: " + parsedMessage.message);
break;
case 'iceCandidate':
this.webRtcPeer.addIceCandidate(parsedMessage.candidate, function (error) {
if (error) {
console.error("Error adding candidate: " + error);
return;
}
});
break;
default:
if (this.status === Status.I_AM_STARTING) {
this.status = Status.I_CAN_START
}
console.log('Unrecognized message', parsedMessage);
}
} catch(e) {
console.log(JSON.stringify(e))
}
},
(error: Error) => {
const { message } = error
if (message === normalClosureMessage) {
// this.eventList.unshift({path: 'RECV', message: PushEvent.UNSUBSCRBE})
this.closeWebsocket()
} else {
// this.eventList.unshift({path: 'RECV', message: PushEvent.UNSUBSCRBE})
console.log('socket was disconnected due to error: ', message)
this.closeWebsocket()
}
},
() => {
console.log('the connection was closed in response to the user')
// this.eventList.unshift({path: 'RECV', message: PushEvent.CLOSED})
this.closeWebsocket()
}
)
}
close() {
try {
if( this.isSubscribed ) {
//this.unsubscribe()
}
const localTimer = new Timer({
startTime: 300,
endTime: null,
updateFrequency: null,
selfAdjust: true,
countdown: false,
animationFrame: false
})
localTimer.on('start', () => {
// this.eventList.unshift({path: 'RECV', message: PushEvent.CLOSED})
this.closeWebsocket()
})
localTimer.start()
} catch(e) {}
}
onOffer(offerSdp) {
console.info('Invoking SDP offer callback function ' + location.host);
var message = {
id : 'start',
sdpOffer : offerSdp
}
this.send(message);
}
onIceCandidate(candidate) {
console.log("Local candidate" + JSON.stringify(candidate));
var message = {
id: 'onIceCandidate',
candidate: candidate
};
this.send(message);
}
async startAfterCheckPermission() {
var list = [
this.androidPermissions.PERMISSION.CAMERA,
this.androidPermissions.PERMISSION.MODIFY_AUDIO_SETTINGS,
this.androidPermissions.PERMISSION.RECORD_AUDIO,
this.androidPermissions.PERMISSION.INTERNET,
this.androidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE,
]
this.androidPermissions.requestPermissions(list).then( result => {
if( result.hasPermission )
this.start()
else
this.auth.presentAlert('Media Device Permission Not Granted')
}, err => {
this.auth.showError(err)
})
}
async start() {
console.log("Starting video call ...")
this.status = Status.I_AM_STARTING
this.loading = await this.auth.showLoading()
console.log("Creating WebRtcPeer and generating local sdp offer ...");
var constraints = {
audio: true,
video: {
width: 640,
framerate: 15
}
}
var options = {
audio: true,
localVideo: this.videoInput,
remoteVideo: this.videoOutput,
onicecandidate: this.onIceCandidate,
mediaConstraints: constraints,
}
this.webRtcPeer = new WebRtcPeer.WebRtcPeerSendrecv(options,
function (error) {
if (error) {
return console.error(error);
}
this.webRtcPeer.generateOffer(this.onOffer);
});
if( ! this.websocket ) {
this.auth.presentAlert('Cannot create webRtc adapter')
this.loading.dismiss()
} else {
if( !this.isConnected() ) {
this.connect()
}
}
}
stop() {
console.log("Stopping video call ...");
this.status = Status.I_CAN_START
if (this.webRtcPeer) {
this.webRtcPeer.dispose();
this.webRtcPeer = null;
var message = {
id : 'stop'
}
this.send(message);
}
this.loading.dismiss()
}
}
But this.webRtcPeer = new WebRtcPeer.WebRtcPeerSendrecv() returns null: So I have got error as followings .
2020-04-26 15:46:28.777 10434-10716/io.bory.speechmate E/cr_VideoCapture: CameraDevice.StateCallback onOpened
2020-04-26 15:46:28.778 10434-10716/io.bory.speechmate V/Surface: sf_framedrop debug : 0x4f4c, game : false, logging : 0
2020-04-26 15:46:28.782 10434-10716/io.bory.speechmate I/CameraManager: getCameraCharacteristics : cameraId = 1
2020-04-26 15:46:28.785 10434-10716/io.bory.speechmate I/CameraManager: getCameraCharacteristics : cameraId = 1
2020-04-26 15:46:28.821 3647-3788/? D/MdnieScenarioControlService: packageName : io.bory.speechmate className : io.bory.speechmate.MainActivity
2020-04-26 15:46:28.887 10434-10434/io.bory.speechmate E/chromium: [ERROR:web_contents_delegate.cc(218)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.
2020-04-26 15:46:28.905 10434-10434/io.bory.speechmate E/Capacitor/Console: File: http://localhost/93-es2015.7ef3d9410b63064250ff.js - Line 1 - Msg: TypeError: Cannot read property 'generateOffer' of undefined