diff --git a/src/debugger/chronicle.ts b/src/debugger/chronicle.ts index 0845c696..7fccacce 100644 --- a/src/debugger/chronicle.ts +++ b/src/debugger/chronicle.ts @@ -46,6 +46,7 @@ type CurrentLatency = CreateMetric<"current-latency", number> type CurrentUrl = CreateMetric<"current-url", string> type Duration = CreateMetric<"duration", number> type FramesDropped = CreateMetric<"frames-dropped", number> +type FramesTotal = CreateMetric<"frames-total", number> type InitialPlaybackTime = CreateMetric<"initial-playback-time", [time: number, timeline: Timeline]> type MediaElementEnded = CreateMetric<"ended", HTMLMediaElement["ended"]> type MediaElementPaused = CreateMetric<"paused", HTMLMediaElement["paused"]> @@ -73,6 +74,7 @@ export type Metric = | CurrentUrl | Duration | FramesDropped + | FramesTotal | InitialPlaybackTime | MediaElementEnded | MediaElementPaused diff --git a/src/debugger/debugviewcontroller.test.ts b/src/debugger/debugviewcontroller.test.ts index 8ff54c7c..6425d4d8 100644 --- a/src/debugger/debugviewcontroller.test.ts +++ b/src/debugger/debugviewcontroller.test.ts @@ -20,7 +20,7 @@ describe("Debug View", () => { controller.showView() chronicle.appendMetric("buffer-length", 0) - chronicle.appendMetric("frames-dropped", 4) + chronicle.appendMetric("auto-resume", 4) chronicle.appendMetric("duration", 30) controller.addEntries(chronicle.retrieve()) @@ -31,7 +31,7 @@ describe("Debug View", () => { expect.objectContaining({ static: [ { id: "buffer-length", key: "buffer length", value: 0 }, - { id: "frames-dropped", key: "frames dropped", value: 4 }, + { id: "auto-resume", key: "auto resume", value: 4 }, { id: "duration", key: "duration", value: 30 }, ], }) @@ -254,22 +254,22 @@ describe("Debug View", () => { const chronicle = new Chronicle() - chronicle.appendMetric("frames-dropped", 0) + chronicle.appendMetric("auto-resume", 0) jest.advanceTimersByTime(500) - chronicle.appendMetric("frames-dropped", 1) + chronicle.appendMetric("auto-resume", 1) jest.advanceTimersByTime(4500) - chronicle.appendMetric("frames-dropped", 4) + chronicle.appendMetric("auto-resume", 4) controller.addEntries(chronicle.retrieve()) jest.advanceTimersToNextTimer() expect(DebugView.render).toHaveBeenCalledWith( - expect.objectContaining({ static: [{ id: "frames-dropped", key: "frames dropped", value: 4 }] }) + expect.objectContaining({ static: [{ id: "auto-resume", key: "auto resume", value: 4 }] }) ) }) diff --git a/src/debugger/debugviewcontroller.ts b/src/debugger/debugviewcontroller.ts index 0287528c..99078263 100644 --- a/src/debugger/debugviewcontroller.ts +++ b/src/debugger/debugviewcontroller.ts @@ -67,10 +67,22 @@ type MaxBitrate = { data: Record } +type FrameKind = Extends + +type Frames = { + category: "union" + kind: "frames" + data: { + total?: number + dropped?: number + } +} + type DynamicEntry = TimestampedMessage | TimestampedTrace | Timestamp type StaticEntry = - | Exclude> + | Exclude> + | Timestamped | Timestamped | Timestamped | Timestamped @@ -107,6 +119,32 @@ class DebugViewController { private dynamicEntries: DynamicEntry[] = [] private latestMetricByKey: Partial> = {} + private isFrameStat(metric: TimestampedMetric): metric is Timestamped> { + const { kind } = metric + return kind === "frames-dropped" || kind === "frames-total" + } + + private mergeFrameStat(entry: Timestamped>): Timestamped { + const prevEntry: Frames = + this.latestMetricByKey.frames == null + ? { category: "union", kind: "frames", data: {} } + : (this.latestMetricByKey.frames as Frames) + + const { sessionTime, currentElementTime, kind: metricKind, data: metricData } = entry + + const keyForKind: Record = { + "frames-dropped": "dropped", + "frames-total": "total", + } + + return { + ...prevEntry, + sessionTime, + currentElementTime, + data: { ...prevEntry.data, [keyForKind[metricKind]]: metricData }, + } + } + private isMediaState(metric: TimestampedMetric): metric is Timestamped> { const { kind } = metric const mediaStateMetrics = ["ended", "paused", "playback-rate", "ready-state", "seeking"] @@ -219,6 +257,11 @@ class DebugViewController { switch (category) { case EntryCategory.METRIC: + if (this.isFrameStat(entry)) { + this.cacheStaticEntry(this.mergeFrameStat(entry)) + return + } + if (this.isMediaState(entry)) { this.cacheStaticEntry(this.mergeMediaState(entry)) return @@ -477,6 +520,14 @@ class DebugViewController { return `${bitratePart} kbps` } + if (kind === "frames") { + if (data.total == null) { + return null + } + + return `${(data.dropped ?? (0 / data.total) * 100).toFixed(2)}% dropped (${data.dropped}/${data.total})` + } + return data.join(", ") }