From a9cec387d4f5b901bd8dfc631daf903ff56c666d Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Sun, 10 Aug 2025 19:55:21 +0200 Subject: [PATCH] Extract insertSorted function --- src/isobmff/isobmff-demuxer.ts | 20 +++++--------------- src/matroska/matroska-demuxer.ts | 23 ++++------------------- src/media-sink.ts | 15 +++------------ src/misc.ts | 6 ++++++ 4 files changed, 18 insertions(+), 46 deletions(-) diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts index c0b3a2d..20bcf42 100644 --- a/src/isobmff/isobmff-demuxer.ts +++ b/src/isobmff/isobmff-demuxer.ts @@ -55,6 +55,7 @@ import { roundToMultiple, normalizeRotation, Bitstream, + insertSorted, } from '../misc'; import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; import { Reader } from '../reader'; @@ -1624,12 +1625,7 @@ export class IsobmffDemuxer extends Demuxer { this.readContiguousBoxes(boxInfo.contentSize); - const insertionIndex = binarySearchLessOrEqual( - this.fragments, - this.currentFragment.moofOffset, - x => x.moofOffset, - ); - this.fragments.splice(insertionIndex + 1, 0, this.currentFragment); + insertSorted(this.fragments, this.currentFragment, x => x.moofOffset); // Compute the byte range of the sample data in this fragment, so we can load the whole fragment at once for (const [, trackData] of this.currentFragment.trackData) { @@ -1661,21 +1657,15 @@ export class IsobmffDemuxer extends Demuxer { if (trackData) { // We know there is sample data for this track in this fragment, so let's add it to the // track's fragments: - const insertionIndex = binarySearchLessOrEqual( - this.currentTrack.fragments, - this.currentFragment.moofOffset, - x => x.moofOffset, - ); - this.currentTrack.fragments.splice(insertionIndex + 1, 0, this.currentFragment); + insertSorted(this.currentTrack.fragments, this.currentFragment, x => x.moofOffset); const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null; if (hasKeyFrame) { - const insertionIndex = binarySearchLessOrEqual( + insertSorted( this.currentTrack.fragmentsWithKeyFrame, - this.currentFragment.moofOffset, + this.currentFragment, x => x.moofOffset, ); - this.currentTrack.fragmentsWithKeyFrame.splice(insertionIndex + 1, 0, this.currentFragment); } const { currentFragmentState } = this.currentTrack; diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index 8d61174..adafd0b 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -38,6 +38,7 @@ import { binarySearchLessOrEqual, COLOR_PRIMARIES_MAP_INVERSE, findLastIndex, + insertSorted, isIso639Dash2LanguageCode, last, MATRIX_COEFFICIENTS_MAP_INVERSE, @@ -570,32 +571,16 @@ export class MatroskaDemuxer extends Demuxer { trackData.endTimestamp = lastBlock.timestamp + lastBlock.duration; if (track) { - const insertionIndex = binarySearchLessOrEqual( - track.clusters, - cluster.elementStartPos, - x => x.elementStartPos, - ); - track.clusters.splice(insertionIndex + 1, 0, cluster); + insertSorted(track.clusters, cluster, x => x.elementStartPos); const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null; if (hasKeyFrame) { - const insertionIndex = binarySearchLessOrEqual( - track.clustersWithKeyFrame, - cluster.elementStartPos, - x => x.elementStartPos, - ); - track.clustersWithKeyFrame.splice(insertionIndex + 1, 0, cluster); + insertSorted(track.clustersWithKeyFrame, cluster, x => x.elementStartPos); } } } - const insertionIndex = binarySearchLessOrEqual( - segment.clusters, - elementStartPos, - x => x.elementStartPos, - ); - segment.clusters.splice(insertionIndex + 1, 0, cluster); - + insertSorted(segment.clusters, cluster, x => x.elementStartPos); this.currentCluster = null; return cluster; diff --git a/src/media-sink.ts b/src/media-sink.ts index 09404a4..df97305 100644 --- a/src/media-sink.ts +++ b/src/media-sink.ts @@ -17,6 +17,7 @@ import { CallSerializer, getInt24, getUint24, + insertSorted, isSafari, last, mapAsyncGenerator, @@ -799,12 +800,7 @@ class VideoDecoderWrapper extends DecoderWrapper { this.sampleQueue.length = 0; } - const insertionIndex = binarySearchLessOrEqual( - this.sampleQueue, - sample.timestamp, - x => x.timestamp, - ); - this.sampleQueue.splice(insertionIndex + 1, 0, sample); + insertSorted(this.sampleQueue, sample, x => x.timestamp); } else { // Assign it the next earliest timestamp from the input. We do this because browsers, by spec, are // required to emit decoded frames in presentation order *while* retaining the timestamp of their @@ -858,12 +854,7 @@ class VideoDecoderWrapper extends DecoderWrapper { assert(this.decoder); if (!isSafari()) { - const insertionIndex = binarySearchLessOrEqual( - this.inputTimestamps, - packet.timestamp, - x => x, - ); - this.inputTimestamps.splice(insertionIndex + 1, 0, packet.timestamp); + insertSorted(this.inputTimestamps, packet.timestamp, x => x); } this.decoder.decode(packet.toEncodedVideoChunk()); diff --git a/src/misc.ts b/src/misc.ts index 6edc9ad..079ab5e 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -292,6 +292,12 @@ export const binarySearchLessOrEqual = (arr: T[], key: number, valueGetter: ( return ans; }; +/** Assumes the array is already sorted. */ +export const insertSorted = (arr: T[], item: T, valueGetter: (x: T) => number) => { + const insertionIndex = binarySearchLessOrEqual(arr, valueGetter(item), valueGetter); + arr.splice(insertionIndex + 1, 0, item); // This even behaves correctly for the -1 case +}; + export const promiseWithResolvers = () => { let resolve: (value: T) => void; let reject: (reason: unknown) => void;