Add maximumPageDuration Ogg muxer option (closes #277)

This commit is contained in:
Vanilagy
2026-01-13 13:37:34 +01:00
parent 571fbb3198
commit 2ab1be3834
4 changed files with 106 additions and 17 deletions
+34 -17
View File
@@ -47,12 +47,13 @@ type OggTrackData = {
currentPageData: Uint8Array[];
currentPageSize: number;
currentPageStartsWithFreshPacket: boolean;
currentPageStartTimestampInSamples: number;
};
type Packet = {
data: Uint8Array;
endGranulePosition: number;
timestamp: number;
timestampInSamples: number;
durationInSamples: number;
forcePageFlush: boolean;
};
@@ -132,6 +133,7 @@ export class OggMuxer extends Muxer {
currentPageData: [],
currentPageSize: 27,
currentPageStartsWithFreshPacket: true,
currentPageStartTimestampInSamples: 0,
};
this.queueHeaderPackets(newTrackData, meta);
@@ -199,18 +201,18 @@ export class OggMuxer extends Muxer {
trackData.packetQueue.push({
data: identificationHeader,
endGranulePosition: 0,
timestamp: 0,
timestampInSamples: 0,
durationInSamples: 0,
forcePageFlush: true,
}, {
data: commentHeader,
endGranulePosition: 0,
timestamp: 0,
timestampInSamples: 0,
durationInSamples: 0,
forcePageFlush: false,
}, {
data: setupHeader,
endGranulePosition: 0,
timestamp: 0,
timestampInSamples: 0,
durationInSamples: 0,
forcePageFlush: true, // The last header packet must flush the page
});
@@ -239,13 +241,13 @@ export class OggMuxer extends Muxer {
trackData.packetQueue.push({
data: identificationHeader,
endGranulePosition: 0,
timestamp: 0,
timestampInSamples: 0,
durationInSamples: 0,
forcePageFlush: true,
}, {
data: commentHeader,
endGranulePosition: 0,
timestamp: 0,
timestampInSamples: 0,
durationInSamples: 0,
forcePageFlush: true, // The last header packet must flush the page
});
@@ -275,8 +277,8 @@ export class OggMuxer extends Muxer {
trackData.packetQueue.push({
data: packet.data,
endGranulePosition: trackData.currentTimestampInSamples,
timestamp: currentTimestampInSamples / trackData.internalSampleRate,
timestampInSamples: currentTimestampInSamples,
durationInSamples,
forcePageFlush: false,
});
@@ -338,10 +340,10 @@ export class OggMuxer extends Muxer {
if (
trackData.packetQueue.length > 0
&& trackData.packetQueue[0]!.timestamp < minTimestamp
&& trackData.packetQueue[0]!.timestampInSamples < minTimestamp
) {
trackWithMinTimestamp = trackData;
minTimestamp = trackData.packetQueue[0]!.timestamp;
minTimestamp = trackData.packetQueue[0]!.timestampInSamples;
}
}
@@ -361,6 +363,20 @@ export class OggMuxer extends Muxer {
}
writePacket(trackData: OggTrackData, packet: Packet, isFinalPacket: boolean) {
const packetEndTimestampInSamples = packet.timestampInSamples + packet.durationInSamples;
if (this.format._options.maximumPageDuration !== undefined) {
const maxDurationInSamples = this.format._options.maximumPageDuration * trackData.internalSampleRate;
if (
trackData.currentLacingValues.length > 0
&& packetEndTimestampInSamples - trackData.currentPageStartTimestampInSamples > maxDurationInSamples
) {
// Flush the current page early to avoid exceeding the maximum page duration
this.writePage(trackData, false);
}
}
let remainingLength = packet.data.length;
let dataStartOffset = 0;
let dataOffset = 0;
@@ -401,7 +417,7 @@ export class OggMuxer extends Muxer {
const slice = packet.data.subarray(dataStartOffset);
trackData.currentPageData.push(slice);
trackData.currentPageSize += slice.length;
trackData.currentGranulePosition = packet.endGranulePosition;
trackData.currentGranulePosition = packetEndTimestampInSamples;
if (trackData.currentPageSize >= PAGE_SIZE_TARGET || packet.forcePageFlush) {
this.writePage(trackData, isFinalPacket);
@@ -452,6 +468,7 @@ export class OggMuxer extends Muxer {
trackData.currentPageData.length = 0;
trackData.currentPageSize = 27;
trackData.currentPageStartsWithFreshPacket = true;
trackData.currentPageStartTimestampInSamples = trackData.currentGranulePosition;
if (this.format._options.onPage) {
this.writer.startTrackingWrites();
+12
View File
@@ -725,6 +725,12 @@ export class WavOutputFormat extends OutputFormat {
* @public
*/
export type OggOutputFormatOptions = {
/**
* The maximum duration of each Ogg page, in seconds. This is useful for streaming contexts where more frequent page
* output is desired. By default, pages are only flushed when they exceed a certain size.
*/
maximumPageDuration?: number;
/**
* Will be called for each Ogg page that is written.
*
@@ -749,6 +755,12 @@ export class OggOutputFormat extends OutputFormat {
if (!options || typeof options !== 'object') {
throw new TypeError('options must be an object.');
}
if (
options.maximumPageDuration !== undefined
&& (!Number.isFinite(options.maximumPageDuration) || options.maximumPageDuration <= 0)
) {
throw new TypeError('options.maximumPageDuration, when provided, must be a positive number.');
}
if (options.onPage !== undefined && typeof options.onPage !== 'function') {
throw new TypeError('options.onPage, when provided, must be a function.');
}