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 ( ) ;
* /
2017-05-17 11:46:32 +02:00
import { Connection } from './Connection' ;
2017-05-10 10:55:31 +02:00
import { SessionInternal } from './SessionInternal' ;
import { OpenViduInternal , Callback } from './OpenViduInternal' ;
2017-10-04 10:30:15 +02:00
import { OpenViduError , OpenViduErrorName } from './OpenViduError' ;
2016-10-16 23:16:58 +02:00
import EventEmitter = require ( 'wolfy87-eventemitter' ) ;
2017-09-22 15:57:59 +02:00
import * as kurentoUtils from '../KurentoUtils/kurento-utils-js' ;
2016-10-16 23:16:58 +02:00
2017-03-23 13:15:45 +01:00
import * as adapter from 'webrtc-adapter' ;
2016-10-16 23:16:58 +02:00
declare var navigator : any ;
2016-10-15 00:11:17 +02:00
declare var RTCSessionDescription : any ;
2017-03-23 13:15:45 +01:00
if ( window ) {
window [ "adapter" ] = adapter ;
}
2017-03-17 19:22:02 +01: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
}
2017-03-17 19:22:02 +01:00
function show ( id : string ) {
2016-10-16 23:16:58 +02:00
document . getElementById ( jq ( id ) ) ! . style . display = 'block' ;
}
2017-03-17 19:22:02 +01:00
function hide ( id : string ) {
2016-10-16 23:16:58 +02:00
document . getElementById ( jq ( id ) ) ! . style . display = 'none' ;
2016-10-15 00:11:17 +02:00
}
export interface StreamOptions {
id : string ;
2017-05-22 22:30:08 +02:00
connection : Connection ;
2017-09-29 20:00:00 +02:00
recvVideo : boolean ;
recvAudio : boolean ;
sendVideo : boolean ;
sendAudio : boolean ;
activeAudio : boolean ;
activeVideo : boolean ;
2016-10-15 00:11:17 +02:00
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 {
2017-05-22 22:30:08 +02:00
public connection : Connection ;
2017-10-03 15:02:36 +02:00
ee = new EventEmitter ( ) ;
2017-09-30 14:14:10 +02:00
private wrStream : MediaStream ;
2016-10-15 00:11:17 +02:00
private wp : any ;
private id : string ;
private video : HTMLVideoElement ;
private videoElements : VideoOptions [ ] = [ ] ;
private elements : HTMLDivElement [ ] = [ ] ;
private speechEvent : any ;
2017-09-29 20:00:00 +02:00
private recvVideo : boolean ;
private recvAudio : boolean ;
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 ;
2017-09-29 20:00:00 +02:00
private activeAudio = true ;
private activeVideo = true ;
2017-08-21 20:12:13 +02:00
2017-09-22 15:57:59 +02:00
private videoSrcObject : MediaStream | null ;
2017-04-17 16:23:30 +02:00
private parentId : string ;
2017-04-12 00:54:35 +02:00
public isReady : boolean = false ;
2017-07-19 17:56:06 +02:00
public isVideoELementCreated : boolean = false ;
2017-05-31 17:31:26 +02:00
public accessIsAllowed : boolean = false ;
public accessIsDenied : boolean = false ;
2017-10-03 15:02:36 +02:00
public isScreenRequestedReady : boolean = false ;
2017-10-04 10:30:15 +02:00
private isScreenRequested = false ;
2017-03-28 20:17:44 +02:00
2017-10-03 15:02:36 +02:00
constructor ( private openVidu : OpenViduInternal , private local : boolean , private room : SessionInternal , options : any ) {
if ( options !== 'screen-options' ) {
this . configureOptions ( options ) ;
2016-10-15 00:11:17 +02:00
} else {
2017-10-04 10:30:15 +02:00
this . isScreenRequested = true ;
2017-10-03 15:02:36 +02:00
this . connection = this . room . getLocalParticipant ( ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-28 20:17:44 +02:00
this . addEventListener ( 'src-added' , ( srcEvent ) = > {
2017-09-22 15:57:59 +02:00
this . videoSrcObject = srcEvent . srcObject ;
if ( this . video ) this . video . srcObject = srcEvent . srcObject ;
console . debug ( "Video srcObject [" + srcEvent . srcObject + "] added to stream [" + this . getId ( ) + "]" ) ;
2017-03-28 20:17:44 +02:00
} ) ;
}
emitSrcEvent ( wrstream ) {
this . ee . emitEvent ( 'src-added' , [ {
2017-09-22 15:57:59 +02:00
srcObject : wrstream
2017-03-28 20:17:44 +02:00
} ] ) ;
}
2017-04-17 16:23:30 +02:00
emitStreamReadyEvent() {
2017-04-12 00:54:35 +02:00
this . ee . emitEvent ( 'stream-ready' ) , [ { } ] ;
}
2017-09-22 15:57:59 +02:00
getVideoSrcObject() {
return this . videoSrcObject ;
2017-03-28 20:17:44 +02:00
}
2017-04-28 14:52:55 +02:00
removeVideo ( parentElement : string ) ;
removeVideo ( parentElement : Element ) ;
removeVideo ( ) ;
2017-04-17 16:23:30 +02:00
removeVideo ( parentElement ? ) {
2017-04-28 14:52:55 +02:00
if ( typeof parentElement === "string" ) {
2017-04-17 16:23:30 +02:00
document . getElementById ( parentElement ) ! . removeChild ( this . video ) ;
2017-04-28 14:52:55 +02:00
} else if ( parentElement instanceof Element ) {
parentElement . removeChild ( this . video ) ;
}
else if ( ! parentElement ) {
2017-04-17 16:23:30 +02:00
if ( document . getElementById ( this . parentId ) ) {
document . getElementById ( this . parentId ) ! . removeChild ( this . video ) ;
}
}
2016-10-15 00:11:17 +02:00
}
2017-04-28 14:52:55 +02:00
getVideoElement ( ) : HTMLVideoElement {
return this . video ;
}
setVideoElement ( video : HTMLVideoElement ) {
this . video = video ;
}
2017-03-28 20:17:44 +02:00
2017-04-17 16:23:30 +02:00
2016-10-15 00:11:17 +02:00
getRecvVideo() {
return this . recvVideo ;
}
getRecvAudio() {
return this . recvAudio ;
}
2017-10-03 15:02:36 +02:00
getSendVideo() {
return this . sendVideo ;
}
getSendAudio() {
return this . sendAudio ;
}
2016-10-15 00:11:17 +02:00
subscribeToMyRemote() {
this . showMyRemote = true ;
}
displayMyRemote() {
return this . showMyRemote ;
}
2017-03-17 19:22:02 +01:00
mirrorLocalStream ( wr ) {
2016-10-15 00:11:17 +02:00
this . showMyRemote = true ;
this . localMirrored = true ;
2017-03-17 19:22:02 +01:00
if ( wr ) {
2016-10-15 00:11:17 +02:00
this . wrStream = wr ;
2017-03-28 20:17:44 +02:00
this . emitSrcEvent ( this . wrStream ) ;
2016-10-15 00:11:17 +02:00
}
}
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 ;
}
2017-03-17 19:22:02 +01:00
onDataChannelOpen ( event ) {
2017-09-22 15:57:59 +02:00
console . debug ( 'Data channel is opened' ) ;
2016-10-15 00:11:17 +02:00
this . dataChannelOpened = true ;
}
2017-03-17 19:22:02 +01:00
onDataChannelClosed ( event ) {
2017-09-22 15:57:59 +02:00
console . debug ( 'Data channel is closed' ) ;
2016-10-15 00:11:17 +02:00
this . dataChannelOpened = false ;
}
2017-03-17 19:22:02 +01:00
sendData ( data ) {
if ( this . wp === undefined ) {
throw new Error ( 'WebRTC peer has not been created yet' ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
if ( ! this . dataChannelOpened ) {
throw new Error ( 'Data channel is not opened' ) ;
2016-10-15 00:11:17 +02:00
}
2017-09-22 15:57:59 +02:00
console . info ( "Sending through data channel: " + data ) ;
2017-03-17 19:22:02 +01:00
this . wp . send ( data ) ;
2016-10-15 00:11:17 +02:00
}
getWrStream() {
return this . wrStream ;
}
getWebRtcPeer() {
return this . wp ;
}
2017-03-17 19:22:02 +01:00
addEventListener ( eventName : string , listener : any ) {
this . ee . addListener ( eventName , listener ) ;
2016-10-15 00:11:17 +02:00
}
2017-04-28 14:52:55 +02:00
addOnceEventListener ( eventName : string , listener : any ) {
this . ee . addOnceListener ( eventName , listener ) ;
}
2017-06-26 10:15:58 +02:00
removeListener ( eventName ) {
2017-04-28 14:52:55 +02:00
this . ee . removeAllListeners ( eventName ) ;
}
2017-03-17 19:22:02 +01:00
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" ;
2017-03-17 19:22:02 +01:00
let spinnerParent = document . getElementById ( spinnerParentId ) ;
if ( spinnerParent ) {
spinnerParent . appendChild ( progress ) ;
2016-10-15 00:11:17 +02:00
}
}
2017-03-17 19:22:02 +01:00
hideSpinner ( spinnerId? : string ) {
spinnerId = ( spinnerId === undefined ) ? this . getId ( ) : spinnerId ;
hide ( 'progress-' + spinnerId ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
playOnlyVideo ( parentElement , thumbnailId ) {
2017-06-26 10:15:58 +02:00
// TO-DO: check somehow if the stream is audio only, so the element created is <audio> instead of <video>
2017-07-19 17:56:06 +02:00
2017-03-17 19:22:02 +01:00
this . video = document . createElement ( 'video' ) ;
2016-10-15 00:11:17 +02:00
2017-09-22 15:57:59 +02:00
this . video . id = ( this . local ? 'local-' : 'remote-' ) + 'video-' + this . getId ( ) ;
2016-10-15 00:11:17 +02:00
this . video . autoplay = true ;
this . video . controls = false ;
2017-09-22 15:57:59 +02:00
this . video . srcObject = this . videoSrcObject ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
this . videoElements . push ( {
2016-10-15 00:11:17 +02:00
thumb : thumbnailId ,
video : this.video
} ) ;
2017-09-22 15:57:59 +02:00
if ( this . local && ! this . displayMyRemote ( ) ) {
2016-10-15 00:11:17 +02:00
this . video . muted = true ;
2017-10-10 15:54:04 +02:00
this . video . oncanplay = ( ) = > {
2017-09-22 15:57:59 +02:00
console . info ( "Local 'Stream' with id [" + this . getId ( ) + "] video is now playing" ) ;
this . ee . emitEvent ( 'video-is-playing' , [ {
element : this.video
} ] ) ;
} ;
2017-03-29 14:57:45 +02:00
} else {
this . video . title = this . getId ( ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
if ( typeof parentElement === "string" ) {
2017-04-17 16:23:30 +02:00
this . parentId = parentElement ;
2017-03-17 19:22:02 +01:00
let parentElementDom = document . getElementById ( parentElement ) ;
if ( parentElementDom ) {
2017-03-28 20:17:44 +02:00
this . video = parentElementDom . appendChild ( this . video ) ;
2017-07-19 17:56:06 +02:00
this . ee . emitEvent ( 'video-element-created-by-stream' , [ {
element : this.video
} ] ) ;
this . isVideoELementCreated = true ;
2016-10-15 00:11:17 +02:00
}
} else {
2017-04-17 16:23:30 +02:00
this . parentId = parentElement . id ;
2017-03-28 20:17:44 +02:00
this . video = parentElement . appendChild ( this . video ) ;
2016-10-15 00:11:17 +02:00
}
2017-04-28 14:52:55 +02:00
this . isReady = true ;
2016-10-15 00:11:17 +02:00
return this . video ;
}
2017-03-17 19:22:02 +01:00
playThumbnail ( thumbnailId ) {
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
let container = document . createElement ( 'div' ) ;
2016-10-15 00:11:17 +02:00
container . className = "participant" ;
2016-10-15 02:08:23 +02:00
container . id = this . getId ( ) ;
2017-03-17 19:22:02 +01:00
let thumbnail = document . getElementById ( thumbnailId ) ;
if ( thumbnail ) {
thumbnail . appendChild ( container ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
this . elements . push ( container ) ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
let name = document . createElement ( 'div' ) ;
container . appendChild ( name ) ;
let userName = this . getId ( ) . replace ( '_webcam' , '' ) ;
if ( userName . length >= 16 ) {
userName = userName . substring ( 0 , 16 ) + "..." ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
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
2017-03-17 19:22:02 +01:00
this . showSpinner ( thumbnailId ) ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
return this . playOnlyVideo ( container , thumbnailId ) ;
2016-10-15 00:11:17 +02:00
}
2016-10-15 02:08:23 +02:00
getIdInParticipant() {
2016-10-15 00:11:17 +02:00
return this . id ;
}
getParticipant() {
2017-05-22 22:30:08 +02:00
return this . connection ;
2016-10-15 00:11:17 +02:00
}
2016-10-15 02:08:23 +02:00
getId() {
2017-09-22 15:57:59 +02:00
return this . connection . connectionId + "_" + this . id ;
2016-10-15 00:11:17 +02:00
}
2017-03-23 13:15:45 +01:00
getRTCPeerConnection() {
2017-03-21 10:36:18 +01:00
return this . getWebRtcPeer ( ) . peerConnection ;
}
2016-10-15 02:08:23 +02:00
requestCameraAccess ( callback : Callback < Stream > ) {
2016-10-15 00:11:17 +02:00
2017-05-22 22:30:08 +02:00
this . connection . addStream ( this ) ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
let constraints = this . mediaConstraints ;
2017-06-26 10:15:58 +02:00
/ * l e t c o n s t r a i n t s 2 = {
2016-10-15 00:11:17 +02:00
audio : true ,
video : {
width : {
ideal : 1280
} ,
frameRate : {
ideal : 15
}
}
2017-06-26 10:15:58 +02:00
} ; * /
this . userMediaHasVideo ( ( hasVideo ) = > {
if ( ! hasVideo ) {
2017-10-04 10:30:15 +02:00
if ( this . sendVideo ) {
callback ( new OpenViduError ( OpenViduErrorName . NO_VIDEO_DEVICE , 'You have requested camera access but there is no video input device available. Trying to connect with an audio input device only' ) , this ) ;
}
if ( ! this . sendAudio ) {
callback ( new OpenViduError ( OpenViduErrorName . NO_INPUT_DEVICE , 'You must init Publisher object with audio or video streams enabled' ) , undefined ) ;
} else {
constraints . video = false ;
this . sendVideo = false ;
this . requestCameraAccesAux ( constraints , callback ) ;
}
2017-06-26 10:15:58 +02:00
} else {
this . requestCameraAccesAux ( constraints , callback ) ;
}
} ) ;
}
2016-10-15 00:11:17 +02:00
2017-06-26 10:15:58 +02:00
private requestCameraAccesAux ( constraints , callback ) {
2017-04-02 23:09:04 +02:00
navigator . mediaDevices . getUserMedia ( constraints )
2017-03-17 19:22:02 +01:00
. then ( userStream = > {
2017-08-22 12:09:44 +02:00
this . cameraAccessSuccess ( userStream , callback ) ;
} )
. catch ( error = > {
2017-09-29 20:00:00 +02:00
this . accessIsDenied = true ;
this . accessIsAllowed = false ;
2017-10-04 10:30:15 +02:00
let errorName : OpenViduErrorName ;
let errorMessage = error . toString ( ) ; ;
if ( ! this . isScreenRequested ) {
errorName = this . sendVideo ? OpenViduErrorName.CAMERA_ACCESS_DENIED : OpenViduErrorName.MICROPHONE_ACCESS_DENIED ;
} else {
errorName = OpenViduErrorName . SCREEN_CAPTURE_DENIED ; // This code is only reachable for Firefox
}
callback ( new OpenViduError ( errorName , errorMessage ) , undefined ) ;
2017-08-22 12:09:44 +02:00
} ) ;
}
2017-05-31 17:31:26 +02:00
2017-10-03 15:02:36 +02:00
private cameraAccessSuccess ( userStream : MediaStream , callback : Function ) {
2017-08-22 12:09:44 +02:00
this . accessIsAllowed = true ;
this . accessIsDenied = false ;
this . ee . emitEvent ( 'access-allowed-by-publisher' ) ;
2017-03-17 19:22:02 +01:00
2017-08-22 12:09:44 +02:00
if ( userStream . getAudioTracks ( ) [ 0 ] != null ) {
2017-09-29 20:00:00 +02:00
userStream . getAudioTracks ( ) [ 0 ] . enabled = this . activeAudio ;
2017-08-22 12:09:44 +02:00
}
if ( userStream . getVideoTracks ( ) [ 0 ] != null ) {
2017-09-29 20:00:00 +02:00
userStream . getVideoTracks ( ) [ 0 ] . enabled = this . activeVideo ;
2017-08-22 12:09:44 +02:00
}
2017-03-28 20:17:44 +02:00
2017-08-22 12:09:44 +02:00
this . wrStream = userStream ;
this . emitSrcEvent ( this . wrStream ) ;
2017-05-31 17:31:26 +02:00
2017-08-22 12:09:44 +02:00
callback ( undefined , this ) ;
2016-10-15 00:11:17 +02:00
}
2017-06-26 10:15:58 +02:00
private userMediaHasVideo ( callback ) {
2017-10-04 10:30:15 +02:00
// If the user is going to publish its screen there's a video source
if ( this . isScreenRequested ) {
callback ( true ) ;
return ;
} else {
// List all input devices and serach for a video kind
navigator . mediaDevices . enumerateDevices ( ) . then ( function ( mediaDevices ) {
var videoInput = mediaDevices . filter ( function ( deviceInfo ) {
return deviceInfo . kind === 'videoinput' ;
} ) [ 0 ] ;
callback ( videoInput != null ) ;
} ) ;
}
2017-06-26 10:15:58 +02:00
}
2017-03-17 19:22:02 +01:00
publishVideoCallback ( error , sdpOfferParam , wp ) {
if ( error ) {
return console . error ( "(publish) SDP offer error: "
+ JSON . stringify ( error ) ) ;
2016-10-15 00:11:17 +02:00
}
2017-09-22 15:57:59 +02:00
console . debug ( "Sending SDP offer to publish as "
2017-03-17 19:22:02 +01:00
+ this . getId ( ) , sdpOfferParam ) ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
this . openVidu . sendRequest ( "publishVideo" , {
2016-10-15 00:11:17 +02:00
sdpOffer : sdpOfferParam ,
2017-08-21 20:12:13 +02:00
doLoopback : this.displayMyRemote ( ) || false ,
2017-09-29 20:00:00 +02:00
audioActive : this.sendAudio ,
videoActive : this.sendVideo
2017-03-17 19:22:02 +01:00
} , ( error , response ) = > {
if ( error ) {
console . error ( "Error on publishVideo: " + JSON . stringify ( error ) ) ;
2016-10-15 00:11:17 +02:00
} else {
2017-03-17 19:22:02 +01:00
this . processSdpAnswer ( response . sdpAnswer ) ;
2017-09-22 15:57:59 +02:00
console . info ( "'Publisher' succesfully published to session" ) ;
2016-10-15 00:11:17 +02:00
}
} ) ;
}
2017-03-17 19:22:02 +01:00
startVideoCallback ( error , sdpOfferParam , wp ) {
if ( error ) {
return console . error ( "(subscribe) SDP offer error: "
+ JSON . stringify ( error ) ) ;
2016-10-15 00:11:17 +02:00
}
2017-09-22 15:57:59 +02:00
console . debug ( "Sending SDP offer to subscribe to "
2017-03-17 19:22:02 +01:00
+ this . getId ( ) , sdpOfferParam ) ;
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
2017-03-17 19:22:02 +01:00
} , ( error , response ) = > {
if ( error ) {
console . error ( "Error on recvVideoFrom: " + JSON . stringify ( error ) ) ;
2016-10-15 00:11:17 +02:00
} else {
2017-03-17 19:22:02 +01:00
this . processSdpAnswer ( response . sdpAnswer ) ;
2016-10-15 00:11:17 +02:00
}
} ) ;
}
2017-03-17 19:22:02 +01:00
private initWebRtcPeer ( sdpOfferCallback ) {
if ( this . local ) {
2017-03-09 19:46:07 +01:00
let userMediaConstraints = {
2017-03-17 19:22:02 +01:00
audio : this.sendAudio ,
video : this.sendVideo
2017-03-09 19:46:07 +01:00
}
2017-03-17 19:22:02 +01:00
2016-10-15 00:11:17 +02:00
let options : any = {
videoStream : this.wrStream ,
2017-03-09 19:46:07 +01:00
mediaConstraints : userMediaConstraints ,
2017-05-22 22:30:08 +02:00
onicecandidate : this.connection.sendIceCandidate.bind ( this . connection ) ,
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
if ( this . dataChannel ) {
2016-10-15 00:11:17 +02:00
options . dataChannelConfig = {
id : this.getChannelName ( ) ,
onopen : this.onDataChannelOpen ,
onclose : this.onDataChannelClosed
} ;
options . dataChannels = true ;
}
2017-03-17 19:22:02 +01:00
if ( this . displayMyRemote ( ) ) {
2017-09-22 15:57:59 +02:00
this . wp = kurentoUtils . WebRtcPeer . WebRtcPeerSendrecv ( options , error = > {
2017-03-17 19:22:02 +01:00
if ( error ) {
return console . error ( error ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
this . wp . generateOffer ( sdpOfferCallback . bind ( this ) ) ;
2016-10-15 00:11:17 +02:00
} ) ;
} else {
2017-09-22 15:57:59 +02:00
this . wp = kurentoUtils . WebRtcPeer . WebRtcPeerSendonly ( options , error = > {
2017-03-17 19:22:02 +01:00
if ( error ) {
return console . error ( error ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
this . wp . generateOffer ( sdpOfferCallback . bind ( this ) ) ;
2016-10-15 00:11:17 +02:00
} ) ;
}
} else {
let offerConstraints = {
2017-08-21 20:12:13 +02:00
audio : this.recvAudio ,
2017-09-29 20:00:00 +02:00
video : this.recvVideo
2016-10-15 00:11:17 +02:00
} ;
2017-09-22 15:57:59 +02:00
console . debug ( "'Session.subscribe(Stream)' called. Constraints of generate SDP offer" ,
2017-03-17 19:22:02 +01:00
offerConstraints ) ;
2016-10-15 00:11:17 +02:00
let options = {
2017-05-22 22:30:08 +02:00
onicecandidate : this.connection.sendIceCandidate.bind ( this . connection ) ,
2017-08-21 20:12:13 +02:00
mediaConstraints : offerConstraints
2016-10-15 00:11:17 +02:00
}
2017-09-22 15:57:59 +02:00
this . wp = kurentoUtils . WebRtcPeer . WebRtcPeerRecvonly ( options , error = > {
2017-03-17 19:22:02 +01:00
if ( error ) {
return console . error ( error ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
this . wp . generateOffer ( sdpOfferCallback . bind ( this ) ) ;
2016-10-15 00:11:17 +02:00
} ) ;
}
2017-09-22 15:57:59 +02:00
console . debug ( "Waiting for SDP offer to be generated ("
+ ( this . local ? "local" : "remote" ) + " 'Stream': " + this . getId ( ) + ")" ) ;
2016-10-15 00:11:17 +02:00
}
publish() {
// FIXME: Throw error when stream is not local
2017-04-12 00:54:35 +02:00
if ( this . isReady ) {
this . initWebRtcPeer ( this . publishVideoCallback ) ;
} else {
2017-04-12 11:45:08 +02:00
this . ee . once ( 'stream-ready' , streamEvent = > {
2017-04-12 00:54:35 +02:00
this . publish ( ) ;
} ) ;
}
2016-10-15 00:11:17 +02:00
// 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
2017-03-17 19:22:02 +01:00
this . initWebRtcPeer ( this . startVideoCallback ) ;
2016-10-15 00:11:17 +02:00
}
2017-03-17 19:22:02 +01:00
processSdpAnswer ( sdpAnswer ) {
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
let answer = new RTCSessionDescription ( {
2016-10-15 00:11:17 +02:00
type : 'answer' ,
sdp : sdpAnswer ,
} ) ;
2017-09-22 15:57:59 +02:00
console . debug ( this . getId ( ) + ": set peer connection with recvd SDP answer" ,
2017-03-17 19:22:02 +01: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 ;
2017-03-17 19:22:02 +01:00
pc . setRemoteDescription ( answer , ( ) = > {
2016-10-15 00:11:17 +02:00
// Avoids to subscribe to your own stream remotely
// except when showMyRemote is true
2017-03-17 19:22:02 +01:00
if ( ! this . local || this . displayMyRemote ( ) ) {
2016-10-15 00:11:17 +02:00
this . wrStream = pc . getRemoteStreams ( ) [ 0 ] ;
2017-09-22 15:57:59 +02:00
console . debug ( "Peer remote stream" , this . wrStream ) ;
2017-03-17 19:22:02 +01:00
if ( this . wrStream != undefined ) {
2017-03-28 20:17:44 +02:00
this . emitSrcEvent ( this . wrStream ) ;
2017-09-30 14:14:10 +02:00
if ( this . wrStream . getAudioTracks ( ) [ 0 ] != null ) {
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
} ] ) ;
} ) ;
2017-10-03 15:02:36 +02:00
2017-09-30 14:14:10 +02:00
}
2016-10-15 00:11:17 +02:00
}
for ( let videoElement of this . videoElements ) {
let thumbnailId = videoElement . thumb ;
let video = videoElement . video ;
2017-09-22 15:57:59 +02:00
video . srcObject = this . wrStream ;
2017-10-10 15:54:04 +02:00
video . oncanplay = ( ) = > {
2017-09-22 15:57:59 +02:00
if ( this . local && this . displayMyRemote ( ) ) {
console . info ( "Your own remote 'Stream' with id [" + this . getId ( ) + "] video is now playing" ) ;
this . ee . emitEvent ( 'remote-video-is-playing' , [ {
2017-10-10 15:54:04 +02:00
element : video
2017-09-22 15:57:59 +02:00
} ] ) ;
} else if ( ! this . local && ! this . displayMyRemote ( ) ) {
console . info ( "Remote 'Stream' with id [" + this . getId ( ) + "] video is now playing" ) ;
this . ee . emitEvent ( 'video-is-playing' , [ {
2017-10-10 15:54:04 +02:00
element : video
2017-09-22 15:57:59 +02:00
} ] ) ;
}
2017-03-28 20:17:44 +02:00
//show(thumbnailId);
//this.hideSpinner(this.getId());
2016-10-15 00:11:17 +02:00
} ;
}
2017-03-17 19:22:02 +01:00
this . room . emitEvent ( 'stream-subscribed' , [ {
2016-10-15 00:11:17 +02:00
stream : this
2017-03-17 19:22:02 +01:00
} ] ) ;
2016-10-15 00:11:17 +02:00
}
} , error = > {
2017-03-17 19:22:02 +01:00
console . error ( this . getId ( ) + ": Error setting SDP to the peer connection: "
+ JSON . stringify ( error ) ) ;
2016-10-15 00:11:17 +02:00
} ) ;
}
unpublish() {
2017-03-17 19:22:02 +01:00
if ( this . wp ) {
2016-10-15 00:11:17 +02:00
this . wp . dispose ( ) ;
} else {
2017-03-17 19:22:02 +01:00
if ( this . wrStream ) {
this . wrStream . getAudioTracks ( ) . forEach ( function ( track ) {
2016-10-15 00:11:17 +02:00
track . stop && track . stop ( )
} )
2017-03-17 19:22:02 +01:00
this . wrStream . getVideoTracks ( ) . forEach ( function ( track ) {
2016-10-15 00:11:17 +02:00
track . stop && track . stop ( )
} )
}
}
2017-03-17 19:22:02 +01:00
if ( this . speechEvent ) {
2016-10-15 00:11:17 +02:00
this . speechEvent . stop ( ) ;
}
2017-09-22 15:57:59 +02:00
console . info ( this . getId ( ) + ": Stream '" + this . id + "' unpublished" ) ;
2016-10-15 00:11:17 +02:00
}
dispose() {
2017-03-17 19:22:02 +01:00
function disposeElement ( element ) {
if ( element && element . parentNode ) {
element . parentNode . removeChild ( element ) ;
2016-10-15 00:11:17 +02:00
}
}
2017-03-17 19:22:02 +01:00
this . elements . forEach ( e = > disposeElement ( e ) ) ;
2016-10-15 00:11:17 +02:00
2017-08-29 17:31:34 +02:00
//this.videoElements.forEach(ve => disposeElement(ve.video));
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
disposeElement ( "progress-" + this . getId ( ) ) ;
2016-10-15 00:11:17 +02:00
2017-03-17 19:22:02 +01:00
if ( this . wp ) {
2016-10-15 00:11:17 +02:00
this . wp . dispose ( ) ;
} else {
2017-03-17 19:22:02 +01:00
if ( this . wrStream ) {
this . wrStream . getAudioTracks ( ) . forEach ( function ( track ) {
2016-10-15 00:11:17 +02:00
track . stop && track . stop ( )
} )
2017-03-17 19:22:02 +01:00
this . wrStream . getVideoTracks ( ) . forEach ( function ( track ) {
2016-10-15 00:11:17 +02:00
track . stop && track . stop ( )
} )
}
}
2017-03-17 19:22:02 +01:00
if ( this . speechEvent ) {
2016-10-15 00:11:17 +02:00
this . speechEvent . stop ( ) ;
}
2017-09-22 15:57:59 +02:00
console . info ( ( this . local ? "Local " : "Remote " ) + "'Stream' with id [" + this . getId ( ) + "]' has been succesfully disposed" ) ;
2016-10-15 00:11:17 +02:00
}
2017-10-03 15:02:36 +02:00
private configureOptions ( options ) {
if ( options . id ) {
this . id = options . id ;
} else {
this . id = "webcam" ;
}
this . connection = options . connection ;
this . recvVideo = options . recvVideo || false ;
this . recvAudio = options . recvAudio || false ;
this . sendVideo = options . sendVideo ;
this . sendAudio = options . sendAudio ;
this . activeAudio = options . activeAudio ;
this . activeVideo = options . activeVideo ;
this . dataChannel = options . data || false ;
this . mediaConstraints = options . mediaConstraints ;
}
configureScreenOptions ( options ) {
if ( options . id ) {
this . id = options . id ;
} else {
this . id = "screen" ;
}
this . recvVideo = options . recvVideo || false ;
this . recvAudio = options . recvAudio || false ;
this . sendVideo = options . sendVideo ;
this . sendAudio = options . sendAudio ;
this . activeAudio = options . activeAudio ;
this . activeVideo = options . activeVideo ;
this . dataChannel = options . data || false ;
this . mediaConstraints = options . mediaConstraints ;
this . ee . emitEvent ( 'can-request-screen' ) ;
}
2016-10-15 00:11:17 +02:00
}