2018-05-29 18:28:58 +02:00
/ *
2022-01-13 11:18:47 +01:00
* ( C ) Copyright 2017 - 2022 OpenVidu ( https : //openvidu.io)
2018-05-29 18:28:58 +02:00
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*
* /
import { Stream } from './Stream' ;
2021-05-31 15:42:48 +02:00
import { Subscriber } from './Subscriber' ;
2020-04-26 22:22:39 +02:00
import { EventDispatcher } from './EventDispatcher' ;
2018-05-29 18:28:58 +02:00
import { StreamManagerVideo } from '../OpenViduInternal/Interfaces/Public/StreamManagerVideo' ;
2022-01-10 12:46:39 +01:00
import { StreamManagerEventMap } from '../OpenViduInternal/Events/EventMap/StreamManagerEventMap' ;
2018-05-29 18:28:58 +02:00
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent' ;
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent' ;
2022-01-10 12:46:39 +01:00
import { ExceptionEvent , ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent' ;
2018-05-29 18:28:58 +02:00
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode' ;
2020-05-04 20:01:56 +02:00
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger' ;
2020-10-13 16:13:37 +02:00
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform' ;
2020-05-04 20:01:56 +02:00
/ * *
* @hidden
* /
const logger : OpenViduLogger = OpenViduLogger . getInstance ( ) ;
2018-05-29 18:28:58 +02:00
2020-10-13 16:13:37 +02:00
/ * *
* @hidden
* /
2020-11-26 13:17:55 +01:00
let platform : PlatformUtils ;
2020-10-13 16:13:37 +02:00
2018-05-29 18:28:58 +02:00
/ * *
* Interface in charge of displaying the media streams in the HTML DOM . This wraps any [ [ Publisher ] ] and [ [ Subscriber ] ] object .
* You can insert as many video players fo the same Stream as you want by calling [ [ StreamManager . addVideoElement ] ] or
* [ [ StreamManager . createVideoElement ] ] .
* The use of StreamManager wrapper is particularly useful when you don ' t need to differentiate between Publisher or Subscriber streams or just
* want to directly manage your own video elements ( even more than one video element per Stream ) . This scenario is pretty common in
* declarative , MVC frontend frameworks such as Angular , React or Vue . js
2020-05-04 20:01:56 +02:00
*
2022-01-13 13:54:34 +01:00
* See available event listeners at [ [ StreamManagerEventMap ] ] .
2018-05-29 18:28:58 +02:00
* /
2020-04-26 22:22:39 +02:00
export class StreamManager extends EventDispatcher {
2018-05-29 18:28:58 +02:00
/ * *
* The Stream represented in the DOM by the Publisher / Subscriber
* /
stream : Stream ;
/ * *
* All the videos displaying the Stream of this Publisher / Subscriber
* /
videos : StreamManagerVideo [ ] = [ ] ;
/ * *
* Whether the Stream represented in the DOM is local or remote
* - ` false ` for [ [ Publisher ] ]
* - ` true ` for [ [ Subscriber ] ]
* /
remote : boolean ;
/ * *
2018-06-01 14:39:38 +02:00
* The DOM HTMLElement assigned as target element when creating the video for the Publisher / Subscriber . This property is only defined if :
2018-05-29 18:28:58 +02:00
* - [ [ Publisher ] ] has been initialized by calling method [ [ OpenVidu . initPublisher ] ] with a valid ` targetElement ` parameter
* - [ [ Subscriber ] ] has been initialized by calling method [ [ Session . subscribe ] ] with a valid ` targetElement ` parameter
* /
targetElement : HTMLElement ;
/ * *
* ` id ` attribute of the DOM video element displaying the Publisher / Subscriber ' s stream . This property is only defined if :
* - [ [ Publisher ] ] has been initialized by calling method [ [ OpenVidu . initPublisher ] ] with a valid ` targetElement ` parameter
* - [ [ Subscriber ] ] has been initialized by calling method [ [ Session . subscribe ] ] with a valid ` targetElement ` parameter
* /
id : string ;
/ * *
* @hidden
* /
2021-05-25 20:22:31 +02:00
protected firstVideoElement? : StreamManagerVideo ;
2018-05-29 18:28:58 +02:00
/ * *
* @hidden
* /
2021-05-25 20:22:31 +02:00
protected element : HTMLElement ;
2018-05-29 18:28:58 +02:00
/ * *
* @hidden
* /
2021-05-25 20:22:31 +02:00
protected canPlayListener : EventListener ;
2018-05-29 18:28:58 +02:00
/ * *
* @hidden
* /
2021-05-25 20:22:31 +02:00
private streamPlayingEventExceptionTimeout? : NodeJS.Timeout ;
/ * *
* @hidden
* /
private lazyLaunchVideoElementCreatedEvent = false ;
2018-05-29 18:28:58 +02:00
/ * *
* @hidden
* /
constructor ( stream : Stream , targetElement? : HTMLElement | string ) {
2020-04-26 22:22:39 +02:00
super ( ) ;
2020-11-26 13:17:55 +01:00
platform = PlatformUtils . getInstance ( ) ;
2018-05-29 18:28:58 +02:00
this . stream = stream ;
this . stream . streamManager = this ;
this . remote = ! this . stream . isLocal ( ) ;
if ( ! ! targetElement ) {
let targEl ;
if ( typeof targetElement === 'string' ) {
targEl = document . getElementById ( targetElement ) ;
} else if ( targetElement instanceof HTMLElement ) {
targEl = targetElement ;
}
if ( ! ! targEl ) {
this . firstVideoElement = {
targetElement : targEl ,
video : document.createElement ( 'video' ) ,
2019-05-29 14:54:37 +02:00
id : '' ,
canplayListenerAdded : false
2018-05-29 18:28:58 +02:00
} ;
2021-07-13 11:49:05 +02:00
if ( platform . isSafariBrowser ( ) || ( platform . isIPhoneOrIPad ( ) && ( platform . isChromeMobileBrowser ( ) || platform . isEdgeMobileBrowser ( ) || platform . isOperaMobileBrowser ( ) || platform . isFirefoxMobileBrowser ( ) ) ) ) {
2018-11-21 12:03:14 +01:00
this . firstVideoElement . video . setAttribute ( 'playsinline' , 'true' ) ;
}
2018-05-29 18:28:58 +02:00
this . targetElement = targEl ;
this . element = targEl ;
}
}
2019-06-03 17:16:18 +02:00
this . canPlayListener = ( ) = > {
2021-05-25 20:22:31 +02:00
this . deactivateStreamPlayingEventExceptionTimeout ( ) ;
2019-06-03 17:16:18 +02:00
this . ee . emitEvent ( 'streamPlaying' , [ new StreamManagerEvent ( this , 'streamPlaying' , undefined ) ] ) ;
} ;
2018-05-29 18:28:58 +02:00
}
/ * *
* See [ [ EventDispatcher . on ] ]
* /
2022-01-10 12:46:39 +01:00
on < K extends keyof StreamManagerEventMap > ( type : K , handler : ( event : StreamManagerEventMap [ K ] ) = > void ) : this {
2020-04-26 22:22:39 +02:00
2022-01-10 12:46:39 +01:00
super . onAux ( type , "Event '" + type + "' triggered by '" + ( this . remote ? 'Subscriber' : 'Publisher' ) + "'" , handler ) ;
2020-04-26 22:22:39 +02:00
2018-05-29 18:28:58 +02:00
if ( type === 'videoElementCreated' ) {
if ( ! ! this . stream && this . lazyLaunchVideoElementCreatedEvent ) {
this . ee . emitEvent ( 'videoElementCreated' , [ new VideoElementEvent ( this . videos [ 0 ] . video , this , 'videoElementCreated' ) ] ) ;
this . lazyLaunchVideoElementCreatedEvent = false ;
}
}
2022-01-10 12:46:39 +01:00
if ( type === 'streamPlaying' ) {
2018-05-29 18:28:58 +02:00
if ( this . videos [ 0 ] && this . videos [ 0 ] . video &&
this . videos [ 0 ] . video . currentTime > 0 &&
this . videos [ 0 ] . video . paused === false &&
this . videos [ 0 ] . video . ended === false &&
this . videos [ 0 ] . video . readyState === 4 ) {
2018-12-07 11:22:21 +01:00
this . ee . emitEvent ( 'streamPlaying' , [ new StreamManagerEvent ( this , 'streamPlaying' , undefined ) ] ) ;
2018-05-29 18:28:58 +02:00
}
}
2021-03-30 18:04:56 +02:00
if ( this . stream . hasAudio ) {
if ( type === 'publisherStartSpeaking' ) {
this . stream . enableHarkSpeakingEvent ( ) ;
}
if ( type === 'publisherStopSpeaking' ) {
this . stream . enableHarkStoppedSpeakingEvent ( ) ;
}
if ( type === 'streamAudioVolumeChange' ) {
this . stream . enableHarkVolumeChangeEvent ( false ) ;
}
2018-12-07 11:22:21 +01:00
}
2018-05-29 18:28:58 +02:00
return this ;
}
/ * *
* See [ [ EventDispatcher . once ] ]
* /
2022-01-10 12:46:39 +01:00
once < K extends keyof StreamManagerEventMap > ( type : K , handler : ( event : StreamManagerEventMap [ K ] ) = > void ) : this {
2020-04-26 22:22:39 +02:00
super . onceAux ( type , "Event '" + type + "' triggered once by '" + ( this . remote ? 'Subscriber' : 'Publisher' ) + "'" , handler ) ;
2018-05-29 18:28:58 +02:00
if ( type === 'videoElementCreated' ) {
if ( ! ! this . stream && this . lazyLaunchVideoElementCreatedEvent ) {
this . ee . emitEvent ( 'videoElementCreated' , [ new VideoElementEvent ( this . videos [ 0 ] . video , this , 'videoElementCreated' ) ] ) ;
}
}
2022-01-10 12:46:39 +01:00
if ( type === 'streamPlaying' ) {
2018-05-29 18:28:58 +02:00
if ( this . videos [ 0 ] && this . videos [ 0 ] . video &&
this . videos [ 0 ] . video . currentTime > 0 &&
this . videos [ 0 ] . video . paused === false &&
this . videos [ 0 ] . video . ended === false &&
this . videos [ 0 ] . video . readyState === 4 ) {
2018-12-07 11:22:21 +01:00
this . ee . emitEvent ( 'streamPlaying' , [ new StreamManagerEvent ( this , 'streamPlaying' , undefined ) ] ) ;
2018-05-29 18:28:58 +02:00
}
}
2021-03-30 18:04:56 +02:00
if ( this . stream . hasAudio ) {
if ( type === 'publisherStartSpeaking' ) {
this . stream . enableOnceHarkSpeakingEvent ( ) ;
}
if ( type === 'publisherStopSpeaking' ) {
this . stream . enableOnceHarkStoppedSpeakingEvent ( ) ;
}
if ( type === 'streamAudioVolumeChange' ) {
this . stream . enableOnceHarkVolumeChangeEvent ( false ) ;
}
2018-12-07 11:22:21 +01:00
}
2018-05-29 18:28:58 +02:00
return this ;
}
/ * *
* See [ [ EventDispatcher . off ] ]
* /
2022-01-10 12:46:39 +01:00
off < K extends keyof StreamManagerEventMap > ( type : K , handler ? : ( event : StreamManagerEventMap [ K ] ) = > void ) : this {
2020-04-26 22:22:39 +02:00
2022-01-10 12:46:39 +01:00
super . offAux ( type , handler ) ;
2018-12-07 11:22:21 +01:00
2021-03-30 18:04:56 +02:00
if ( type === 'publisherStartSpeaking' ) {
// Both StreamManager and Session can have "publisherStartSpeaking" event listeners
const remainingStartSpeakingEventListeners = this . ee . getListeners ( type ) . length + this . stream . session . ee . getListeners ( type ) . length ;
if ( remainingStartSpeakingEventListeners === 0 ) {
this . stream . disableHarkSpeakingEvent ( false ) ;
}
}
if ( type === 'publisherStopSpeaking' ) {
// Both StreamManager and Session can have "publisherStopSpeaking" event listeners
const remainingStopSpeakingEventListeners = this . ee . getListeners ( type ) . length + this . stream . session . ee . getListeners ( type ) . length ;
if ( remainingStopSpeakingEventListeners === 0 ) {
this . stream . disableHarkStoppedSpeakingEvent ( false ) ;
}
}
2018-12-07 11:22:21 +01:00
if ( type === 'streamAudioVolumeChange' ) {
2021-03-30 18:04:56 +02:00
// Only StreamManager can have "streamAudioVolumeChange" event listeners
const remainingVolumeEventListeners = this . ee . getListeners ( type ) . length ;
2019-12-05 14:18:46 +01:00
if ( remainingVolumeEventListeners === 0 ) {
2021-03-30 18:04:56 +02:00
this . stream . disableHarkVolumeChangeEvent ( false ) ;
2019-12-05 14:18:46 +01:00
}
2018-12-07 11:22:21 +01:00
}
2018-05-29 18:28:58 +02:00
return this ;
}
/ * *
2018-06-01 14:39:38 +02:00
* Makes ` video ` element parameter display this [ [ stream ] ] . This is useful when you are
2020-04-05 21:34:10 +02:00
* [ managing the video elements on your own ] ( / e n / s t a b l e / c h e a t s h e e t / m a n a g e - v i d e o s / # y o u - t a k e - c a r e - o f - t h e - v i d e o - p l a y e r s )
2018-05-29 18:28:58 +02:00
*
* Calling this method with a video already added to other Publisher / Subscriber will cause the video element to be
* disassociated from that previous Publisher / Subscriber and to be associated to this one .
*
* @returns 1 if the video wasn ' t associated to any other Publisher / Subscriber and has been successfully added to this one .
* 0 if the video was already added to this Publisher / Subscriber . - 1 if the video was previously associated to any other
* Publisher / Subscriber and has been successfully disassociated from that one and properly added to this one .
* /
addVideoElement ( video : HTMLVideoElement ) : number {
this . initializeVideoProperties ( video ) ;
2021-05-31 15:42:48 +02:00
if ( ! this . remote && this . stream . displayMyRemote ( ) ) {
2019-05-29 14:54:37 +02:00
if ( video . srcObject !== this . stream . getMediaStream ( ) ) {
video . srcObject = this . stream . getMediaStream ( ) ;
}
2018-09-26 12:09:34 +02:00
}
2018-05-29 18:28:58 +02:00
// If the video element is already part of this StreamManager do nothing
for ( const v of this . videos ) {
if ( v . video === video ) {
return 0 ;
}
}
let returnNumber = 1 ;
for ( const streamManager of this . stream . session . streamManagers ) {
if ( streamManager . disassociateVideo ( video ) ) {
returnNumber = - 1 ;
break ;
}
}
this . stream . session . streamManagers . forEach ( streamManager = > {
streamManager . disassociateVideo ( video ) ;
} ) ;
this . pushNewStreamManagerVideo ( {
video ,
2019-05-29 14:54:37 +02:00
id : video.id ,
canplayListenerAdded : false
2018-05-29 18:28:58 +02:00
} ) ;
2021-05-25 20:22:31 +02:00
2021-04-29 23:37:44 +02:00
logger . info ( 'New video element associated to ' , this ) ;
2018-05-29 18:28:58 +02:00
return returnNumber ;
}
/ * *
2018-06-01 14:39:38 +02:00
* Creates a new video element displaying this [ [ stream ] ] . This allows you to have multiple video elements displaying the same media stream .
*
* # # # # Events dispatched
2018-05-29 18:28:58 +02:00
*
2018-06-01 14:39:38 +02:00
* The Publisher / Subscriber object will dispatch a ` videoElementCreated ` event once the HTML video element has been added to DOM . See [ [ VideoElementEvent ] ]
*
* @param targetElement HTML DOM element ( or its ` id ` attribute ) in which the video element of the Publisher / Subscriber will be inserted
* @param insertMode How the video element will be inserted accordingly to ` targetElemet `
2019-05-29 14:54:37 +02:00
*
* @returns The created HTMLVideoElement
2018-05-29 18:28:58 +02:00
* /
createVideoElement ( targetElement? : string | HTMLElement , insertMode? : VideoInsertMode ) : HTMLVideoElement {
let targEl ;
if ( typeof targetElement === 'string' ) {
2019-04-06 16:59:12 +02:00
targEl = document . getElementById ( targetElement ) ;
2018-05-29 18:28:58 +02:00
if ( ! targEl ) {
throw new Error ( "The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement ) ;
}
} else if ( targetElement instanceof HTMLElement ) {
targEl = targetElement ;
} else {
throw new Error ( "The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement ) ;
}
2020-06-30 10:48:55 +02:00
const video = this . createVideo ( ) ;
2018-05-29 18:28:58 +02:00
this . initializeVideoProperties ( video ) ;
let insMode = ! ! insertMode ? insertMode : VideoInsertMode.APPEND ;
switch ( insMode ) {
case VideoInsertMode . AFTER :
targEl . parentNode ! ! . insertBefore ( video , targEl . nextSibling ) ;
break ;
case VideoInsertMode . APPEND :
targEl . appendChild ( video ) ;
break ;
case VideoInsertMode . BEFORE :
targEl . parentNode ! ! . insertBefore ( video , targEl ) ;
break ;
case VideoInsertMode . PREPEND :
targEl . insertBefore ( video , targEl . childNodes [ 0 ] ) ;
break ;
case VideoInsertMode . REPLACE :
targEl . parentNode ! ! . replaceChild ( video , targEl ) ;
break ;
default :
insMode = VideoInsertMode . APPEND ;
targEl . appendChild ( video ) ;
break ;
}
const v : StreamManagerVideo = {
targetElement : targEl ,
video ,
insertMode : insMode ,
2019-05-29 14:54:37 +02:00
id : video.id ,
canplayListenerAdded : false
2018-05-29 18:28:58 +02:00
} ;
this . pushNewStreamManagerVideo ( v ) ;
2019-06-03 17:16:18 +02:00
this . ee . emitEvent ( 'videoElementCreated' , [ new VideoElementEvent ( v . video , this , 'videoElementCreated' ) ] ) ;
this . lazyLaunchVideoElementCreatedEvent = ! ! this . firstVideoElement ;
2018-05-29 18:28:58 +02:00
return video ;
}
2019-12-10 14:14:54 +01:00
/ * *
2022-01-14 13:31:26 +01:00
* Updates the current configuration for the [ [ PublisherSpeakingEvent ] ] feature and the [ StreamManagerEvent . streamAudioVolumeChange ] ( / e n / s t a b l e / a p i / o p e n v i d u - b r o w s e r / c l a s s e s / S t r e a m M a n a g e r E v e n t . h t m l ) f e a t u r e f o r t h i s s p e c i f i c
2019-12-10 14:14:54 +01:00
* StreamManager audio stream , overriding the global options set with [ [ OpenVidu . setAdvancedConfiguration ] ] . This way you can customize the audio events options
* for each specific StreamManager and change them dynamically .
2020-05-04 20:01:56 +02:00
*
2019-12-10 14:14:54 +01:00
* @param publisherSpeakingEventsOptions New options to be applied to this StreamManager ' s audio stream . It is an object which includes the following optional properties :
* - ` interval ` : ( number ) how frequently the analyser polls the audio stream to check if speaking has started / stopped or audio volume has changed . Default * * 100 * * ( ms )
* - ` threshold ` : ( number ) the volume at which _publisherStartSpeaking_ , _publisherStopSpeaking_ events will be fired . Default * * - 50 * * ( dB )
* /
2021-05-27 21:53:01 +02:00
updatePublisherSpeakingEventsOptions ( publisherSpeakingEventsOptions : { interval? : number , threshold? : number } ) : void {
2019-12-10 14:14:54 +01:00
const currentHarkOptions = ! ! this . stream . harkOptions ? this . stream . harkOptions : ( this . stream . session . openvidu . advancedConfiguration . publisherSpeakingEventsOptions || { } ) ;
const newInterval = ( typeof publisherSpeakingEventsOptions . interval === 'number' ) ?
publisherSpeakingEventsOptions . interval : ( ( typeof currentHarkOptions . interval === 'number' ) ? currentHarkOptions.interval : 100 ) ;
const newThreshold = ( typeof publisherSpeakingEventsOptions . threshold === 'number' ) ?
publisherSpeakingEventsOptions . threshold : ( ( typeof currentHarkOptions . threshold === 'number' ) ? currentHarkOptions . threshold : - 50 ) ;
this . stream . harkOptions = {
interval : newInterval ,
threshold : newThreshold
} ;
if ( ! ! this . stream . speechEvent ) {
this . stream . speechEvent . setInterval ( newInterval ) ;
this . stream . speechEvent . setThreshold ( newThreshold ) ;
}
}
/* Hidden methods */
2018-05-29 18:28:58 +02:00
/ * *
* @hidden
* /
initializeVideoProperties ( video : HTMLVideoElement ) : void {
2021-05-31 15:42:48 +02:00
if ( ! ( ! this . remote && this . stream . displayMyRemote ( ) ) ) {
2018-07-04 10:31:36 +02:00
// Avoid setting the MediaStream into the srcObject if remote subscription before publishing
2019-05-29 14:54:37 +02:00
if ( video . srcObject !== this . stream . getMediaStream ( ) ) {
// If srcObject already set don't do it again
video . srcObject = this . stream . getMediaStream ( ) ;
}
2018-07-04 10:31:36 +02:00
}
2018-05-29 18:28:58 +02:00
video . autoplay = true ;
video . controls = false ;
2018-11-21 12:03:14 +01:00
2021-07-13 11:49:05 +02:00
if ( platform . isSafariBrowser ( ) || ( platform . isIPhoneOrIPad ( ) && ( platform . isChromeMobileBrowser ( ) || platform . isEdgeMobileBrowser ( ) || platform . isOperaMobileBrowser ( ) || platform . isFirefoxMobileBrowser ( ) ) ) ) {
2018-11-21 12:03:14 +01:00
video . setAttribute ( 'playsinline' , 'true' ) ;
}
2019-06-03 17:16:18 +02:00
if ( ! video . id ) {
2019-05-29 14:54:37 +02:00
video . id = ( this . remote ? 'remote-' : 'local-' ) + 'video-' + this . stream . streamId ;
2020-05-04 20:01:56 +02:00
// DEPRECATED property: assign once the property id if the user provided a valid targetElement
2019-05-29 14:54:37 +02:00
if ( ! this . id && ! ! this . targetElement ) {
this . id = video . id ;
}
2019-05-21 14:18:57 +02:00
}
2022-01-25 20:11:31 +01:00
if ( this . remote ) {
// Do not mirror remote video.
// Remove the mirror if this video was currently associated with a local mirrored video.
this . removeMirrorVideo ( video ) ;
}
else if ( ! this . stream . displayMyRemote ( ) ) {
2018-05-29 18:28:58 +02:00
video . muted = true ;
2018-10-25 11:31:23 +02:00
if ( video . style . transform === 'rotateY(180deg)' && ! this . stream . outboundStreamOpts . publisherProperties . mirror ) {
2018-10-25 11:30:36 +02:00
// If the video was already rotated and now is set to not mirror
this . removeMirrorVideo ( video ) ;
2019-06-03 13:55:09 +02:00
} else if ( this . stream . outboundStreamOpts . publisherProperties . mirror && ! this . stream . isSendScreen ( ) ) {
2018-05-29 18:28:58 +02:00
this . mirrorVideo ( video ) ;
}
}
}
/ * *
* @hidden
* /
removeAllVideos ( ) : void {
for ( let i = this . stream . session . streamManagers . length - 1 ; i >= 0 ; -- i ) {
if ( this . stream . session . streamManagers [ i ] === this ) {
this . stream . session . streamManagers . splice ( i , 1 ) ;
}
}
2018-07-19 17:31:30 +02:00
this . videos . forEach ( streamManagerVideo = > {
2019-05-29 14:54:37 +02:00
// Remove oncanplay event listener (only OpenVidu browser listener, not the user ones)
2021-03-30 18:04:56 +02:00
if ( ! ! streamManagerVideo . video && ! ! streamManagerVideo . video . removeEventListener ) {
2020-06-30 10:48:55 +02:00
streamManagerVideo . video . removeEventListener ( 'canplay' , this . canPlayListener ) ;
2021-03-30 18:04:56 +02:00
}
streamManagerVideo . canplayListenerAdded = false ;
2018-05-29 18:28:58 +02:00
if ( ! ! streamManagerVideo . targetElement ) {
2018-07-19 17:31:30 +02:00
// Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher
// and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event
2018-05-29 18:28:58 +02:00
streamManagerVideo . video . parentNode ! . removeChild ( streamManagerVideo . video ) ;
this . ee . emitEvent ( 'videoElementDestroyed' , [ new VideoElementEvent ( streamManagerVideo . video , this , 'videoElementDestroyed' ) ] ) ;
}
2018-07-19 17:31:30 +02:00
// Remove srcObject from the video
2020-06-30 10:48:55 +02:00
this . removeSrcObject ( streamManagerVideo ) ;
2018-07-19 17:31:30 +02:00
// Remove from collection of videos every video managed by OpenVidu Browser
this . videos . filter ( v = > ! v . targetElement ) ;
2018-05-29 18:28:58 +02:00
} ) ;
}
/ * *
* @hidden
* /
disassociateVideo ( video : HTMLVideoElement ) : boolean {
let disassociated = false ;
for ( let i = 0 ; i < this . videos . length ; i ++ ) {
if ( this . videos [ i ] . video === video ) {
2019-06-03 17:16:18 +02:00
this . videos [ i ] . video . removeEventListener ( 'canplay' , this . canPlayListener ) ;
2018-05-29 18:28:58 +02:00
this . videos . splice ( i , 1 ) ;
disassociated = true ;
2020-05-04 20:01:56 +02:00
logger . info ( 'Video element disassociated from ' , this ) ;
2018-05-29 18:28:58 +02:00
break ;
}
}
return disassociated ;
}
/ * *
* @hidden
* /
addPlayEventToFirstVideo() {
2019-05-29 14:54:37 +02:00
if ( ( ! ! this . videos [ 0 ] ) && ( ! ! this . videos [ 0 ] . video ) && ( ! this . videos [ 0 ] . canplayListenerAdded ) ) {
2021-05-25 20:22:31 +02:00
this . activateStreamPlayingEventExceptionTimeout ( ) ;
2019-06-03 17:16:18 +02:00
this . videos [ 0 ] . video . addEventListener ( 'canplay' , this . canPlayListener ) ;
2019-05-29 14:54:37 +02:00
this . videos [ 0 ] . canplayListenerAdded = true ;
2018-05-29 18:28:58 +02:00
}
}
/ * *
* @hidden
* /
updateMediaStream ( mediaStream : MediaStream ) {
this . videos . forEach ( streamManagerVideo = > {
streamManagerVideo . video . srcObject = mediaStream ;
2020-10-13 16:13:37 +02:00
if ( platform . isIonicIos ( ) ) {
2018-11-28 09:42:26 +01:00
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
// the media stream to be updated
const vParent = streamManagerVideo . video . parentElement ;
const newVideo = streamManagerVideo . video ;
vParent ! ! . replaceChild ( newVideo , streamManagerVideo . video ) ;
streamManagerVideo . video = newVideo ;
2018-11-26 10:30:49 +01:00
}
2018-05-29 18:28:58 +02:00
} ) ;
}
2018-07-03 15:35:08 +02:00
/ * *
* @hidden
* /
emitEvent ( type : string , eventArray : any [ ] ) : void {
this . ee . emitEvent ( type , eventArray ) ;
}
2021-03-30 18:04:56 +02:00
/ * *
* @hidden
* /
2020-06-30 10:48:55 +02:00
createVideo ( ) : HTMLVideoElement {
return document . createElement ( 'video' ) ;
}
/ * *
* @hidden
* /
removeSrcObject ( streamManagerVideo : StreamManagerVideo ) {
streamManagerVideo . video . srcObject = null ;
2021-05-25 20:22:31 +02:00
this . deactivateStreamPlayingEventExceptionTimeout ( ) ;
2020-06-30 10:48:55 +02:00
}
2019-12-10 14:14:54 +01:00
/* Private methods */
2020-06-30 10:48:55 +02:00
protected pushNewStreamManagerVideo ( streamManagerVideo : StreamManagerVideo ) {
2018-05-29 18:28:58 +02:00
this . videos . push ( streamManagerVideo ) ;
this . addPlayEventToFirstVideo ( ) ;
if ( this . stream . session . streamManagers . indexOf ( this ) === - 1 ) {
this . stream . session . streamManagers . push ( this ) ;
}
}
private mirrorVideo ( video ) : void {
2020-10-13 16:13:37 +02:00
if ( ! platform . isIonicIos ( ) ) {
2018-11-29 09:41:43 +01:00
video . style . transform = 'rotateY(180deg)' ;
video . style . webkitTransform = 'rotateY(180deg)' ;
}
2018-05-29 18:28:58 +02:00
}
2018-10-25 11:30:36 +02:00
private removeMirrorVideo ( video ) : void {
video . style . transform = 'unset' ;
video . style . webkitTransform = 'unset' ;
}
2021-05-25 20:22:31 +02:00
private activateStreamPlayingEventExceptionTimeout() {
2021-05-31 15:42:48 +02:00
if ( ! this . remote ) {
// ExceptionEvent NO_STREAM_PLAYING_EVENT is only for subscribers
return ;
}
2021-05-25 20:22:31 +02:00
if ( this . streamPlayingEventExceptionTimeout != null ) {
// The timeout is already activated
return ;
}
// Trigger ExceptionEvent NO_STREAM_PLAYING_EVENT if after timeout there is no 'canplay' event
const msTimeout = this . stream . session . openvidu . advancedConfiguration . noStreamPlayingEventExceptionTimeout || 4000 ;
this . streamPlayingEventExceptionTimeout = setTimeout ( ( ) = > {
const msg = 'StreamManager of Stream ' + this . stream . streamId + ' (' + ( this . remote ? 'Subscriber' : 'Publisher' ) + ') did not trigger "streamPlaying" event in ' + msTimeout + ' ms' ;
logger . warn ( msg ) ;
2021-05-31 15:42:48 +02:00
this . stream . session . emitEvent ( 'exception' , [ new ExceptionEvent ( this . stream . session , ExceptionEventName . NO_STREAM_PLAYING_EVENT , ( < any > this ) as Subscriber , msg ) ] ) ;
2021-05-25 20:22:31 +02:00
delete this . streamPlayingEventExceptionTimeout ;
} , msTimeout ) ;
}
private deactivateStreamPlayingEventExceptionTimeout() {
clearTimeout ( this . streamPlayingEventExceptionTimeout as any ) ;
delete this . streamPlayingEventExceptionTimeout ;
}
2022-01-25 20:11:31 +01:00
}