Replace block sorting algorithm

This commit is contained in:
Vanilagy
2025-01-12 15:40:05 +01:00
parent 2dfb595911
commit 764a6dfd52
2 changed files with 29 additions and 133 deletions
+2 -29
View File
@@ -41,37 +41,10 @@ const input = new Metamuxer.Input({
source source
}); });
//console.log(await input.getMimeType()) console.log(await input.getMimeType())
let videoTrack = await input.getPrimaryVideoTrack(); let videoTrack = await input.getPrimaryVideoTrack();
let audioTrack = null && await input.getPrimaryAudioTrack(); let audioTrack = 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(() => {});
*/
if (!(await videoTrack?.canDecode())) { if (!(await videoTrack?.canDecode())) {
videoTrack = null; videoTrack = null;
+27 -104
View File
@@ -429,7 +429,7 @@ export class MatroskaDemuxer extends Demuxer {
} }
if (blockReferencesExist) { if (blockReferencesExist) {
trackData.blocks = sortBlocksTopologically(trackData.blocks); trackData.blocks = sortBlocksByReferences(trackData.blocks);
} }
trackData.presentationTimestamps = trackData.blocks trackData.presentationTimestamps = trackData.blocks
@@ -1496,119 +1496,42 @@ class MatroskaAudioTrackBacking extends MatroskaTrackBacking<EncodedAudioSample>
} }
} }
/** /** Sorts blocks such that referenced blocks come before the blocks that reference them. */
* This function sorts blocks to satisfy block references: If block A is referenced by block B, then block A should const sortBlocksByReferences = (blocks: ClusterBlock[]) => {
* come before block B. The resulting array is one that is in decode order. const timestampToBlock = new Map<number, ClusterBlock>();
*/
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.
const n = blocks.length; for (let i = 0; i < blocks.length; i++) {
const block = blocks[i]!;
// Build timestamp -> index mapping timestampToBlock.set(block.timestamp, block);
const timestampToIndex = new Map<number, number>();
for (let i = 0; i < n; i++) {
timestampToIndex.set(blocks[i]!.timestamp, i);
} }
const outgoing = new Array<Set<number>>(n); const processedBlocks = new Set<ClusterBlock>();
const incoming = new Array<Set<number>>(n); const result: ClusterBlock[] = [];
const remaining = new Set<number>();
for (let i = 0; i < n; i++) { const processBlock = (block: ClusterBlock) => {
outgoing[i] = new Set(); if (processedBlocks.has(block)) {
incoming[i] = new Set(); return;
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<number>(n).fill(-1);
const suffix = new Array<number>(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;
}
} }
// Remove sources // Marking the block as processed here already; prevents this algorithm from dying on cycles
for (let i = 0; i < currentVertices.length; i++) { processedBlocks.add(block);
const index = currentVertices[i]!;
if (remaining.has(index) && incoming[index]!.size === 0) { for (let j = 0; j < block.referencedTimestamps.length; j++) {
prefix[prefixSize++] = index; const timestamp = block.referencedTimestamps[j]!;
// Remove this vertex const otherBlock = timestampToBlock.get(timestamp);
for (const toIndex of outgoing[index]!) { if (!otherBlock) {
incoming[toIndex]!.delete(index); continue;
}
remaining.delete(index);
progress = true;
} }
processBlock(otherBlock);
} }
// If no sinks or sources, remove vertex with maximum Δ result.push(block);
if (!progress && remaining.size > 0) { };
let maxDelta = -Infinity;
let maxDeltaIndex = -1;
for (const index of remaining) { for (let i = 0; i < blocks.length; i++) {
const delta = outgoing[index]!.size - incoming[index]!.size; processBlock(blocks[i]!);
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);
}
} }
const result = new Array<ClusterBlock | null>(n).fill(null); return result;
// 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[];
}; };