Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Tweak voice broadcast chunk decoding (#9713)
Browse files Browse the repository at this point in the history
  • Loading branch information
weeman1337 authored Dec 7, 2022
1 parent 8ced72d commit 254815c
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 27 deletions.
63 changes: 36 additions & 27 deletions src/voice-broadcast/models/VoiceBroadcastPlayback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,14 @@ export class VoiceBroadcastPlayback
return false;
}

if (!event.getId() && !event.getTxnId()) {
// skip events without id and txn id
return false;
}

this.chunkEvents.addEvent(event);
this.setDuration(this.chunkEvents.getLength());

if (this.getState() !== VoiceBroadcastPlaybackState.Stopped) {
await this.enqueueChunk(event);
}

if (this.getState() === VoiceBroadcastPlaybackState.Buffering) {
await this.start();
this.updateLiveness();
Expand Down Expand Up @@ -183,18 +184,7 @@ export class VoiceBroadcastPlayback
}
};

private async enqueueChunks(): Promise<void> {
const promises = this.chunkEvents.getEvents().reduce((promises, event: MatrixEvent) => {
if (!this.playbacks.has(event.getId() || "")) {
promises.push(this.enqueueChunk(event));
}
return promises;
}, [] as Promise<void>[]);

await Promise.all(promises);
}

private async enqueueChunk(chunkEvent: MatrixEvent): Promise<void> {
private async loadPlayback(chunkEvent: MatrixEvent): Promise<void> {
const eventId = chunkEvent.getId();

if (!eventId) {
Expand All @@ -215,6 +205,14 @@ export class VoiceBroadcastPlayback
});
}

private unloadPlayback(event: MatrixEvent): void {
const playback = this.playbacks.get(event.getId()!);
if (!playback) return;

playback.destroy();
this.playbacks.delete(event.getId()!);
}

private onPlaybackPositionUpdate = (
event: MatrixEvent,
position: number,
Expand Down Expand Up @@ -261,6 +259,7 @@ export class VoiceBroadcastPlayback
if (newState !== PlaybackState.Stopped) return;

await this.playNext();
this.unloadPlayback(event);
};

private async playNext(): Promise<void> {
Expand All @@ -283,27 +282,41 @@ export class VoiceBroadcastPlayback
private async playEvent(event: MatrixEvent): Promise<void> {
this.setState(VoiceBroadcastPlaybackState.Playing);
this.currentlyPlaying = event;
await this.getPlaybackForEvent(event)?.play();
const playback = await this.getOrLoadPlaybackForEvent(event);
playback?.play();
}

private getPlaybackForEvent(event: MatrixEvent): Playback | undefined {
private async getOrLoadPlaybackForEvent(event: MatrixEvent): Promise<Playback | undefined> {
const eventId = event.getId();

if (!eventId) {
logger.warn("event without id occurred");
return;
}

if (!this.playbacks.has(eventId)) {
await this.loadPlayback(event);
}

const playback = this.playbacks.get(eventId);

if (!playback) {
// logging error, because this should not happen
logger.warn("unable to find playback for event", event);
}

// try to load the playback for the next event for a smooth(er) playback
const nextEvent = this.chunkEvents.getNext(event);
if (nextEvent) this.loadPlayback(nextEvent);

return playback;
}

private getCurrentPlayback(): Playback | undefined {
if (!this.currentlyPlaying) return;
return this.playbacks.get(this.currentlyPlaying.getId()!);
}

public getLiveness(): VoiceBroadcastLiveness {
return this.liveness;
}
Expand Down Expand Up @@ -365,11 +378,8 @@ export class VoiceBroadcastPlayback
return;
}

const currentPlayback = this.currentlyPlaying
? this.getPlaybackForEvent(this.currentlyPlaying)
: null;

const skipToPlayback = this.getPlaybackForEvent(event);
const currentPlayback = this.getCurrentPlayback();
const skipToPlayback = await this.getOrLoadPlaybackForEvent(event);

if (!skipToPlayback) {
logger.warn("voice broadcast chunk to skip to not found", event);
Expand All @@ -396,14 +406,13 @@ export class VoiceBroadcastPlayback
}

public async start(): Promise<void> {
await this.enqueueChunks();
const chunkEvents = this.chunkEvents.getEvents();

const toPlay = this.getInfoState() === VoiceBroadcastInfoState.Stopped
? chunkEvents[0] // start at the beginning for an ended voice broadcast
: chunkEvents[chunkEvents.length - 1]; // start at the current chunk for an ongoing voice broadcast

if (this.playbacks.has(toPlay?.getId() || "")) {
if (toPlay) {
return this.playEvent(toPlay);
}

Expand All @@ -422,7 +431,7 @@ export class VoiceBroadcastPlayback

this.setState(VoiceBroadcastPlaybackState.Paused);
if (!this.currentlyPlaying) return;
this.getPlaybackForEvent(this.currentlyPlaying)?.pause();
this.getCurrentPlayback()?.pause();
}

public resume(): void {
Expand All @@ -433,7 +442,7 @@ export class VoiceBroadcastPlayback
}

this.setState(VoiceBroadcastPlaybackState.Playing);
this.getPlaybackForEvent(this.currentlyPlaying)?.play();
this.getCurrentPlayback()?.play();
}

/**
Expand Down
3 changes: 3 additions & 0 deletions test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ describe("VoiceBroadcastPlayback", () => {
});

it("should play until the end", () => {
// assert first chunk was unloaded
expect(chunk1Playback.destroy).toHaveBeenCalled();

// assert that the second chunk is being played
expect(chunk2Playback.play).toHaveBeenCalled();

Expand Down

0 comments on commit 254815c

Please sign in to comment.