diff --git a/dev/player.html b/dev/player.html
index 5cc42fe..08b8977 100644
--- a/dev/player.html
+++ b/dev/player.html
@@ -41,37 +41,10 @@ const input = new Metamuxer.Input({
source
});
-//console.log(await input.getMimeType())
+console.log(await input.getMimeType())
let videoTrack = await input.getPrimaryVideoTrack();
-let audioTrack = null && await input.getPrimaryAudioTrack();
-
-
-const sink = new Metamuxer.EncodedVideoSampleSink(videoTrack);
-console.log(await sink.getSample(4.93))
-await new Promise(() => {});
-
-for await (const sample of sink.samples()) {
- console.log(sample);
- await new Promise(resolve => setTimeout(resolve, 100));
-}
-
-
-
-
-//console.log(await videoTrack.computeDuration());
-//await new Promise(() => {});
-
-/*
-const sink = new Metamuxer.EncodedVideoSampleSink(videoTrack);
-
-for await (const sample of sink.samples()) {
- console.log(sample);
- await new Promise(resolve => setTimeout(resolve, 100));
-}
-
-await new Promise(() => {});
-*/
+let audioTrack = await input.getPrimaryAudioTrack();
if (!(await videoTrack?.canDecode())) {
videoTrack = null;
diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts
index e81e45d..9409d26 100644
--- a/src/matroska/matroska-demuxer.ts
+++ b/src/matroska/matroska-demuxer.ts
@@ -429,7 +429,7 @@ export class MatroskaDemuxer extends Demuxer {
}
if (blockReferencesExist) {
- trackData.blocks = sortBlocksTopologically(trackData.blocks);
+ trackData.blocks = sortBlocksByReferences(trackData.blocks);
}
trackData.presentationTimestamps = trackData.blocks
@@ -1496,119 +1496,42 @@ class MatroskaAudioTrackBacking extends MatroskaTrackBacking
}
}
-/**
- * This function sorts blocks to satisfy block references: If block A is referenced by block B, then block A should
- * come before block B. The resulting array is one that is in decode order.
- */
-const sortBlocksTopologically = (blocks: ClusterBlock[]) => {
- return blocks; // temp
- // Based on "A fast and effective heuristic for the feedback arc set problem" by Peter Eades et al.
+/** Sorts blocks such that referenced blocks come before the blocks that reference them. */
+const sortBlocksByReferences = (blocks: ClusterBlock[]) => {
+ const timestampToBlock = new Map();
- const n = blocks.length;
-
- // Build timestamp -> index mapping
- const timestampToIndex = new Map();
- for (let i = 0; i < n; i++) {
- timestampToIndex.set(blocks[i]!.timestamp, i);
+ for (let i = 0; i < blocks.length; i++) {
+ const block = blocks[i]!;
+ timestampToBlock.set(block.timestamp, block);
}
- const outgoing = new Array>(n);
- const incoming = new Array>(n);
- const remaining = new Set();
+ const processedBlocks = new Set();
+ const result: ClusterBlock[] = [];
- for (let i = 0; i < n; i++) {
- outgoing[i] = new Set();
- incoming[i] = new Set();
- remaining.add(i);
- }
-
- // Build the graph
- for (let i = 0; i < n; i++) {
- const refs = blocks[i]!.referencedTimestamps;
- for (let j = 0; j < refs.length; j++) {
- const referencedIndex = timestampToIndex.get(refs[j]!);
- if (referencedIndex !== undefined) {
- outgoing[referencedIndex]!.add(i);
- incoming[i]!.add(referencedIndex);
- }
- }
- }
-
- // Pre-allocate arrays with known size
- const prefix = new Array(n).fill(-1);
- const suffix = new Array(n).fill(-1);
- let prefixSize = 0;
- let suffixSize = 0;
-
- while (remaining.size > 0) {
- let progress = false;
-
- const currentVertices = Array.from(remaining);
-
- // Remove sinks
- for (let i = 0; i < currentVertices.length; i++) {
- const index = currentVertices[i]!;
- if (outgoing[index]!.size === 0) {
- suffix[suffixSize++] = index;
- // Remove this vertex
- for (const fromIndex of incoming[index]!) {
- outgoing[fromIndex]!.delete(index);
- }
- remaining.delete(index);
- progress = true;
- }
+ const processBlock = (block: ClusterBlock) => {
+ if (processedBlocks.has(block)) {
+ return;
}
- // Remove sources
- for (let i = 0; i < currentVertices.length; i++) {
- const index = currentVertices[i]!;
- if (remaining.has(index) && incoming[index]!.size === 0) {
- prefix[prefixSize++] = index;
- // Remove this vertex
- for (const toIndex of outgoing[index]!) {
- incoming[toIndex]!.delete(index);
- }
- remaining.delete(index);
- progress = true;
+ // Marking the block as processed here already; prevents this algorithm from dying on cycles
+ processedBlocks.add(block);
+
+ for (let j = 0; j < block.referencedTimestamps.length; j++) {
+ const timestamp = block.referencedTimestamps[j]!;
+ const otherBlock = timestampToBlock.get(timestamp);
+ if (!otherBlock) {
+ continue;
}
+
+ processBlock(otherBlock);
}
- // If no sinks or sources, remove vertex with maximum Δ
- if (!progress && remaining.size > 0) {
- let maxDelta = -Infinity;
- let maxDeltaIndex = -1;
+ result.push(block);
+ };
- for (const index of remaining) {
- const delta = outgoing[index]!.size - incoming[index]!.size;
- if (delta > maxDelta) {
- maxDelta = delta;
- maxDeltaIndex = index;
- }
- }
-
- prefix[prefixSize++] = maxDeltaIndex;
- // Remove this vertex
- for (const toIndex of outgoing[maxDeltaIndex]!) {
- incoming[toIndex]!.delete(maxDeltaIndex);
- }
- for (const fromIndex of incoming[maxDeltaIndex]!) {
- outgoing[fromIndex]!.delete(maxDeltaIndex);
- }
- remaining.delete(maxDeltaIndex);
- }
+ for (let i = 0; i < blocks.length; i++) {
+ processBlock(blocks[i]!);
}
- const result = new Array(n).fill(null);
-
- // Copy prefix
- for (let i = 0; i < prefixSize; i++) {
- result[i] = blocks[prefix[i]!]!;
- }
-
- // Copy suffix in reverse
- for (let i = 0; i < suffixSize; i++) {
- result[prefixSize + i] = blocks[suffix[suffixSize - 1 - i]!]!;
- }
-
- return result as ClusterBlock[];
+ return result;
};