openvidu-components: Improved algorithm for showing captions

pull/761/head
Carlos Santos 2022-11-11 16:47:53 +01:00
parent 55c97c3d78
commit b2426149c6
1 changed files with 48 additions and 39 deletions

View File

@ -1,11 +1,4 @@
import { import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
OnInit,
QueryList, ViewChildren
} from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { PanelEvent, PanelService } from '../../services/panel/panel.service'; import { PanelEvent, PanelService } from '../../services/panel/panel.service';
@ -50,10 +43,10 @@ export class CaptionsComponent implements OnInit {
session: Session; session: Session;
private deleteTimeout: NodeJS.Timeout; private deleteFirstTimeout: NodeJS.Timeout;
private deleteAllTimeout: NodeJS.Timeout; private deleteAllTimeout: NodeJS.Timeout;
private DELETE_TIMEOUT = 5 * 1000; private DELETE_TIMEOUT = 10 * 1000;
private MAX_EVENTS_LIMIT = 3; private MAX_EVENTS_LIMIT = 3;
private captionLanguageSubscription: Subscription; private captionLanguageSubscription: Subscription;
private captionLangSelected: { name: string; ISO: string }; private captionLangSelected: { name: string; ISO: string };
@ -85,16 +78,15 @@ export class CaptionsComponent implements OnInit {
async ngOnDestroy() { async ngOnDestroy() {
this.captionService.setCaptionsEnabled(false); this.captionService.setCaptionsEnabled(false);
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe();
this.session.off('speechToTextMessage');
this.captionEvents = [];
for (const p of this.participantService.getRemoteParticipants()) { for (const p of this.participantService.getRemoteParticipants()) {
const stream = p.getCameraConnection().streamManager.stream; const stream = p.getCameraConnection().streamManager.stream;
await this.session.unsubscribeFromSpeechToText(stream); await this.session.unsubscribeFromSpeechToText(stream);
} }
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
if (this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe();
this.session.off('speechToTextMessage');
this.captionEvents = [];
} }
onSettingsCliked() { onSettingsCliked() {
@ -125,47 +117,64 @@ export class CaptionsComponent implements OnInit {
let captionEventsCopy = [...this.captionEvents]; let captionEventsCopy = [...this.captionEvents];
let eventsNumber = captionEventsCopy.length; let eventsNumber = captionEventsCopy.length;
if (eventsNumber === this.MAX_EVENTS_LIMIT) {
captionEventsCopy.shift();
eventsNumber--;
}
if (eventsNumber === 0) { if (eventsNumber === 0) {
captionEventsCopy.push(caption); captionEventsCopy.push(caption);
} else { } else {
const lastCaption: CaptionModel = captionEventsCopy.slice(-1)[0]; const lastCaption: CaptionModel | undefined = captionEventsCopy[eventsNumber - 1];
const sameAuthorAsAbove: boolean = lastCaption.connectionId === caption.connectionId; const sameSpeakerAsAbove: boolean = lastCaption.connectionId === caption.connectionId;
const allEventsAreSameSpeaker = captionEventsCopy.every((e) => e.connectionId === caption.connectionId); const lastSpeakerHasStoppedTalking = lastCaption.type === 'recognized';
if (sameAuthorAsAbove) { if (sameSpeakerAsAbove) {
if (lastCaption.type === 'recognized') { if (lastSpeakerHasStoppedTalking) {
if (eventsNumber === 2 && allEventsAreSameSpeaker) { // Add event if different from previous one
captionEventsCopy.shift(); if (caption.text !== lastCaption.text) {
clearInterval(this.deleteTimeout);
}
captionEventsCopy.push(caption);
this.deleteFirstEventAfterDelay(this.DELETE_TIMEOUT); this.deleteFirstEventAfterDelay(this.DELETE_TIMEOUT);
captionEventsCopy.push(caption);
}
} else { } else {
//Updating last 'recognizing' caption
lastCaption.text = caption.text; lastCaption.text = caption.text;
lastCaption.type = caption.type; lastCaption.type = caption.type;
} }
} else { } else {
// TODO: Test when STT is able to work with multiple streams // Different speaker is talking
if (eventsNumber === 2) { const speakerExists: boolean = captionEventsCopy.some((ev) => ev.connectionId === caption.connectionId);
captionEventsCopy.shift(); if (speakerExists) {
clearInterval(this.deleteTimeout); // Speaker is already showing
} if (lastSpeakerHasStoppedTalking) {
captionEventsCopy.push(caption);
this.deleteFirstEventAfterDelay(this.DELETE_TIMEOUT); this.deleteFirstEventAfterDelay(this.DELETE_TIMEOUT);
captionEventsCopy.push(caption);
} else {
// There was an interruption. Last event is still being 'recognizing' (speaker is talking)
// Update last speaker event.
const lastSpeakerCaption = captionEventsCopy.find((cap) => cap.connectionId === caption.connectionId);
if (lastSpeakerCaption) {
if (lastSpeakerCaption.type === 'recognized') {
captionEventsCopy.push(caption);
} else {
lastSpeakerCaption.text = caption.text;
lastSpeakerCaption.type = caption.type;
} }
} }
}
} else {
this.deleteFirstEventAfterDelay(this.DELETE_TIMEOUT);
captionEventsCopy.push(caption);
}
}
}
if (captionEventsCopy.length === this.MAX_EVENTS_LIMIT) {
clearInterval(this.deleteFirstTimeout);
captionEventsCopy.shift();
}
this.captionEvents = [...captionEventsCopy]; this.captionEvents = [...captionEventsCopy];
this.scrollToBottom(); this.scrollToBottom();
} }
private deleteFirstEventAfterDelay(timeout: number) { private deleteFirstEventAfterDelay(timeout: number) {
this.deleteTimeout = setTimeout(() => { this.deleteFirstTimeout = setTimeout(() => {
this.captionEvents.shift(); this.captionEvents.shift();
this.cd.markForCheck(); this.cd.markForCheck();
}, timeout); }, timeout);
@ -195,9 +204,9 @@ export class CaptionsComponent implements OnInit {
private scrollToBottom(): void { private scrollToBottom(): void {
setTimeout(() => { setTimeout(() => {
try { try {
this.scrollContainer.forEach((el: ElementRef) => { this.scrollContainer.forEach((el: ElementRef, index: number) => {
el.nativeElement.scroll({ el.nativeElement.scroll({
top: this.scrollContainer.first.nativeElement.scrollHeight, top: this.scrollContainer.get(index)?.nativeElement.scrollHeight,
left: 0 left: 0
// behavior: 'smooth' // behavior: 'smooth'
}); });