From 51b9de1c6f7c417fd69df6df2961706bc89a8e66 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:31:33 +0100 Subject: [PATCH] Move PacketReader elsewhere --- src/adts/adts-demuxer.ts | 3 +- src/cursors.ts | 252 +-------------------------- src/flac/flac-demuxer.ts | 3 +- src/index.ts | 2 +- src/input-track.ts | 4 +- src/isobmff/isobmff-demuxer.ts | 3 +- src/matroska/matroska-demuxer.ts | 3 +- src/mp3/mp3-demuxer.ts | 3 +- src/ogg/ogg-demuxer.ts | 3 +- src/packet.ts | 220 ++++++++++++++++++++++- src/wave/wave-demuxer.ts | 3 +- test/browser/sample-cursor.test.ts | 28 --- test/browser/transparency.test.ts | 3 +- test/node/annex-b-conversion.test.ts | 2 +- test/node/demux.test.ts | 3 +- test/node/flac.test.ts | 3 +- test/node/packet-reading.test.ts | 3 +- 17 files changed, 244 insertions(+), 297 deletions(-) diff --git a/src/adts/adts-demuxer.ts b/src/adts/adts-demuxer.ts index cb7a669..be0c697 100644 --- a/src/adts/adts-demuxer.ts +++ b/src/adts/adts-demuxer.ts @@ -19,11 +19,10 @@ import { ResultValue, UNDETERMINED_LANGUAGE, } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { readBytes, Reader } from '../reader'; import { DEFAULT_TRACK_DISPOSITION } from '../metadata'; import { FrameHeader, MAX_FRAME_HEADER_SIZE, MIN_FRAME_HEADER_SIZE, readFrameHeader } from './adts-reader'; -import { PacketRetrievalOptions } from '../cursors'; const SAMPLES_PER_AAC_FRAME = 1024; diff --git a/src/cursors.ts b/src/cursors.ts index 1dc7f71..c8218b7 100644 --- a/src/cursors.ts +++ b/src/cursors.ts @@ -8,7 +8,6 @@ import { PCM_AUDIO_CODECS } from './codec'; import { AudioDecoderWrapper, DecoderWrapper, PcmAudioDecoderWrapper, VideoDecoderWrapper } from './decode'; -import { InputDisposedError } from './input'; import { InputAudioTrack, InputTrack, InputVideoTrack } from './input-track'; import { assert, @@ -24,230 +23,19 @@ import { ResultValue, Rotation, AsyncGate, - isNumber, MaybeRelevantPromise, } from './misc'; -import { EncodedPacket } from './packet'; +import { + EncodedPacket, + PacketReader, + PacketRetrievalOptions, + validatePacketRetrievalOptions, + validateTimestamp, +} from './packet'; import { AudioSample, clampCropRectangle, CropRectangle, validateCropRectangle, VideoSample } from './sample'; polyfillSymbolDispose(); -/** - * Additional options for controlling packet retrieval. - * @group Media sinks - * @public - */ -export type PacketRetrievalOptions = { - /** - * When set to `true`, only packet metadata (like timestamp) will be retrieved - the actual packet data will not - * be loaded. - */ - metadataOnly?: boolean; - - /** - * When set to true, key packets will be verified upon retrieval by looking into the packet's bitstream. - * If not enabled, the packet types will be determined solely by what's stored in the containing file and may be - * incorrect, potentially leading to decoder errors. Since determining a packet's actual type requires looking into - * its data, this option cannot be enabled together with `metadataOnly`. - */ - verifyKeyPackets?: boolean; -}; - -export const validatePacketRetrievalOptions = (options: PacketRetrievalOptions) => { - if (!options || typeof options !== 'object') { - throw new TypeError('options must be an object.'); - } - if (options.metadataOnly !== undefined && typeof options.metadataOnly !== 'boolean') { - throw new TypeError('options.metadataOnly, when defined, must be a boolean.'); - } - if (options.verifyKeyPackets !== undefined && typeof options.verifyKeyPackets !== 'boolean') { - throw new TypeError('options.verifyKeyPackets, when defined, must be a boolean.'); - } - if (options.verifyKeyPackets && options.metadataOnly) { - throw new TypeError('options.verifyKeyPackets and options.metadataOnly cannot be enabled together.'); - } -}; - -export const validateTimestamp = (timestamp: number) => { - if (!isNumber(timestamp)) { - throw new TypeError('timestamp must be a number.'); // It can be non-finite, that's fine - } -}; - -export class PacketReader { - track: T; - - constructor(track: T) { - if (!(track instanceof InputTrack)) { - throw new TypeError('track must be an InputTrack.'); - } - this.track = track; - } - - private _maybeVerifyPacketType( - packet: EncodedPacket | null, - options: PacketRetrievalOptions, - ): MaybePromise { - if (!options.verifyKeyPackets || !packet || packet.type === 'delta') { - return packet; - } - - return this.track.determinePacketType(packet).then((determinedType) => { - if (determinedType) { - // @ts-expect-error Technically readonly - packet.type = determinedType; - } - - return packet; - }); - } - - getFirst(options: PacketRetrievalOptions = {}): MaybePromise { - validatePacketRetrievalOptions(options); - - if (this.track.input._disposed) { - throw new InputDisposedError(); - } - - const result = new ResultValue(); - const promise = this.track._backing.getFirstPacket(result, options); - - if (result.pending) { - return promise.then(() => this._maybeVerifyPacketType(result.value, options)); - } else { - return this._maybeVerifyPacketType(result.value, options); - } - } - - getAt(timestamp: number, options: PacketRetrievalOptions = {}): MaybePromise { - validateTimestamp(timestamp); - validatePacketRetrievalOptions(options); - - if (this.track.input._disposed) { - throw new InputDisposedError(); - } - - const result = new ResultValue(); - const promise = this.track._backing.getPacket(result, timestamp, options); - - if (result.pending) { - return promise.then(() => this._maybeVerifyPacketType(result.value, options)); - } else { - return this._maybeVerifyPacketType(result.value, options); - } - } - - getKeyAt(timestamp: number, options: PacketRetrievalOptions = {}): MaybePromise { - validateTimestamp(timestamp); - validatePacketRetrievalOptions(options); - - if (this.track.input._disposed) { - throw new InputDisposedError(); - } - - if (options.verifyKeyPackets) { - return this._readKeyAtVerified(timestamp, options); - } - - const result = new ResultValue(); - const promise = this.track._backing.getKeyPacket(result, timestamp, options); - - if (result.pending) { - return promise.then(() => result.value); - } else { - return result.value; - } - } - - private async _readKeyAtVerified( - timestamp: number, - options: PacketRetrievalOptions, - ): Promise { - const result = new ResultValue(); - const promise = this.track._backing.getKeyPacket(result, timestamp, options); - if (result.pending) await promise; - - const packet = result.value; - if (!packet) { - return null; - } - - const determinedType = await this.track.determinePacketType(packet); - if (determinedType === 'delta') { - // Try returning the previous key packet (in hopes that it's actually a key packet) - return this._readKeyAtVerified(packet.timestamp - 1 / this.track.timeResolution, options); - } - - return packet; - } - - getNext(from: EncodedPacket, options: PacketRetrievalOptions = {}): MaybePromise { - if (!(from instanceof EncodedPacket)) { - throw new TypeError('from must be an EncodedPacket.'); - } - validatePacketRetrievalOptions(options); - - if (this.track.input._disposed) { - throw new InputDisposedError(); - } - - const result = new ResultValue(); - const promise = this.track._backing.getNextPacket(result, from, options); - - if (result.pending) { - return promise.then(() => this._maybeVerifyPacketType(result.value, options)); - } else { - return this._maybeVerifyPacketType(result.value, options); - } - } - - getNextKey(from: EncodedPacket, options: PacketRetrievalOptions = {}): MaybePromise { - if (!(from instanceof EncodedPacket)) { - throw new TypeError('from must be an EncodedPacket.'); - } - validatePacketRetrievalOptions(options); - - if (this.track.input._disposed) { - throw new InputDisposedError(); - } - - if (options.verifyKeyPackets) { - return this._getNextKeyVerified(from, options); - } - - const result = new ResultValue(); - const promise = this.track._backing.getNextKeyPacket(result, from, options); - - if (result.pending) { - return promise.then(() => result.value); - } else { - return result.value; - } - } - - private async _getNextKeyVerified( - from: EncodedPacket, - options: PacketRetrievalOptions, - ): Promise { - const result = new ResultValue(); - const promise = this.track._backing.getNextKeyPacket(result, from, options); - if (result.pending) await promise; - - const nextPacket = result.value; - if (!nextPacket) { - return null; - } - - const determinedType = await this.track.determinePacketType(nextPacket); - if (determinedType === 'delta') { - // Try returning the next key packet (in hopes that it's actually a key packet) - return this._getNextKeyVerified(nextPacket, options); - } - - return nextPacket; - } -} - export class PacketCursor { track: T; current: EncodedPacket | null = null; @@ -1640,29 +1428,3 @@ export const canvasTransformer = ( return new WrappedCanvas(canvas, sample.timestamp, sample.duration); }; }; - -/** - * An AudioBuffer with additional timing information (timestamp & duration). - * @public - */ -export type WrappedAudioBuffer = { - /** An AudioBuffer. */ - buffer: AudioBuffer; - /** The timestamp of the corresponding audio sample, in seconds. */ - timestamp: number; - /** The duration of the corresponding audio sample, in seconds. */ - duration: number; -}; - -export const audioBufferTransformer = (): SampleTransformer => { - return (sample) => { - const result: WrappedAudioBuffer = { - buffer: sample.toAudioBuffer(), - timestamp: sample.timestamp, - duration: sample.duration, - }; - - sample.close(); - return result; - }; -}; diff --git a/src/flac/flac-demuxer.ts b/src/flac/flac-demuxer.ts index ac2fb15..678b20f 100644 --- a/src/flac/flac-demuxer.ts +++ b/src/flac/flac-demuxer.ts @@ -20,7 +20,7 @@ import { textDecoder, UNDETERMINED_LANGUAGE, } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { FileSlice, readBytes, @@ -38,7 +38,6 @@ import { readSampleRate, getSampleRateOrUncommon, } from './flac-misc'; -import { PacketRetrievalOptions } from '../cursors'; type FlacAudioInfo = { numberOfChannels: number; diff --git a/src/index.ts b/src/index.ts index 026e44b..fcd25b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -161,6 +161,7 @@ export { EncodedPacket, EncodedPacketSideData, PacketType, + PacketReader, } from './packet'; export { AudioSample, @@ -174,7 +175,6 @@ export { VIDEO_SAMPLE_PIXEL_FORMATS, } from './sample'; export { - PacketReader, PacketCursor, SampleCursor, VideoSampleCursor, diff --git a/src/input-track.ts b/src/input-track.ts index 45ad642..2cb6e48 100644 --- a/src/input-track.ts +++ b/src/input-track.ts @@ -12,9 +12,9 @@ import { customAudioDecoders, customVideoDecoders } from './custom-coder'; import { Input } from './input'; import { assert, MaybeRelevantPromise, ResultValue, Rotation } from './misc'; import { TrackType } from './output'; -import { EncodedPacket, PacketType } from './packet'; +import { EncodedPacket, PacketRetrievalOptions, PacketType } from './packet'; import { TrackDisposition } from './metadata'; -import { PacketCursor, PacketRetrievalOptions } from './cursors'; +import { PacketCursor } from './cursors'; /** * Contains aggregate statistics about the encoded packets of a track. diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts index 4b981c2..1f90e56 100644 --- a/src/isobmff/isobmff-demuxer.ts +++ b/src/isobmff/isobmff-demuxer.ts @@ -60,7 +60,7 @@ import { ResultValue, MaybeRelevantPromise, } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { buildIsobmffMimeType } from './isobmff-misc'; import { MAX_BOX_HEADER_SIZE, @@ -88,7 +88,6 @@ import { readAscii, } from '../reader'; import { DEFAULT_TRACK_DISPOSITION, MetadataTags, RichImageData, TrackDisposition } from '../metadata'; -import { PacketRetrievalOptions } from '../cursors'; type InternalTrack = { id: number; diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index 1ceef16..4dfa40f 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -48,7 +48,7 @@ import { TRANSFER_CHARACTERISTICS_MAP_INVERSE, UNDETERMINED_LANGUAGE, } from '../misc'; -import { EncodedPacket, EncodedPacketSideData, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, EncodedPacketSideData, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { assertDefinedSize, CODEC_STRING_MAP, @@ -70,7 +70,6 @@ import { } from './ebml'; import { buildMatroskaMimeType } from './matroska-misc'; import { FileSlice, readBytes, Reader, readI16Be, readU8 } from '../reader'; -import { PacketRetrievalOptions } from '../cursors'; type Segment = { seekHeadSeen: boolean; diff --git a/src/mp3/mp3-demuxer.ts b/src/mp3/mp3-demuxer.ts index 1809eb3..98a27af 100644 --- a/src/mp3/mp3-demuxer.ts +++ b/src/mp3/mp3-demuxer.ts @@ -19,7 +19,7 @@ import { ResultValue, UNDETERMINED_LANGUAGE, } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { FrameHeader, getXingOffset, INFO, XING } from '../../shared/mp3-misc'; import { ID3_V1_TAG_SIZE, @@ -30,7 +30,6 @@ import { } from '../id3'; import { readNextFrameHeader } from './mp3-reader'; import { readAscii, readBytes, Reader, readU32Be } from '../reader'; -import { PacketRetrievalOptions } from '../cursors'; type Sample = { timestamp: number; diff --git a/src/ogg/ogg-demuxer.ts b/src/ogg/ogg-demuxer.ts index 64e5b65..17a07d3 100644 --- a/src/ogg/ogg-demuxer.ts +++ b/src/ogg/ogg-demuxer.ts @@ -8,7 +8,6 @@ import { OPUS_SAMPLE_RATE } from '../codec'; import { parseModesFromVorbisSetupPacket, parseOpusIdentificationHeader, readVorbisComments } from '../codec-data'; -import { PacketRetrievalOptions } from '../cursors'; import { Demuxer } from '../demuxer'; import { Input } from '../input'; import { InputAudioTrack, InputAudioTrackBacking } from '../input-track'; @@ -25,7 +24,7 @@ import { toDataView, UNDETERMINED_LANGUAGE, } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { readBytes, Reader } from '../reader'; import { buildOggMimeType, computeOggPageCrc, extractSampleMetadata, OggCodecInfo } from './ogg-misc'; import { diff --git a/src/packet.ts b/src/packet.ts index 1b7d9ed..3ccd6ec 100644 --- a/src/packet.ts +++ b/src/packet.ts @@ -6,7 +6,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { SECOND_TO_MICROSECOND_FACTOR } from './misc'; +import { InputDisposedError } from './input'; +import { InputTrack } from './input-track'; +import { isNumber, MaybePromise, ResultValue, SECOND_TO_MICROSECOND_FACTOR } from './misc'; export const PLACEHOLDER_DATA = /* #__PURE__ */ new Uint8Array(0); @@ -267,3 +269,219 @@ export class EncodedPacket { ); } } + +/** + * Additional options for controlling packet retrieval. + * @group Media sinks + * @public + */ +export type PacketRetrievalOptions = { + /** + * When set to `true`, only packet metadata (like timestamp) will be retrieved - the actual packet data will not + * be loaded. + */ + metadataOnly?: boolean; + + /** + * When set to true, key packets will be verified upon retrieval by looking into the packet's bitstream. + * If not enabled, the packet types will be determined solely by what's stored in the containing file and may be + * incorrect, potentially leading to decoder errors. Since determining a packet's actual type requires looking into + * its data, this option cannot be enabled together with `metadataOnly`. + */ + verifyKeyPackets?: boolean; +}; + +export const validatePacketRetrievalOptions = (options: PacketRetrievalOptions) => { + if (!options || typeof options !== 'object') { + throw new TypeError('options must be an object.'); + } + if (options.metadataOnly !== undefined && typeof options.metadataOnly !== 'boolean') { + throw new TypeError('options.metadataOnly, when defined, must be a boolean.'); + } + if (options.verifyKeyPackets !== undefined && typeof options.verifyKeyPackets !== 'boolean') { + throw new TypeError('options.verifyKeyPackets, when defined, must be a boolean.'); + } + if (options.verifyKeyPackets && options.metadataOnly) { + throw new TypeError('options.verifyKeyPackets and options.metadataOnly cannot be enabled together.'); + } +}; + +export const validateTimestamp = (timestamp: number) => { + if (!isNumber(timestamp)) { + throw new TypeError('timestamp must be a number.'); // It can be non-finite, that's fine + } +}; + +export class PacketReader { + track: T; + + constructor(track: T) { + if (!(track instanceof InputTrack)) { + throw new TypeError('track must be an InputTrack.'); + } + this.track = track; + } + + private _maybeVerifyPacketType( + packet: EncodedPacket | null, + options: PacketRetrievalOptions, + ): MaybePromise { + if (!options.verifyKeyPackets || !packet || packet.type === 'delta') { + return packet; + } + + return this.track.determinePacketType(packet).then((determinedType) => { + if (determinedType) { + // @ts-expect-error Technically readonly + packet.type = determinedType; + } + + return packet; + }); + } + + getFirst(options: PacketRetrievalOptions = {}): MaybePromise { + validatePacketRetrievalOptions(options); + + if (this.track.input._disposed) { + throw new InputDisposedError(); + } + + const result = new ResultValue(); + const promise = this.track._backing.getFirstPacket(result, options); + + if (result.pending) { + return promise.then(() => this._maybeVerifyPacketType(result.value, options)); + } else { + return this._maybeVerifyPacketType(result.value, options); + } + } + + getAt(timestamp: number, options: PacketRetrievalOptions = {}): MaybePromise { + validateTimestamp(timestamp); + validatePacketRetrievalOptions(options); + + if (this.track.input._disposed) { + throw new InputDisposedError(); + } + + const result = new ResultValue(); + const promise = this.track._backing.getPacket(result, timestamp, options); + + if (result.pending) { + return promise.then(() => this._maybeVerifyPacketType(result.value, options)); + } else { + return this._maybeVerifyPacketType(result.value, options); + } + } + + getKeyAt(timestamp: number, options: PacketRetrievalOptions = {}): MaybePromise { + validateTimestamp(timestamp); + validatePacketRetrievalOptions(options); + + if (this.track.input._disposed) { + throw new InputDisposedError(); + } + + if (options.verifyKeyPackets) { + return this._readKeyAtVerified(timestamp, options); + } + + const result = new ResultValue(); + const promise = this.track._backing.getKeyPacket(result, timestamp, options); + + if (result.pending) { + return promise.then(() => result.value); + } else { + return result.value; + } + } + + private async _readKeyAtVerified( + timestamp: number, + options: PacketRetrievalOptions, + ): Promise { + const result = new ResultValue(); + const promise = this.track._backing.getKeyPacket(result, timestamp, options); + if (result.pending) await promise; + + const packet = result.value; + if (!packet) { + return null; + } + + const determinedType = await this.track.determinePacketType(packet); + if (determinedType === 'delta') { + // Try returning the previous key packet (in hopes that it's actually a key packet) + return this._readKeyAtVerified(packet.timestamp - 1 / this.track.timeResolution, options); + } + + return packet; + } + + getNext(from: EncodedPacket, options: PacketRetrievalOptions = {}): MaybePromise { + if (!(from instanceof EncodedPacket)) { + throw new TypeError('from must be an EncodedPacket.'); + } + validatePacketRetrievalOptions(options); + + if (this.track.input._disposed) { + throw new InputDisposedError(); + } + + const result = new ResultValue(); + const promise = this.track._backing.getNextPacket(result, from, options); + + if (result.pending) { + return promise.then(() => this._maybeVerifyPacketType(result.value, options)); + } else { + return this._maybeVerifyPacketType(result.value, options); + } + } + + getNextKey(from: EncodedPacket, options: PacketRetrievalOptions = {}): MaybePromise { + if (!(from instanceof EncodedPacket)) { + throw new TypeError('from must be an EncodedPacket.'); + } + validatePacketRetrievalOptions(options); + + if (this.track.input._disposed) { + throw new InputDisposedError(); + } + + if (options.verifyKeyPackets) { + return this._getNextKeyVerified(from, options); + } + + const result = new ResultValue(); + const promise = this.track._backing.getNextKeyPacket(result, from, options); + + if (result.pending) { + return promise.then(() => result.value); + } else { + return result.value; + } + } + + private async _getNextKeyVerified( + from: EncodedPacket, + options: PacketRetrievalOptions, + ): Promise { + const result = new ResultValue(); + const promise = this.track._backing.getNextKeyPacket(result, from, options); + if (result.pending) await promise; + + const nextPacket = result.value; + if (!nextPacket) { + return null; + } + + const determinedType = await this.track.determinePacketType(nextPacket); + if (determinedType === 'delta') { + // Try returning the next key packet (in hopes that it's actually a key packet) + return this._getNextKeyVerified(nextPacket, options); + } + + return nextPacket; + } +} diff --git a/src/wave/wave-demuxer.ts b/src/wave/wave-demuxer.ts index 50ad949..af4a1b9 100644 --- a/src/wave/wave-demuxer.ts +++ b/src/wave/wave-demuxer.ts @@ -12,10 +12,9 @@ import { Input } from '../input'; import { InputAudioTrack, InputAudioTrackBacking } from '../input-track'; import { DEFAULT_TRACK_DISPOSITION, MetadataTags } from '../metadata'; import { assert, MaybeRelevantPromise, ResultValue, UNDETERMINED_LANGUAGE } from '../misc'; -import { EncodedPacket, PLACEHOLDER_DATA } from '../packet'; +import { EncodedPacket, PacketRetrievalOptions, PLACEHOLDER_DATA } from '../packet'; import { readAscii, readBytes, Reader, readU16, readU32, readU64 } from '../reader'; import { parseId3V2Tag, readId3V2Header } from '../id3'; -import { PacketRetrievalOptions } from '../cursors'; export enum WaveFormat { PCM = 0x0001, diff --git a/test/browser/sample-cursor.test.ts b/test/browser/sample-cursor.test.ts index a65ac8e..6af7109 100644 --- a/test/browser/sample-cursor.test.ts +++ b/test/browser/sample-cursor.test.ts @@ -3,7 +3,6 @@ import { Input } from '../../src/input.js'; import { BufferSource, UrlSource } from '../../src/source.js'; import { ALL_FORMATS } from '../../src/input-format.js'; import { - audioBufferTransformer, AudioSampleCursor, canvasTransformer, VideoSampleCursor, @@ -1115,33 +1114,6 @@ test('Canvas transformer', async () => { await cursor2.close(); }); -test('AudioBuffer transformer', async () => { - using input = new Input({ - source: new UrlSource('/trim-buck-bunny.mov'), - formats: ALL_FORMATS, - }); - - const audioTrack = (await input.getPrimaryAudioTrack())!; - - const cursor = new AudioSampleCursor(audioTrack, { - transform: audioBufferTransformer(), - }); - - const firstSample = (await cursor.seekToFirst())!; - expect(firstSample.buffer).toBeInstanceOf(AudioBuffer); - expect(firstSample.timestamp).toBe(0); - expect(firstSample.duration).toBeGreaterThan(0); - expect(firstSample.buffer.duration).toBe(firstSample.duration); - - const nextSample = (await cursor.next())!; - expect(nextSample.buffer).toBeInstanceOf(AudioBuffer); - expect(nextSample.timestamp).toBeGreaterThan(firstSample.timestamp); - expect(nextSample.buffer).not.toBe(firstSample.buffer); - expect(nextSample.buffer.duration).toBe(nextSample.duration); - - await cursor.close(); -}); - test('Unthrottled decoder', async () => { // While the decoder is throttled in practice, these tests simulate what happens when the decoder naturally reaches // the end of the encodable packets without having been stopped; in this case, we still expect the cursor to keep diff --git a/test/browser/transparency.test.ts b/test/browser/transparency.test.ts index 20c096c..f48b44f 100644 --- a/test/browser/transparency.test.ts +++ b/test/browser/transparency.test.ts @@ -9,7 +9,8 @@ import { CanvasSource, VideoSampleSource } from '../../src/media-source.js'; import { canEncodeVideo, QUALITY_HIGH } from '../../src/encode.js'; import { VideoSample } from '../../src/sample.js'; import { Conversion } from '../../src/conversion.js'; -import { canvasTransformer, PacketCursor, PacketReader, VideoSampleCursor } from '../../src/cursors.js'; +import { canvasTransformer, PacketCursor, VideoSampleCursor } from '../../src/cursors.js'; +import { PacketReader } from '../../src/packet.js'; test('Can decode transparent video', async () => { using input = new Input({ diff --git a/test/node/annex-b-conversion.test.ts b/test/node/annex-b-conversion.test.ts index feec19d..a519110 100644 --- a/test/node/annex-b-conversion.test.ts +++ b/test/node/annex-b-conversion.test.ts @@ -8,7 +8,7 @@ import { Mp4OutputFormat } from '../../src/output-format.js'; import { BufferTarget } from '../../src/target.js'; import { Conversion } from '../../src/conversion.js'; import { extractAvcNalUnits } from '../../src/codec-data.js'; -import { PacketReader } from '../../src/cursors.js'; +import { PacketReader } from '../../src/packet.js'; const __dirname = new URL('.', import.meta.url).pathname; diff --git a/test/node/demux.test.ts b/test/node/demux.test.ts index 0496388..5a1b13f 100644 --- a/test/node/demux.test.ts +++ b/test/node/demux.test.ts @@ -4,10 +4,9 @@ import { BufferSource, FilePathSource } from '../../src/source.js'; import path from 'node:path'; import fs from 'node:fs'; import { ADTS, ALL_FORMATS, FLAC, MP3, MP4, OGG, QTFF, WAVE, WEBM } from '../../src/input-format.js'; -import { PacketReader } from '../../src/cursors.js'; import { InputAudioTrack, InputTrack } from '../../src/input-track.js'; import { assert } from '../../src/misc.js'; -import { EncodedPacket } from '../../src/packet.js'; +import { EncodedPacket, PacketReader } from '../../src/packet.js'; const __dirname = new URL('.', import.meta.url).pathname; diff --git a/test/node/flac.test.ts b/test/node/flac.test.ts index 292812a..1e2fd27 100644 --- a/test/node/flac.test.ts +++ b/test/node/flac.test.ts @@ -8,7 +8,8 @@ import { Output } from '../../src/output.js'; import { BufferTarget } from '../../src/target.js'; import { FlacOutputFormat } from '../../src/output-format.js'; import { Conversion } from '../../src/conversion.js'; -import { PacketCursor, PacketReader } from '../../src/cursors.js'; +import { PacketCursor } from '../../src/cursors.js'; +import { PacketReader } from '../../src/packet.js'; const __dirname = new URL('.', import.meta.url).pathname; diff --git a/test/node/packet-reading.test.ts b/test/node/packet-reading.test.ts index f3b9870..5ccb40a 100644 --- a/test/node/packet-reading.test.ts +++ b/test/node/packet-reading.test.ts @@ -4,8 +4,9 @@ import { BufferSource, FilePathSource } from '../../src/source.js'; import path from 'node:path'; import fs from 'node:fs'; import { ALL_FORMATS } from '../../src/input-format.js'; -import { PacketCursor, PacketReader } from '../../src/cursors.js'; +import { PacketCursor } from '../../src/cursors.js'; import { promiseAllEnsureOrder } from '../../src/misc.js'; +import { PacketReader } from '../../src/packet.js'; const __dirname = new URL('.', import.meta.url).pathname;