openvidu-browser: PublisherSpeaking and StreamVolumeChange events refactoring

pull/370/head
pabloFuente 2019-12-05 14:18:46 +01:00
parent c387145894
commit 78ffeca860
3 changed files with 258 additions and 83 deletions

View File

@ -100,7 +100,19 @@ export class Session implements EventDispatcher {
/**
* @hidden
*/
speakingEventsEnabled = false;
startSpeakingEventsEnabled = false;
/**
* @hidden
*/
startSpeakingEventsEnabledOnce = false;
/**
* @hidden
*/
stopSpeakingEventsEnabled = false;
/**
* @hidden
*/
stopSpeakingEventsEnabledOnce = false;
private ee = new EventEmitter();
@ -569,13 +581,23 @@ export class Session implements EventDispatcher {
handler(event);
});
if (type === 'publisherStartSpeaking' || type === 'publisherStopSpeaking') {
this.speakingEventsEnabled = true;
if (type === 'publisherStartSpeaking') {
this.startSpeakingEventsEnabled = true;
// If there are already available remote streams, enable hark 'speaking' event in all of them
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str && str.hasAudio) {
str.enableSpeakingEvents();
str.enableStartSpeakingEvent();
}
}
}
if (type === 'publisherStopSpeaking') {
this.stopSpeakingEventsEnabled = true;
// If there are already available remote streams, enable hark 'stopped_speaking' event in all of them
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str && str.hasAudio) {
str.enableStopSpeakingEvent();
}
}
}
@ -591,20 +613,30 @@ export class Session implements EventDispatcher {
this.ee.once(type, event => {
if (event) {
console.info("Event '" + type + "' triggered by 'Session'", event);
console.info("Event '" + type + "' triggered once by 'Session'", event);
} else {
console.info("Event '" + type + "' triggered by 'Session'");
console.info("Event '" + type + "' triggered once by 'Session'");
}
handler(event);
});
if (type === 'publisherStartSpeaking' || type === 'publisherStopSpeaking') {
this.speakingEventsEnabled = true;
// If there are already available remote streams, enable hark in all of them
if (type === 'publisherStartSpeaking') {
this.startSpeakingEventsEnabledOnce = true;
// If there are already available remote streams, enable hark 'speaking' event in all of them once
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str && str.hasAudio) {
str.enableOnceSpeakingEvents();
str.enableOnceStartSpeakingEvent();
}
}
}
if (type === 'publisherStopSpeaking') {
this.stopSpeakingEventsEnabledOnce = true;
// If there are already available remote streams, enable hark 'stopped_speaking' event in all of them once
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str && str.hasAudio) {
str.enableOnceStopSpeakingEvent();
}
}
}
@ -617,21 +649,35 @@ export class Session implements EventDispatcher {
* See [[EventDispatcher.off]]
*/
off(type: string, handler?: (event: SessionDisconnectedEvent | SignalEvent | StreamEvent | ConnectionEvent | PublisherSpeakingEvent | RecordingEvent) => void): Session {
if (!handler) {
this.ee.removeAllListeners(type);
} else {
this.ee.off(type, handler);
}
if (type === 'publisherStartSpeaking' || type === 'publisherStopSpeaking') {
this.speakingEventsEnabled = false;
// If there are already available remote streams, disable hark in all of them
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str) {
str.disableSpeakingEvents();
if (type === 'publisherStartSpeaking') {
let remainingStartSpeakingListeners = this.ee.getListeners(type).length;
if (remainingStartSpeakingListeners === 0) {
this.startSpeakingEventsEnabled = false;
// If there are already available remote streams, disable hark in all of them
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str) {
str.disableStartSpeakingEvent(false);
}
}
}
}
if (type === 'publisherStopSpeaking') {
let remainingStopSpeakingListeners = this.ee.getListeners(type).length;
if (remainingStopSpeakingListeners === 0) {
this.stopSpeakingEventsEnabled = false;
// If there are already available remote streams, disable hark in all of them
for (const connectionId in this.remoteConnections) {
const str = this.remoteConnections[connectionId].stream;
if (!!str) {
str.disableStopSpeakingEvent(false);
}
}
}
}

View File

@ -168,14 +168,26 @@ export class Stream implements EventDispatcher {
* @hidden
*/
publisherStartSpeakingEventEnabled = false;
/**
* @hidden
*/
publisherStartSpeakingEventEnabledOnce = false;
/**
* @hidden
*/
publisherStopSpeakingEventEnabled = false;
/**
* @hidden
*/
publisherStopSpeakingEventEnabledOnce = false;
/**
* @hidden
*/
volumeChangeEventEnabled = false;
/**
* @hidden
*/
volumeChangeEventEnabledOnce = false;
/**
@ -529,30 +541,73 @@ export class Stream implements EventDispatcher {
/**
* @hidden
*/
setSpeechEventIfNotExists(): void {
if (!this.speechEvent) {
const harkOptions = this.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {};
harkOptions.interval = (typeof harkOptions.interval === 'number') ? harkOptions.interval : 50;
harkOptions.threshold = (typeof harkOptions.threshold === 'number') ? harkOptions.threshold : -50;
this.speechEvent = hark(this.mediaStream, harkOptions);
}
}
/**
* @hidden
*/
enableSpeakingEvents(): void {
enableStartSpeakingEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.publisherStartSpeakingEventEnabled) {
this.publisherStartSpeakingEventEnabled = true;
this.speechEvent.on('speaking', () => {
this.session.emitEvent('publisherStartSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStartSpeaking', this.connection, this.streamId)]);
this.publisherStartSpeakingEventEnabledOnce = false; // Disable 'once' version if 'on' version was triggered
});
}
}
/**
* @hidden
*/
enableOnceStartSpeakingEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.publisherStartSpeakingEventEnabledOnce) {
this.publisherStartSpeakingEventEnabledOnce = true;
this.speechEvent.once('speaking', () => {
if (this.publisherStartSpeakingEventEnabledOnce) {
// If the listener has been disabled in the meantime (for example by the 'on' version) do not trigger the event
this.session.emitEvent('publisherStartSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStartSpeaking', this.connection, this.streamId)]);
}
this.disableStartSpeakingEvent(true);
});
}
}
/**
* @hidden
*/
disableStartSpeakingEvent(disabledByOnce: boolean): void {
if (!!this.speechEvent) {
this.publisherStartSpeakingEventEnabledOnce = false;
if (disabledByOnce) {
if (this.publisherStartSpeakingEventEnabled) {
// The 'on' version of this same event is enabled too. Do not remove the hark listener
return;
}
} else {
this.publisherStartSpeakingEventEnabled = false;
}
// Shutting down the hark event
if (this.volumeChangeEventEnabled ||
this.volumeChangeEventEnabledOnce ||
this.publisherStopSpeakingEventEnabled ||
this.publisherStopSpeakingEventEnabledOnce) {
// Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener
this.speechEvent.off('speaking');
} else {
// No other hark event is enabled. We can get entirely rid of it
this.speechEvent.stop();
delete this.speechEvent;
}
}
}
/**
* @hidden
*/
enableStopSpeakingEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.publisherStopSpeakingEventEnabled) {
this.publisherStopSpeakingEventEnabled = true;
this.speechEvent.on('stopped_speaking', () => {
this.session.emitEvent('publisherStopSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStopSpeaking', this.connection, this.streamId)]);
this.publisherStopSpeakingEventEnabledOnce = false; // Disable 'once' version if 'on' version was triggered
});
}
}
@ -560,89 +615,119 @@ export class Stream implements EventDispatcher {
/**
* @hidden
*/
enableOnceSpeakingEvents(): void {
enableOnceStopSpeakingEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.publisherStartSpeakingEventEnabled) {
this.publisherStartSpeakingEventEnabled = true;
this.speechEvent.once('speaking', () => {
this.session.emitEvent('publisherStartSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStartSpeaking', this.connection, this.streamId)]);
this.disableSpeakingEvents();
});
}
if (!this.publisherStopSpeakingEventEnabled) {
this.publisherStopSpeakingEventEnabled = true;
if (!this.publisherStopSpeakingEventEnabledOnce) {
this.publisherStopSpeakingEventEnabledOnce = true;
this.speechEvent.once('stopped_speaking', () => {
this.session.emitEvent('publisherStopSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStopSpeaking', this.connection, this.streamId)]);
this.disableSpeakingEvents();
if (this.publisherStopSpeakingEventEnabledOnce) {
// If the listener has been disabled in the meantime (for example by the 'on' version) do not trigger the event
this.session.emitEvent('publisherStopSpeaking', [new PublisherSpeakingEvent(this.session, 'publisherStopSpeaking', this.connection, this.streamId)]);
}
this.disableStopSpeakingEvent(true);
});
}
}
/**
* @hidden
*/
disableSpeakingEvents(): void {
* @hidden
*/
disableStopSpeakingEvent(disabledByOnce: boolean): void {
if (!!this.speechEvent) {
if (this.volumeChangeEventEnabled) {
// 'streamAudioVolumeChange' event is enabled. Cannot stop the hark process
this.speechEvent.off('speaking');
this.publisherStopSpeakingEventEnabledOnce = false;
if (disabledByOnce) {
if (this.publisherStopSpeakingEventEnabled) {
// We are cancelling the 'once' listener for this event, but the 'on' version
// of this same event is enabled too. Do not remove the hark listener
return;
}
} else {
this.publisherStopSpeakingEventEnabled = false;
}
// Shutting down the hark event
if (this.volumeChangeEventEnabled ||
this.volumeChangeEventEnabledOnce ||
this.publisherStartSpeakingEventEnabled ||
this.publisherStartSpeakingEventEnabledOnce) {
// Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener
this.speechEvent.off('stopped_speaking');
} else {
// No other hark event is enabled. We can get entirely rid of it
this.speechEvent.stop();
delete this.speechEvent;
}
}
this.publisherStartSpeakingEventEnabled = false;
this.publisherStopSpeakingEventEnabled = false;
}
/**
* @hidden
*/
enableVolumeChangeEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.volumeChangeEventEnabled) {
enableVolumeChangeEvent(force: boolean): void {
if (this.setSpeechEventIfNotExists()) {
if (!this.volumeChangeEventEnabled || force) {
this.volumeChangeEventEnabled = true;
this.speechEvent.on('volume_change', harkEvent => {
const oldValue = this.speechEvent.oldVolumeValue;
const value = { newValue: harkEvent, oldValue };
this.speechEvent.oldVolumeValue = harkEvent;
this.streamManager.emitEvent('streamAudioVolumeChange', [new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value)]);
});
}
} else {
// This way whenever the MediaStream object is available, the event listener will be automatically added
this.volumeChangeEventEnabled = true;
this.speechEvent.on('volume_change', harkEvent => {
const oldValue = this.speechEvent.oldVolumeValue;
const value = { newValue: harkEvent, oldValue };
this.speechEvent.oldVolumeValue = harkEvent;
this.streamManager.emitEvent('streamAudioVolumeChange', [new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value)]);
});
}
}
/**
* @hidden
*/
enableOnceVolumeChangeEvent(): void {
this.setSpeechEventIfNotExists();
if (!this.volumeChangeEventEnabled) {
this.volumeChangeEventEnabled = true;
this.speechEvent.once('volume_change', harkEvent => {
const oldValue = this.speechEvent.oldVolumeValue;
const value = { newValue: harkEvent, oldValue };
this.speechEvent.oldVolumeValue = harkEvent;
this.disableVolumeChangeEvent();
this.streamManager.emitEvent('streamAudioVolumeChange', [new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value)]);
});
enableOnceVolumeChangeEvent(force: boolean): void {
if (this.setSpeechEventIfNotExists()) {
if (!this.volumeChangeEventEnabledOnce || force) {
this.volumeChangeEventEnabledOnce = true;
this.speechEvent.once('volume_change', harkEvent => {
const oldValue = this.speechEvent.oldVolumeValue;
const value = { newValue: harkEvent, oldValue };
this.speechEvent.oldVolumeValue = harkEvent;
this.disableVolumeChangeEvent(true);
this.streamManager.emitEvent('streamAudioVolumeChange', [new StreamManagerEvent(this.streamManager, 'streamAudioVolumeChange', value)]);
});
}
} else {
// This way whenever the MediaStream object is available, the event listener will be automatically added
this.volumeChangeEventEnabledOnce = true;
}
}
/**
* @hidden
*/
disableVolumeChangeEvent(): void {
disableVolumeChangeEvent(disabledByOnce: boolean): void {
if (!!this.speechEvent) {
if (this.session.speakingEventsEnabled) {
// 'publisherStartSpeaking' and/or publisherStopSpeaking` events are enabled. Cannot stop the hark process
this.volumeChangeEventEnabledOnce = false;
if (disabledByOnce) {
if (this.volumeChangeEventEnabled) {
// We are cancelling the 'once' listener for this event, but the 'on' version
// of this same event is enabled too. Do not remove the hark listener
return;
}
} else {
this.volumeChangeEventEnabled = false;
}
// Shutting down the hark event
if (this.publisherStartSpeakingEventEnabled ||
this.publisherStartSpeakingEventEnabledOnce ||
this.publisherStopSpeakingEventEnabled ||
this.publisherStopSpeakingEventEnabledOnce) {
// Some other hark event is enabled. Cannot stop the hark process, just remove the specific listener
this.speechEvent.off('volume_change');
} else {
// No other hark event is enabled. We can get entirely rid of it
this.speechEvent.stop();
delete this.speechEvent;
}
}
this.volumeChangeEventEnabled = false;
}
/**
@ -680,9 +765,24 @@ export class Stream implements EventDispatcher {
/* Private methods */
private setSpeechEventIfNotExists(): boolean {
if (!!this.mediaStream) {
if (!this.speechEvent) {
const harkOptions = this.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {};
harkOptions.interval = (typeof harkOptions.interval === 'number') ? harkOptions.interval : 50;
harkOptions.threshold = (typeof harkOptions.threshold === 'number') ? harkOptions.threshold : -50;
this.speechEvent = hark(this.mediaStream, harkOptions);
}
return true;
}
return false;
}
private initWebRtcPeerSend(): Promise<any> {
return new Promise((resolve, reject) => {
this.initHarkEvents(); // Init hark events for the local stream
const userMediaConstraints = {
audio: this.isSendAudio(),
video: this.isSendVideo()
@ -849,8 +949,34 @@ export class Stream implements EventDispatcher {
}
this.updateMediaStreamInVideos();
if (!this.displayMyRemote() && !!this.mediaStream.getAudioTracks()[0] && this.session.speakingEventsEnabled) {
this.enableSpeakingEvents();
this.initHarkEvents(); // Init hark events for the remote stream
}
}
private initHarkEvents(): void {
if (!!this.mediaStream.getAudioTracks()[0]) {
// Hark events can only be set if audio track is available
if (this.streamManager.remote) {
// publisherStartSpeaking/publisherStopSpeaking is only defined for remote streams
if (this.session.startSpeakingEventsEnabled) {
this.enableStartSpeakingEvent();
}
if (this.session.startSpeakingEventsEnabledOnce) {
this.enableOnceStartSpeakingEvent();
}
if (this.session.stopSpeakingEventsEnabled) {
this.enableStopSpeakingEvent();
}
if (this.session.stopSpeakingEventsEnabledOnce) {
this.enableOnceStopSpeakingEvent();
}
}
// streamAudioVolumeChange event is defined for both Publishers and Subscribers
if (this.volumeChangeEventEnabled) {
this.enableVolumeChangeEvent(true);
}
if (this.volumeChangeEventEnabledOnce) {
this.enableOnceVolumeChangeEvent(true);
}
}
}

View File

@ -167,7 +167,7 @@ export class StreamManager implements EventDispatcher {
}
}
if (type === 'streamAudioVolumeChange' && this.stream.hasAudio) {
this.stream.enableVolumeChangeEvent();
this.stream.enableVolumeChangeEvent(false);
}
return this;
}
@ -178,9 +178,9 @@ export class StreamManager implements EventDispatcher {
once(type: string, handler: (event: Event) => void): StreamManager {
this.ee.once(type, event => {
if (event) {
console.info("Event '" + type + "' triggered once", event);
console.info("Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", event);
} else {
console.info("Event '" + type + "' triggered once");
console.info("Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'");
}
handler(event);
});
@ -200,7 +200,7 @@ export class StreamManager implements EventDispatcher {
}
}
if (type === 'streamAudioVolumeChange' && this.stream.hasAudio) {
this.stream.enableOnceVolumeChangeEvent();
this.stream.enableOnceVolumeChangeEvent(false);
}
return this;
}
@ -216,7 +216,10 @@ export class StreamManager implements EventDispatcher {
}
if (type === 'streamAudioVolumeChange') {
this.stream.disableVolumeChangeEvent();
let remainingVolumeEventListeners = this.ee.getListeners(type).length;
if (remainingVolumeEventListeners === 0) {
this.stream.disableVolumeChangeEvent(false);
}
}
return this;