[SDK] make stats$ a polled method and add audioLevel$ to audio tracks

This commit is contained in:
Timo K.
2026-10-08 16:36:25 +02:00
parent 5f914b8a01
commit 799619557b
8 changed files with 127 additions and 144 deletions
-52
View File
@@ -1,52 +0,0 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { describe, expect, it, vi } from "vitest";
import { Observable, Subject } from "rxjs";
import { LazyBehavior } from "./LazyBehavior";
describe("LazyBehavior", () => {
it("runs its source only while subscribed", () => {
const teardown = vi.fn();
let starts = 0;
const values$ = new Subject<number>();
const source$ = new Observable<number>((subscriber) => {
starts++;
const subscription = values$.subscribe(subscriber);
return (): void => {
teardown();
subscription.unsubscribe();
};
});
const lazy$ = new LazyBehavior(source$, 0);
expect(starts).toBe(0);
expect(lazy$.value).toBe(0);
const seenByFirst: number[] = [];
const first = lazy$.subscribe((v) => seenByFirst.push(v));
expect(starts).toBe(1);
values$.next(1);
expect(seenByFirst).toEqual([0, 1]);
expect(lazy$.value).toBe(1);
const seenBySecond: number[] = [];
const second = lazy$.subscribe((v) => seenBySecond.push(v));
expect(starts).toBe(1);
expect(seenBySecond).toEqual([1]);
first.unsubscribe();
expect(teardown).not.toHaveBeenCalled();
second.unsubscribe();
expect(teardown).toHaveBeenCalledTimes(1);
values$.next(2);
expect(lazy$.value).toBe(1);
lazy$.subscribe().unsubscribe();
expect(starts).toBe(2);
});
});
-53
View File
@@ -1,53 +0,0 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import {
BehaviorSubject,
type Observable,
type Subscriber,
type Subscription,
} from "rxjs";
/**
* A behavior that only runs its source while someone is subscribed. The source
* is subscribed with the first subscriber and unsubscribed with the last, so a
* value that is expensive to keep current, such as polled statistics, costs
* nothing while nobody looks at it. `value` is whatever the source last
* produced, or the initial value.
*/
export class LazyBehavior<T> extends BehaviorSubject<T> {
private subscribers = 0;
private upstream: Subscription | undefined;
public constructor(
private readonly source$: Observable<T>,
initialValue: T,
) {
super(initialValue);
}
// Every subscription of an Observable goes through this hook, which rxjs
// declares as internal and so leaves out of its types. The base class's
// version replays the current value and registers the subscriber; it is
// wrapped rather than replaced.
protected _subscribe(subscriber: Subscriber<T>): Subscription {
if (this.subscribers++ === 0)
this.upstream = this.source$.subscribe((value) => this.next(value));
const subscription = (
BehaviorSubject.prototype as unknown as {
_subscribe(this: BehaviorSubject<T>, s: Subscriber<T>): Subscription;
}
)._subscribe.call(this, subscriber);
subscription.add(() => {
if (--this.subscribers === 0) {
this.upstream?.unsubscribe();
this.upstream = undefined;
}
});
return subscription;
}
}