2016-10-15 00:11:17 +02:00
|
|
|
/*
|
|
|
|
* options: name: XXX data: true (Maybe this is based on webrtc) audio: true,
|
|
|
|
* video: true, url: "file:///..." > Player screen: true > Desktop (implicit
|
|
|
|
* video:true, audio:false) audio: true, video: true > Webcam
|
|
|
|
*
|
|
|
|
* stream.hasAudio(); stream.hasVideo(); stream.hasData();
|
|
|
|
*/
|
|
|
|
import { Participant } from './Participant';
|
2016-10-15 02:08:23 +02:00
|
|
|
import { Session } from './Session';
|
|
|
|
import { OpenVidu, Callback } from './OpenVidu';
|
2016-10-16 23:16:58 +02:00
|
|
|
import EventEmitter = require('wolfy87-eventemitter');
|
|
|
|
import * as kurentoUtils from 'kurento-utils';
|
|
|
|
|
|
|
|
import 'webrtc-adapter';
|
|
|
|
declare var navigator: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
declare var RTCSessionDescription: any;
|
|
|
|
|
2016-10-16 23:16:58 +02:00
|
|
|
function jq(id: string):string {
|
2016-10-16 23:51:41 +02:00
|
|
|
return id.replace(/(@|:|\.|\[|\]|,)/g, "\\$1");
|
2016-10-16 23:16:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function show(id: string){
|
|
|
|
document.getElementById(jq(id))!.style.display = 'block';
|
|
|
|
}
|
|
|
|
|
|
|
|
function hide(id: string){
|
|
|
|
document.getElementById(jq(id))!.style.display = 'none';
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface StreamOptions {
|
|
|
|
id: string;
|
|
|
|
participant: Participant;
|
|
|
|
recvVideo: any;
|
|
|
|
recvAudio: any;
|
|
|
|
video: boolean;
|
|
|
|
audio: boolean;
|
|
|
|
data: boolean;
|
2017-03-09 19:46:07 +01:00
|
|
|
mediaConstraints: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface VideoOptions {
|
|
|
|
thumb: string;
|
|
|
|
video: HTMLVideoElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Stream {
|
|
|
|
|
|
|
|
private ee = new EventEmitter();
|
|
|
|
private wrStream: any;
|
|
|
|
private wp: any;
|
|
|
|
private id: string;
|
|
|
|
private video: HTMLVideoElement;
|
|
|
|
private videoElements: VideoOptions[] = [];
|
|
|
|
private elements: HTMLDivElement[] = [];
|
|
|
|
private participant: Participant;
|
|
|
|
private speechEvent: any;
|
|
|
|
private recvVideo: any;
|
|
|
|
private recvAudio: any;
|
2017-03-09 19:46:07 +01:00
|
|
|
private sendVideo: boolean;
|
|
|
|
private sendAudio: boolean;
|
|
|
|
private mediaConstraints: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
private showMyRemote = false;
|
|
|
|
private localMirrored = false;
|
|
|
|
private chanId = 0;
|
|
|
|
private dataChannel: boolean;
|
|
|
|
private dataChannelOpened = false;
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
constructor( private openVidu: OpenVidu, private local: boolean, private room: Session, options: StreamOptions ) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
if ( options.id ) {
|
|
|
|
this.id = options.id;
|
|
|
|
} else {
|
|
|
|
this.id = "webcam";
|
|
|
|
}
|
|
|
|
|
|
|
|
this.participant = options.participant;
|
|
|
|
this.recvVideo = options.recvVideo;
|
|
|
|
this.recvAudio = options.recvAudio;
|
|
|
|
this.dataChannel = options.data || false;
|
2017-03-09 19:46:07 +01:00
|
|
|
this.sendVideo = options.video;
|
|
|
|
this.sendAudio = options.audio;
|
|
|
|
this.mediaConstraints = options.mediaConstraints;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getRecvVideo() {
|
|
|
|
return this.recvVideo;
|
|
|
|
}
|
|
|
|
|
|
|
|
getRecvAudio() {
|
|
|
|
return this.recvAudio;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
subscribeToMyRemote() {
|
|
|
|
this.showMyRemote = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
displayMyRemote() {
|
|
|
|
return this.showMyRemote;
|
|
|
|
}
|
|
|
|
|
|
|
|
mirrorLocalStream( wr ) {
|
|
|
|
this.showMyRemote = true;
|
|
|
|
this.localMirrored = true;
|
|
|
|
if ( wr ) {
|
|
|
|
this.wrStream = wr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isLocalMirrored() {
|
|
|
|
return this.localMirrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
getChannelName() {
|
2016-10-15 02:08:23 +02:00
|
|
|
return this.getId() + '_' + this.chanId++;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isDataChannelEnabled() {
|
|
|
|
return this.dataChannel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isDataChannelOpened() {
|
|
|
|
return this.dataChannelOpened;
|
|
|
|
}
|
|
|
|
|
|
|
|
onDataChannelOpen( event ) {
|
|
|
|
console.log( 'Data channel is opened' );
|
|
|
|
this.dataChannelOpened = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
onDataChannelClosed( event ) {
|
|
|
|
console.log( 'Data channel is closed' );
|
|
|
|
this.dataChannelOpened = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendData( data ) {
|
|
|
|
if ( this.wp === undefined ) {
|
|
|
|
throw new Error( 'WebRTC peer has not been created yet' );
|
|
|
|
}
|
|
|
|
if ( !this.dataChannelOpened ) {
|
|
|
|
throw new Error( 'Data channel is not opened' );
|
|
|
|
}
|
|
|
|
console.log( "Sending through data channel: " + data );
|
|
|
|
this.wp.send( data );
|
|
|
|
}
|
|
|
|
|
|
|
|
getWrStream() {
|
|
|
|
return this.wrStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
getWebRtcPeer() {
|
|
|
|
return this.wp;
|
|
|
|
}
|
|
|
|
|
|
|
|
addEventListener( eventName: string, listener: any ) {
|
|
|
|
this.ee.addListener( eventName, listener );
|
|
|
|
}
|
|
|
|
|
|
|
|
showSpinner( spinnerParentId: string ) {
|
|
|
|
let progress = document.createElement( 'div' );
|
2016-10-15 02:08:23 +02:00
|
|
|
progress.id = 'progress-' + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
progress.style.background = "center transparent url('img/spinner.gif') no-repeat";
|
|
|
|
let spinnerParent = document.getElementById( spinnerParentId );
|
|
|
|
if(spinnerParent){
|
|
|
|
spinnerParent.appendChild( progress );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hideSpinner( spinnerId?: string ) {
|
2016-10-15 02:08:23 +02:00
|
|
|
spinnerId = ( spinnerId === undefined ) ? this.getId() : spinnerId;
|
2016-10-16 23:16:58 +02:00
|
|
|
hide( 'progress-' + spinnerId );
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
playOnlyVideo( parentElement, thumbnailId ) {
|
|
|
|
this.video = document.createElement( 'video' );
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
this.video.id = 'native-video-' + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
this.video.autoplay = true;
|
|
|
|
this.video.controls = false;
|
|
|
|
if ( this.wrStream ) {
|
|
|
|
this.video.src = URL.createObjectURL( this.wrStream );
|
2016-10-16 23:16:58 +02:00
|
|
|
show( thumbnailId );
|
2016-10-15 00:11:17 +02:00
|
|
|
this.hideSpinner();
|
|
|
|
} else {
|
2016-10-15 02:08:23 +02:00
|
|
|
console.log( "No wrStream yet for", this.getId() );
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.videoElements.push( {
|
|
|
|
thumb: thumbnailId,
|
|
|
|
video: this.video
|
|
|
|
});
|
|
|
|
|
|
|
|
if ( this.local ) {
|
|
|
|
this.video.muted = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( typeof parentElement === "string" ) {
|
|
|
|
let parentElementDom = document.getElementById( parentElement );
|
|
|
|
if(parentElementDom){
|
|
|
|
parentElementDom.appendChild( this.video );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parentElement.appendChild( this.video );
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.video;
|
|
|
|
}
|
|
|
|
|
|
|
|
playThumbnail( thumbnailId ) {
|
|
|
|
|
|
|
|
let container = document.createElement( 'div' );
|
|
|
|
container.className = "participant";
|
2016-10-15 02:08:23 +02:00
|
|
|
container.id = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
let thumbnail = document.getElementById( thumbnailId );
|
|
|
|
if(thumbnail){
|
|
|
|
thumbnail.appendChild( container );
|
|
|
|
}
|
|
|
|
|
|
|
|
this.elements.push( container );
|
|
|
|
|
|
|
|
let name = document.createElement( 'div' );
|
|
|
|
container.appendChild( name );
|
2016-10-15 02:08:23 +02:00
|
|
|
let userName = this.getId().replace( '_webcam', '' );
|
2016-10-15 00:11:17 +02:00
|
|
|
if ( userName.length >= 16 ) {
|
|
|
|
userName = userName.substring( 0, 16 ) + "...";
|
|
|
|
}
|
|
|
|
name.appendChild( document.createTextNode( userName ) );
|
2016-10-15 02:08:23 +02:00
|
|
|
name.id = "name-" + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
name.className = "name";
|
2016-10-15 02:08:23 +02:00
|
|
|
name.title = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
this.showSpinner( thumbnailId );
|
|
|
|
|
|
|
|
return this.playOnlyVideo( container, thumbnailId );
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
getIdInParticipant() {
|
2016-10-15 00:11:17 +02:00
|
|
|
return this.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
getParticipant() {
|
|
|
|
return this.participant;
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
getId() {
|
2016-10-15 00:11:17 +02:00
|
|
|
if ( this.participant ) {
|
2016-10-15 02:08:23 +02:00
|
|
|
return this.participant.getId() + "_" + this.id;
|
2016-10-15 00:11:17 +02:00
|
|
|
} else {
|
|
|
|
return this.id + "_webcam";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
requestCameraAccess(callback: Callback<Stream>) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
this.participant.addStream( this );
|
|
|
|
|
|
|
|
let constraints = {
|
|
|
|
audio: true,
|
|
|
|
video: {
|
|
|
|
width: {
|
|
|
|
ideal: 1280
|
|
|
|
},
|
|
|
|
frameRate: {
|
|
|
|
ideal: 15
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-15 16:46:19 +01:00
|
|
|
navigator.mediaDevices.getUserMedia(constraints)
|
2017-03-13 18:55:50 +01:00
|
|
|
.then(userStream => {
|
2017-03-09 19:46:07 +01:00
|
|
|
userStream.getAudioTracks()[0].enabled = this.sendAudio;
|
|
|
|
userStream.getVideoTracks()[0].enabled = this.sendVideo;
|
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
this.wrStream = userStream;
|
2017-03-13 18:55:50 +01:00
|
|
|
callback(undefined, this);})
|
|
|
|
.catch(function(e) {
|
|
|
|
console.error( "Access denied", e );
|
|
|
|
callback(e, undefined);
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
publishVideoCallback( error, sdpOfferParam, wp ) {
|
2016-10-15 02:08:23 +02:00
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
if ( error ) {
|
|
|
|
return console.error( "(publish) SDP offer error: "
|
|
|
|
+ JSON.stringify( error ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log( "Sending SDP offer to publish as "
|
2016-10-15 02:08:23 +02:00
|
|
|
+ this.getId(), sdpOfferParam );
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
this.openVidu.sendRequest( "publishVideo", {
|
|
|
|
sdpOffer: sdpOfferParam,
|
|
|
|
doLoopback: this.displayMyRemote() || false
|
|
|
|
}, ( error, response ) => {
|
|
|
|
if ( error ) {
|
|
|
|
console.error( "Error on publishVideo: " + JSON.stringify( error ) );
|
|
|
|
} else {
|
|
|
|
this.room.emitEvent( 'stream-published', [{
|
|
|
|
stream: this
|
|
|
|
}] )
|
|
|
|
this.processSdpAnswer( response.sdpAnswer );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
startVideoCallback( error, sdpOfferParam, wp ) {
|
|
|
|
if ( error ) {
|
|
|
|
return console.error( "(subscribe) SDP offer error: "
|
|
|
|
+ JSON.stringify( error ) );
|
|
|
|
}
|
|
|
|
console.log( "Sending SDP offer to subscribe to "
|
2016-10-15 02:08:23 +02:00
|
|
|
+ this.getId(), sdpOfferParam );
|
2016-10-15 00:11:17 +02:00
|
|
|
this.openVidu.sendRequest( "receiveVideoFrom", {
|
2016-10-15 02:08:23 +02:00
|
|
|
sender: this.getId(),
|
2016-10-15 00:11:17 +02:00
|
|
|
sdpOffer: sdpOfferParam
|
|
|
|
}, ( error, response ) => {
|
|
|
|
if ( error ) {
|
|
|
|
console.error( "Error on recvVideoFrom: " + JSON.stringify( error ) );
|
|
|
|
} else {
|
|
|
|
this.processSdpAnswer( response.sdpAnswer );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private initWebRtcPeer( sdpOfferCallback ) {
|
|
|
|
if ( this.local ) {
|
2017-03-09 19:46:07 +01:00
|
|
|
|
|
|
|
let userMediaConstraints = {
|
|
|
|
audio : this.sendAudio,
|
|
|
|
video : this.sendVideo
|
|
|
|
}
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
let options: any = {
|
|
|
|
videoStream: this.wrStream,
|
2017-03-09 19:46:07 +01:00
|
|
|
mediaConstraints: userMediaConstraints,
|
2016-10-15 00:11:17 +02:00
|
|
|
onicecandidate: this.participant.sendIceCandidate.bind( this.participant ),
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this.dataChannel ) {
|
|
|
|
options.dataChannelConfig = {
|
|
|
|
id: this.getChannelName(),
|
|
|
|
onopen: this.onDataChannelOpen,
|
|
|
|
onclose: this.onDataChannelClosed
|
|
|
|
};
|
|
|
|
options.dataChannels = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this.displayMyRemote() ) {
|
|
|
|
this.wp = new kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv( options, error => {
|
|
|
|
if ( error ) {
|
|
|
|
return console.error( error );
|
|
|
|
}
|
|
|
|
this.wp.generateOffer( sdpOfferCallback.bind( this ) );
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.wp = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly( options, error => {
|
|
|
|
if ( error ) {
|
|
|
|
return console.error( error );
|
|
|
|
}
|
|
|
|
this.wp.generateOffer( sdpOfferCallback.bind( this ) );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let offerConstraints = {
|
|
|
|
mandatory: {
|
|
|
|
OfferToReceiveVideo: this.recvVideo,
|
|
|
|
OfferToReceiveAudio: this.recvAudio
|
|
|
|
}
|
|
|
|
};
|
|
|
|
console.log( "Constraints of generate SDP offer (subscribing)",
|
|
|
|
offerConstraints );
|
|
|
|
let options = {
|
|
|
|
onicecandidate: this.participant.sendIceCandidate.bind( this.participant ),
|
|
|
|
connectionConstraints: offerConstraints
|
|
|
|
}
|
|
|
|
this.wp = new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly( options, error => {
|
|
|
|
if ( error ) {
|
|
|
|
return console.error( error );
|
|
|
|
}
|
|
|
|
this.wp.generateOffer( sdpOfferCallback.bind( this ) );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
console.log( "Waiting for SDP offer to be generated ("
|
2016-10-15 02:08:23 +02:00
|
|
|
+ ( this.local ? "local" : "remote" ) + " peer: " + this.getId() + ")" );
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
publish() {
|
|
|
|
|
|
|
|
// FIXME: Throw error when stream is not local
|
|
|
|
|
|
|
|
this.initWebRtcPeer( this.publishVideoCallback );
|
|
|
|
|
|
|
|
// FIXME: Now we have coupled connecting to a room and adding a
|
|
|
|
// stream to this room. But in the new API, there are two steps.
|
|
|
|
// This is the second step. For now, it do nothing.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
subscribe() {
|
|
|
|
|
|
|
|
// FIXME: In the current implementation all participants are subscribed
|
|
|
|
// automatically to all other participants. We use this method only to
|
|
|
|
// negotiate SDP
|
|
|
|
|
|
|
|
this.initWebRtcPeer( this.startVideoCallback );
|
|
|
|
}
|
|
|
|
|
|
|
|
processSdpAnswer( sdpAnswer ) {
|
|
|
|
|
|
|
|
let answer = new RTCSessionDescription( {
|
|
|
|
type: 'answer',
|
|
|
|
sdp: sdpAnswer,
|
|
|
|
});
|
2016-10-15 02:08:23 +02:00
|
|
|
console.log( this.getId() + ": set peer connection with recvd SDP answer",
|
2016-10-15 00:11:17 +02:00
|
|
|
sdpAnswer );
|
2016-10-15 02:08:23 +02:00
|
|
|
let participantId = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
let pc = this.wp.peerConnection;
|
|
|
|
pc.setRemoteDescription( answer, () => {
|
|
|
|
// Avoids to subscribe to your own stream remotely
|
|
|
|
// except when showMyRemote is true
|
|
|
|
if ( !this.local || this.displayMyRemote() ) {
|
|
|
|
this.wrStream = pc.getRemoteStreams()[0];
|
|
|
|
console.log( "Peer remote stream", this.wrStream );
|
|
|
|
|
|
|
|
if ( this.wrStream != undefined ) {
|
|
|
|
|
|
|
|
this.speechEvent = kurentoUtils.WebRtcPeer.hark( this.wrStream, { threshold: this.room.thresholdSpeaker });
|
|
|
|
|
|
|
|
this.speechEvent.on( 'speaking', () => {
|
|
|
|
this.room.addParticipantSpeaking( participantId );
|
|
|
|
this.room.emitEvent( 'stream-speaking', [{
|
|
|
|
participantId: participantId
|
|
|
|
}] );
|
|
|
|
});
|
|
|
|
|
|
|
|
this.speechEvent.on( 'stopped_speaking', () => {
|
|
|
|
this.room.removeParticipantSpeaking( participantId );
|
|
|
|
this.room.emitEvent( 'stream-stopped-speaking', [{
|
|
|
|
participantId: participantId
|
|
|
|
}] );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for (let videoElement of this.videoElements) {
|
|
|
|
let thumbnailId = videoElement.thumb;
|
|
|
|
let video = videoElement.video;
|
|
|
|
video.src = URL.createObjectURL( this.wrStream );
|
|
|
|
video.onplay = () => {
|
2016-10-15 02:08:23 +02:00
|
|
|
console.log( this.getId() + ': ' + 'Video playing' );
|
2016-10-16 23:16:58 +02:00
|
|
|
show(thumbnailId);
|
2016-10-15 02:08:23 +02:00
|
|
|
this.hideSpinner( this.getId() );
|
2016-10-15 00:11:17 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
this.room.emitEvent( 'stream-subscribed', [{
|
|
|
|
stream: this
|
|
|
|
}] );
|
|
|
|
}
|
|
|
|
}, error => {
|
2016-10-15 02:08:23 +02:00
|
|
|
console.error( this.getId() + ": Error setting SDP to the peer connection: "
|
2016-10-15 00:11:17 +02:00
|
|
|
+ JSON.stringify( error ) );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unpublish() {
|
|
|
|
if ( this.wp ) {
|
|
|
|
this.wp.dispose();
|
|
|
|
} else {
|
|
|
|
if ( this.wrStream ) {
|
|
|
|
this.wrStream.getAudioTracks().forEach( function( track ) {
|
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
this.wrStream.getVideoTracks().forEach( function( track ) {
|
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this.speechEvent ) {
|
|
|
|
this.speechEvent.stop();
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
console.log( this.getId() + ": Stream '" + this.id + "' unpublished" );
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dispose() {
|
|
|
|
|
|
|
|
function disposeElement( element ) {
|
|
|
|
if ( element && element.parentNode ) {
|
|
|
|
element.parentNode.removeChild( element );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.elements.forEach( e => disposeElement( e ) );
|
|
|
|
|
|
|
|
this.videoElements.forEach( ve => disposeElement( ve ) );
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
disposeElement( "progress-" + this.getId() );
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
if ( this.wp ) {
|
|
|
|
this.wp.dispose();
|
|
|
|
} else {
|
|
|
|
if ( this.wrStream ) {
|
|
|
|
this.wrStream.getAudioTracks().forEach( function( track ) {
|
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
this.wrStream.getVideoTracks().forEach( function( track ) {
|
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this.speechEvent ) {
|
|
|
|
this.speechEvent.stop();
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
console.log( this.getId() + ": Stream '" + this.id + "' disposed" );
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}
|