Unpromise many input track methods

This commit is contained in:
Vanilagy
2025-02-08 16:32:34 +01:00
parent 1d823805cf
commit cd30c9faec
10 changed files with 150 additions and 160 deletions
+2 -2
View File
@@ -60,8 +60,8 @@ let videoRotation = 0;
if (!videoTrack) { if (!videoTrack) {
canvas.style.display = 'none'; canvas.style.display = 'none';
} else { } else {
canvas.width = await videoTrack.getDisplayWidth(); canvas.width = videoTrack.displayWidth;
canvas.height = await videoTrack.getDisplayHeight(); canvas.height = videoTrack.displayHeight;
} }
const totalDuration = await input.computeDuration(); const totalDuration = await input.computeDuration();
+29 -35
View File
@@ -257,8 +257,7 @@ class Conversion {
continue; continue;
} }
const type = track.getType(); if (this.addedCounts[track.type] === outputTrackCounts[track.type].max) {
if (this.addedCounts[type] === outputTrackCounts[type].max) {
this.result.discardedTracks.push({ this.result.discardedTracks.push({
track, track,
reason: 'maxTrackCountOfTypeReached', reason: 'maxTrackCountOfTypeReached',
@@ -310,9 +309,7 @@ class Conversion {
} }
async processVideoTrack(track: InputVideoTrack) { async processVideoTrack(track: InputVideoTrack) {
const trackId = track.getId(); const sourceCodec = track.codec;
const sourceCodec = await track.getCodec();
if (!sourceCodec) { if (!sourceCodec) {
this.result.discardedTracks.push({ this.result.discardedTracks.push({
track, track,
@@ -323,8 +320,8 @@ class Conversion {
let videoSource: VideoSource; let videoSource: VideoSource;
const originalWidth = await track.getCodedWidth(); const originalWidth = track.codedWidth;
const originalHeight = await track.getCodedHeight(); const originalHeight = track.codedHeight;
const originalAspectRatio = originalWidth / originalHeight; const originalAspectRatio = originalWidth / originalHeight;
let width = originalWidth; let width = originalWidth;
@@ -368,16 +365,16 @@ class Conversion {
const meta: EncodedVideoChunkMetadata = { decoderConfig: decoderConfig ?? undefined }; const meta: EncodedVideoChunkMetadata = { decoderConfig: decoderConfig ?? undefined };
for await (const sample of sink.samples(undefined, this.endTimestamp)) { for await (const sample of sink.samples(undefined, this.endTimestamp)) {
if (this.synchronizer.shouldWait(trackId, sample.timestamp)) { if (this.synchronizer.shouldWait(track.id, sample.timestamp)) {
await this.synchronizer.wait(sample.timestamp); await this.synchronizer.wait(sample.timestamp);
} }
await source.digest(sample, meta); await source.digest(sample, meta);
this.reportProgress(trackId, sample.timestamp + sample.duration); this.reportProgress(track.id, sample.timestamp + sample.duration);
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
} else { } else {
// We need to decode & reencode the video // We need to decode & reencode the video
@@ -396,8 +393,8 @@ class Conversion {
} }
const encodableCodecs = await getEncodableVideoCodecs(videoCodecs, { const encodableCodecs = await getEncodableVideoCodecs(videoCodecs, {
width: needsResize ? width : await track.getCodedWidth(), width: needsResize ? width : track.codedWidth,
height: needsResize ? height : await track.getCodedHeight(), height: needsResize ? height : track.codedHeight,
}); });
if (encodableCodecs.length === 0) { if (encodableCodecs.length === 0) {
this.result.discardedTracks.push({ this.result.discardedTracks.push({
@@ -410,7 +407,7 @@ class Conversion {
const encodingConfig: VideoEncodingConfig = { const encodingConfig: VideoEncodingConfig = {
codec: encodableCodecs[0]!, codec: encodableCodecs[0]!,
bitrate: this.options.video?.bitrate ?? QUALITY_HIGH, bitrate: this.options.video?.bitrate ?? QUALITY_HIGH,
onEncodedSample: sample => this.reportProgress(trackId, sample.timestamp + sample.duration), onEncodedSample: sample => this.reportProgress(track.id, sample.timestamp + sample.duration),
}; };
if (needsResize) { if (needsResize) {
@@ -432,7 +429,7 @@ class Conversion {
const iterator = sink.canvases(this.startTimestamp, this.endTimestamp); const iterator = sink.canvases(this.startTimestamp, this.endTimestamp);
for await (const { canvas, timestamp, duration } of iterator) { for await (const { canvas, timestamp, duration } of iterator) {
if (this.synchronizer.shouldWait(trackId, timestamp)) { if (this.synchronizer.shouldWait(track.id, timestamp)) {
await this.synchronizer.wait(timestamp); await this.synchronizer.wait(timestamp);
} }
@@ -458,7 +455,7 @@ class Conversion {
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
} else { } else {
const source = new VideoFrameSource(encodingConfig); const source = new VideoFrameSource(encodingConfig);
@@ -470,7 +467,7 @@ class Conversion {
const sink = new VideoFrameSink(track); const sink = new VideoFrameSink(track);
for await (const { frame, timestamp } of sink.frames(this.startTimestamp, this.endTimestamp)) { for await (const { frame, timestamp } of sink.frames(this.startTimestamp, this.endTimestamp)) {
if (this.synchronizer.shouldWait(trackId, timestamp)) { if (this.synchronizer.shouldWait(track.id, timestamp)) {
await this.synchronizer.wait(timestamp); await this.synchronizer.wait(timestamp);
} }
@@ -483,16 +480,16 @@ class Conversion {
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
} }
} }
// Rotation metadata is reset if we do resizing // Rotation metadata is reset if we do resizing
const baseRotation = needsResize ? 0 : await track.getRotation(); const baseRotation = needsResize ? 0 : track.rotation;
this.output.addVideoTrack(videoSource, { this.output.addVideoTrack(videoSource, {
languageCode: await track.getLanguageCode(), languageCode: track.languageCode,
rotation: (baseRotation + (this.options.video?.rotate ?? 0)) % 360 as Rotation, rotation: (baseRotation + (this.options.video?.rotate ?? 0)) % 360 as Rotation,
}); });
this.addedCounts.video++; this.addedCounts.video++;
@@ -502,9 +499,7 @@ class Conversion {
} }
async processAudioTrack(track: InputAudioTrack) { async processAudioTrack(track: InputAudioTrack) {
const trackId = track.getId(); const sourceCodec = track.codec;
const sourceCodec = await track.getCodec();
if (!sourceCodec) { if (!sourceCodec) {
this.result.discardedTracks.push({ this.result.discardedTracks.push({
track, track,
@@ -515,8 +510,8 @@ class Conversion {
let audioSource: AudioSource; let audioSource: AudioSource;
const originalNumberOfChannels = await track.getNumberOfChannels(); const originalNumberOfChannels = track.numberOfChannels;
const originalSampleRate = await track.getSampleRate(); const originalSampleRate = track.sampleRate;
let numberOfChannels = this.options.audio?.numberOfChannels ?? originalNumberOfChannels; let numberOfChannels = this.options.audio?.numberOfChannels ?? originalNumberOfChannels;
let sampleRate = this.options.audio?.sampleRate ?? originalSampleRate; let sampleRate = this.options.audio?.sampleRate ?? originalSampleRate;
@@ -545,16 +540,16 @@ class Conversion {
const meta: EncodedAudioChunkMetadata = { decoderConfig: decoderConfig ?? undefined }; const meta: EncodedAudioChunkMetadata = { decoderConfig: decoderConfig ?? undefined };
for await (const sample of sink.samples(undefined, this.endTimestamp)) { for await (const sample of sink.samples(undefined, this.endTimestamp)) {
if (this.synchronizer.shouldWait(trackId, sample.timestamp)) { if (this.synchronizer.shouldWait(track.id, sample.timestamp)) {
await this.synchronizer.wait(sample.timestamp); await this.synchronizer.wait(sample.timestamp);
} }
await source.digest(sample, meta); await source.digest(sample, meta);
this.reportProgress(trackId, sample.timestamp + sample.duration); this.reportProgress(track.id, sample.timestamp + sample.duration);
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
} else { } else {
// We need to decode & reencode the audio // We need to decode & reencode the audio
@@ -621,7 +616,7 @@ class Conversion {
const source = new AudioDataSource({ const source = new AudioDataSource({
codec: codecOfChoice, codec: codecOfChoice,
bitrate: this.options.audio?.bitrate ?? QUALITY_HIGH, bitrate: this.options.audio?.bitrate ?? QUALITY_HIGH,
onEncodedSample: sample => this.reportProgress(trackId, sample.timestamp + sample.duration), onEncodedSample: sample => this.reportProgress(track.id, sample.timestamp + sample.duration),
}); });
audioSource = source; audioSource = source;
@@ -630,7 +625,7 @@ class Conversion {
const sink = new AudioDataSink(track); const sink = new AudioDataSink(track);
for await (const { data, timestamp } of sink.data(undefined, this.startTimestamp)) { for await (const { data, timestamp } of sink.data(undefined, this.startTimestamp)) {
if (this.synchronizer.shouldWait(trackId, timestamp)) { if (this.synchronizer.shouldWait(track.id, timestamp)) {
await this.synchronizer.wait(timestamp); await this.synchronizer.wait(timestamp);
} }
@@ -639,13 +634,13 @@ class Conversion {
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
} }
} }
this.output.addAudioTrack(audioSource, { this.output.addAudioTrack(audioSource, {
languageCode: await track.getLanguageCode(), languageCode: track.languageCode,
}); });
this.addedCounts.audio++; this.addedCounts.audio++;
this.totalTrackCount++; this.totalTrackCount++;
@@ -663,11 +658,10 @@ class Conversion {
targetNumberOfChannels: number, targetNumberOfChannels: number,
targetSampleRate: number, targetSampleRate: number,
) { ) {
const trackId = track.getId();
const source = new AudioBufferSource({ const source = new AudioBufferSource({
codec, codec,
bitrate: this.options.audio?.bitrate ?? QUALITY_HIGH, bitrate: this.options.audio?.bitrate ?? QUALITY_HIGH,
onEncodedSample: sample => this.reportProgress(trackId, sample.timestamp + sample.duration), onEncodedSample: sample => this.reportProgress(track.id, sample.timestamp + sample.duration),
}); });
this.trackPromises.push((async () => { this.trackPromises.push((async () => {
@@ -690,7 +684,7 @@ class Conversion {
const sink = new AudioBufferSink(track); const sink = new AudioBufferSink(track);
for await (const { buffer, timestamp, duration } of sink.buffers(this.startTimestamp, this.endTimestamp)) { for await (const { buffer, timestamp, duration } of sink.buffers(this.startTimestamp, this.endTimestamp)) {
if (this.synchronizer.shouldWait(trackId, timestamp)) { if (this.synchronizer.shouldWait(track.id, timestamp)) {
await this.synchronizer.wait(timestamp); await this.synchronizer.wait(timestamp);
} }
@@ -746,7 +740,7 @@ class Conversion {
} }
await source.close(); await source.close();
this.synchronizer.closeTrack(trackId); this.synchronizer.closeTrack(track.id);
})()); })());
return source; return source;
+38 -38
View File
@@ -7,11 +7,11 @@ import { EncodedAudioSample, EncodedVideoSample } from './sample';
export interface InputTrackBacking { export interface InputTrackBacking {
getId(): number; getId(): number;
getCodec(): Promise<MediaCodec | null>; getCodec(): MediaCodec | null;
getLanguageCode(): string;
getTimeResolution(): number;
getFirstTimestamp(): Promise<number>; getFirstTimestamp(): Promise<number>;
computeDuration(): Promise<number>; computeDuration(): Promise<number>;
getLanguageCode(): Promise<string>;
getTimeResolution(): Promise<number>;
} }
/** @public */ /** @public */
@@ -24,8 +24,8 @@ export abstract class InputTrack {
this._backing = backing; this._backing = backing;
} }
abstract getType(): TrackType; abstract get type(): TrackType;
abstract getCodec(): Promise<MediaCodec | null>; abstract get codec(): MediaCodec | null;
abstract getCodecMimeType(): Promise<string | null>; abstract getCodecMimeType(): Promise<string | null>;
abstract canDecode(): Promise<boolean>; abstract canDecode(): Promise<boolean>;
abstract computeSampleStats(): Promise<SampleStats>; abstract computeSampleStats(): Promise<SampleStats>;
@@ -38,10 +38,18 @@ export abstract class InputTrack {
return this instanceof InputAudioTrack; return this instanceof InputAudioTrack;
} }
getId() { get id() {
return this._backing.getId(); return this._backing.getId();
} }
get languageCode() {
return this._backing.getLanguageCode();
}
get timeResolution() {
return this._backing.getTimeResolution();
}
getFirstTimestamp() { getFirstTimestamp() {
return this._backing.getFirstTimestamp(); return this._backing.getFirstTimestamp();
} }
@@ -49,21 +57,13 @@ export abstract class InputTrack {
computeDuration() { computeDuration() {
return this._backing.computeDuration(); return this._backing.computeDuration();
} }
getLanguageCode() {
return this._backing.getLanguageCode();
}
getTimeResolution() {
return this._backing.getTimeResolution();
}
} }
export interface InputVideoTrackBacking extends InputTrackBacking { export interface InputVideoTrackBacking extends InputTrackBacking {
getCodec(): Promise<VideoCodec | null>; getCodec(): VideoCodec | null;
getCodedWidth(): Promise<number>; getCodedWidth(): number;
getCodedHeight(): Promise<number>; getCodedHeight(): number;
getRotation(): Promise<Rotation>; getRotation(): Rotation;
getColorSpace(): Promise<VideoColorSpaceInit>; getColorSpace(): Promise<VideoColorSpaceInit>;
getDecoderConfig(): Promise<VideoDecoderConfig | null>; getDecoderConfig(): Promise<VideoDecoderConfig | null>;
getFirstSample(options: SampleRetrievalOptions): Promise<EncodedVideoSample | null>; getFirstSample(options: SampleRetrievalOptions): Promise<EncodedVideoSample | null>;
@@ -85,33 +85,33 @@ export class InputVideoTrack extends InputTrack {
this._backing = backing; this._backing = backing;
} }
getType(): TrackType { get type(): TrackType {
return 'video'; return 'video';
} }
getCodec(): Promise<VideoCodec | null> { get codec() {
return this._backing.getCodec(); return this._backing.getCodec();
} }
getCodedWidth() { get codedWidth() {
return this._backing.getCodedWidth(); return this._backing.getCodedWidth();
} }
getCodedHeight() { get codedHeight() {
return this._backing.getCodedHeight(); return this._backing.getCodedHeight();
} }
getRotation() { get rotation() {
return this._backing.getRotation(); return this._backing.getRotation();
} }
async getDisplayWidth() { get displayWidth() {
const rotation = await this._backing.getRotation(); const rotation = this._backing.getRotation();
return rotation % 180 === 0 ? this._backing.getCodedWidth() : this._backing.getCodedHeight(); return rotation % 180 === 0 ? this._backing.getCodedWidth() : this._backing.getCodedHeight();
} }
async getDisplayHeight() { get displayHeight() {
const rotation = await this._backing.getRotation(); const rotation = this._backing.getRotation();
return rotation % 180 === 0 ? this._backing.getCodedHeight() : this._backing.getCodedWidth(); return rotation % 180 === 0 ? this._backing.getCodedHeight() : this._backing.getCodedWidth();
} }
@@ -132,7 +132,7 @@ export class InputVideoTrack extends InputTrack {
} }
async getCodecMimeType() { async getCodecMimeType() {
const decoderConfig = await this.getDecoderConfig(); const decoderConfig = await this._backing.getDecoderConfig();
return decoderConfig?.codec ?? null; return decoderConfig?.codec ?? null;
} }
@@ -143,7 +143,7 @@ export class InputVideoTrack extends InputTrack {
return false; return false;
} }
const codec = await this.getCodec(); const codec = this._backing.getCodec();
assert(codec !== null); assert(codec !== null);
if (customVideoDecoders.some(x => x.supports(codec, decoderConfig))) { if (customVideoDecoders.some(x => x.supports(codec, decoderConfig))) {
@@ -168,9 +168,9 @@ export class InputVideoTrack extends InputTrack {
} }
export interface InputAudioTrackBacking extends InputTrackBacking { export interface InputAudioTrackBacking extends InputTrackBacking {
getCodec(): Promise<AudioCodec | null>; getCodec(): AudioCodec | null;
getNumberOfChannels(): Promise<number>; getNumberOfChannels(): number;
getSampleRate(): Promise<number>; getSampleRate(): number;
getDecoderConfig(): Promise<AudioDecoderConfig | null>; getDecoderConfig(): Promise<AudioDecoderConfig | null>;
getFirstSample(options: SampleRetrievalOptions): Promise<EncodedAudioSample | null>; getFirstSample(options: SampleRetrievalOptions): Promise<EncodedAudioSample | null>;
getSample(timestamp: number, options: SampleRetrievalOptions): Promise<EncodedAudioSample | null>; getSample(timestamp: number, options: SampleRetrievalOptions): Promise<EncodedAudioSample | null>;
@@ -191,19 +191,19 @@ export class InputAudioTrack extends InputTrack {
this._backing = backing; this._backing = backing;
} }
getType(): TrackType { get type(): TrackType {
return 'audio'; return 'audio';
} }
getCodec(): Promise<AudioCodec | null> { get codec(): AudioCodec | null {
return this._backing.getCodec(); return this._backing.getCodec();
} }
getNumberOfChannels() { get numberOfChannels() {
return this._backing.getNumberOfChannels(); return this._backing.getNumberOfChannels();
} }
getSampleRate() { get sampleRate() {
return this._backing.getSampleRate(); return this._backing.getSampleRate();
} }
@@ -212,7 +212,7 @@ export class InputAudioTrack extends InputTrack {
} }
async getCodecMimeType() { async getCodecMimeType() {
const decoderConfig = await this.getDecoderConfig(); const decoderConfig = await this._backing.getDecoderConfig();
return decoderConfig?.codec ?? null; return decoderConfig?.codec ?? null;
} }
@@ -223,7 +223,7 @@ export class InputAudioTrack extends InputTrack {
return false; return false;
} }
const codec = await this.getCodec(); const codec = this._backing.getCodec();
assert(codec !== null); assert(codec !== null);
if (customAudioDecoders.some(x => x.supports(codec, decoderConfig))) { if (customAudioDecoders.some(x => x.supports(codec, decoderConfig))) {
+28 -30
View File
@@ -1725,28 +1725,28 @@ abstract class IsobmffTrackBacking<
return this.internalTrack.id; return this.internalTrack.id;
} }
getCodec(): Promise<MediaCodec | null> { getCodec(): MediaCodec | null {
throw new Error('Not implemented on base class.'); throw new Error('Not implemented on base class.');
} }
getLanguageCode() {
return this.internalTrack.languageCode;
}
getTimeResolution() {
return this.internalTrack.timescale;
}
async computeDuration() { async computeDuration() {
const lastSample = await this.getSample(Infinity, { metadataOnly: true }); const lastSample = await this.getSample(Infinity, { metadataOnly: true });
return (lastSample?.timestamp ?? 0) + (lastSample?.duration ?? 0); return (lastSample?.timestamp ?? 0) + (lastSample?.duration ?? 0);
} }
async getLanguageCode() {
return this.internalTrack.languageCode;
}
async getFirstTimestamp() { async getFirstTimestamp() {
const firstSample = await this.getFirstSample({ metadataOnly: true }); const firstSample = await this.getFirstSample({ metadataOnly: true });
return firstSample?.timestamp ?? 0; return firstSample?.timestamp ?? 0;
} }
async getTimeResolution() {
return this.internalTrack.timescale;
}
abstract createSample( abstract createSample(
data: Uint8Array, data: Uint8Array,
byteLength: number, byteLength: number,
@@ -2264,19 +2264,19 @@ class IsobmffVideoTrackBacking extends IsobmffTrackBacking<EncodedVideoSample> i
this.internalTrack = internalTrack; this.internalTrack = internalTrack;
} }
override async getCodec(): Promise<VideoCodec | null> { override getCodec(): VideoCodec | null {
return this.internalTrack.info.codec; return this.internalTrack.info.codec;
} }
async getCodedWidth() { getCodedWidth() {
return this.internalTrack.info.width; return this.internalTrack.info.width;
} }
async getCodedHeight() { getCodedHeight() {
return this.internalTrack.info.height; return this.internalTrack.info.height;
} }
async getRotation() { getRotation() {
return this.internalTrack.rotation; return this.internalTrack.rotation;
} }
@@ -2294,12 +2294,12 @@ class IsobmffVideoTrackBacking extends IsobmffTrackBacking<EncodedVideoSample> i
return null; return null;
} }
if (this.internalTrack.info.codec === 'vp9' && !this.internalTrack.info.vp9CodecInfo) {
const firstSample = await this.getFirstSample({});
this.internalTrack.info.vp9CodecInfo = firstSample && extractVp9CodecInfoFromFrame(firstSample.data);
}
return this.decoderConfigPromise ??= (async (): Promise<VideoDecoderConfig> => { return this.decoderConfigPromise ??= (async (): Promise<VideoDecoderConfig> => {
if (this.internalTrack.info.codec === 'vp9' && !this.internalTrack.info.vp9CodecInfo) {
const firstSample = await this.getFirstSample({});
this.internalTrack.info.vp9CodecInfo = firstSample && extractVp9CodecInfoFromFrame(firstSample.data);
}
return { return {
codec: extractVideoCodecString(this.internalTrack.info), codec: extractVideoCodecString(this.internalTrack.info),
codedWidth: this.internalTrack.info.width, codedWidth: this.internalTrack.info.width,
@@ -2323,22 +2323,22 @@ class IsobmffVideoTrackBacking extends IsobmffTrackBacking<EncodedVideoSample> i
class IsobmffAudioTrackBacking extends IsobmffTrackBacking<EncodedAudioSample> implements InputAudioTrackBacking { class IsobmffAudioTrackBacking extends IsobmffTrackBacking<EncodedAudioSample> implements InputAudioTrackBacking {
override internalTrack: InternalAudioTrack; override internalTrack: InternalAudioTrack;
decoderConfigPromise: Promise<AudioDecoderConfig> | null = null; decoderConfig: AudioDecoderConfig | null = null;
constructor(internalTrack: InternalAudioTrack) { constructor(internalTrack: InternalAudioTrack) {
super(internalTrack); super(internalTrack);
this.internalTrack = internalTrack; this.internalTrack = internalTrack;
} }
override async getCodec(): Promise<AudioCodec | null> { override getCodec(): AudioCodec | null {
return this.internalTrack.info.codec; return this.internalTrack.info.codec;
} }
async getNumberOfChannels() { getNumberOfChannels() {
return this.internalTrack.info.numberOfChannels; return this.internalTrack.info.numberOfChannels;
} }
async getSampleRate() { getSampleRate() {
return this.internalTrack.info.sampleRate; return this.internalTrack.info.sampleRate;
} }
@@ -2347,14 +2347,12 @@ class IsobmffAudioTrackBacking extends IsobmffTrackBacking<EncodedAudioSample> i
return null; return null;
} }
return this.decoderConfigPromise ??= (async (): Promise<AudioDecoderConfig> => { return this.decoderConfig ??= {
return { codec: extractAudioCodecString(this.internalTrack.info),
codec: extractAudioCodecString(this.internalTrack.info), numberOfChannels: this.internalTrack.info.numberOfChannels,
numberOfChannels: this.internalTrack.info.numberOfChannels, sampleRate: this.internalTrack.info.sampleRate,
sampleRate: this.internalTrack.info.sampleRate, description: this.internalTrack.info.codecDescription ?? undefined,
description: this.internalTrack.info.codecDescription ?? undefined, };
};
})();
} }
createSample( createSample(
+21 -23
View File
@@ -980,7 +980,7 @@ abstract class MatroskaTrackBacking<
return this.internalTrack.id; return this.internalTrack.id;
} }
getCodec(): Promise<MediaCodec | null> { getCodec(): MediaCodec | null {
throw new Error('Not implemented on base class.'); throw new Error('Not implemented on base class.');
} }
@@ -989,7 +989,7 @@ abstract class MatroskaTrackBacking<
return (lastSample?.timestamp ?? 0) + (lastSample?.duration ?? 0); return (lastSample?.timestamp ?? 0) + (lastSample?.duration ?? 0);
} }
async getLanguageCode() { getLanguageCode() {
return this.internalTrack.languageCode; return this.internalTrack.languageCode;
} }
@@ -998,7 +998,7 @@ abstract class MatroskaTrackBacking<
return firstSample?.timestamp ?? 0; return firstSample?.timestamp ?? 0;
} }
async getTimeResolution() { getTimeResolution() {
return this.internalTrack.segment.timestampFactor; return this.internalTrack.segment.timestampFactor;
} }
@@ -1439,19 +1439,19 @@ class MatroskaVideoTrackBacking extends MatroskaTrackBacking<EncodedVideoSample>
this.internalTrack = internalTrack; this.internalTrack = internalTrack;
} }
override async getCodec(): Promise<VideoCodec | null> { override getCodec(): VideoCodec | null {
return this.internalTrack.info.codec; return this.internalTrack.info.codec;
} }
async getCodedWidth() { getCodedWidth() {
return this.internalTrack.info.width; return this.internalTrack.info.width;
} }
async getCodedHeight() { getCodedHeight() {
return this.internalTrack.info.height; return this.internalTrack.info.height;
} }
async getRotation() { getRotation() {
return this.internalTrack.info.rotation; return this.internalTrack.info.rotation;
} }
@@ -1513,22 +1513,22 @@ class MatroskaVideoTrackBacking extends MatroskaTrackBacking<EncodedVideoSample>
class MatroskaAudioTrackBacking extends MatroskaTrackBacking<EncodedAudioSample> implements InputAudioTrackBacking { class MatroskaAudioTrackBacking extends MatroskaTrackBacking<EncodedAudioSample> implements InputAudioTrackBacking {
override internalTrack: InternalAudioTrack; override internalTrack: InternalAudioTrack;
decoderConfigPromise: Promise<AudioDecoderConfig> | null = null; decoderConfig: AudioDecoderConfig | null = null;
constructor(internalTrack: InternalAudioTrack) { constructor(internalTrack: InternalAudioTrack) {
super(internalTrack); super(internalTrack);
this.internalTrack = internalTrack; this.internalTrack = internalTrack;
} }
override async getCodec(): Promise<AudioCodec | null> { override getCodec(): AudioCodec | null {
return this.internalTrack.info.codec; return this.internalTrack.info.codec;
} }
async getNumberOfChannels() { getNumberOfChannels() {
return this.internalTrack.info.numberOfChannels; return this.internalTrack.info.numberOfChannels;
} }
async getSampleRate() { getSampleRate() {
return this.internalTrack.info.sampleRate; return this.internalTrack.info.sampleRate;
} }
@@ -1537,18 +1537,16 @@ class MatroskaAudioTrackBacking extends MatroskaTrackBacking<EncodedAudioSample>
return null; return null;
} }
return this.decoderConfigPromise ??= (async (): Promise<AudioDecoderConfig> => { return this.decoderConfig ??= {
return { codec: extractAudioCodecString({
codec: extractAudioCodecString({ codec: this.internalTrack.info.codec,
codec: this.internalTrack.info.codec, codecDescription: this.internalTrack.codecPrivate,
codecDescription: this.internalTrack.codecPrivate, aacCodecInfo: this.internalTrack.info.aacCodecInfo,
aacCodecInfo: this.internalTrack.info.aacCodecInfo, }),
}), numberOfChannels: this.internalTrack.info.numberOfChannels,
numberOfChannels: this.internalTrack.info.numberOfChannels, sampleRate: this.internalTrack.info.sampleRate,
sampleRate: this.internalTrack.info.sampleRate, description: this.internalTrack.codecPrivate ?? undefined,
description: this.internalTrack.codecPrivate ?? undefined, };
};
})();
} }
createSample( createSample(
+15 -15
View File
@@ -693,9 +693,9 @@ export class VideoFrameSink extends BaseMediaFrameSink<EncodedVideoSample, Video
); );
} }
const codec = await this._videoTrack.getCodec(); const codec = this._videoTrack.codec;
const decoderConfig = await this._videoTrack.getDecoderConfig(); const decoderConfig = await this._videoTrack.getDecoderConfig();
const timeResolution = await this._videoTrack.getTimeResolution(); const timeResolution = this._videoTrack.timeResolution;
assert(codec && decoderConfig); assert(codec && decoderConfig);
return new VideoDecoderWrapper(onFrame, onError, codec, decoderConfig, timeResolution); return new VideoDecoderWrapper(onFrame, onError, codec, decoderConfig, timeResolution);
@@ -727,14 +727,14 @@ export class VideoFrameSink extends BaseMediaFrameSink<EncodedVideoSample, Video
frames(startTimestamp = 0, endTimestamp = Infinity) { frames(startTimestamp = 0, endTimestamp = Infinity) {
return mapAsyncGenerator( return mapAsyncGenerator(
this.mediaFramesInRange(startTimestamp, endTimestamp), this.mediaFramesInRange(startTimestamp, endTimestamp),
async frame => this._wrappedFrameToWrappedVideoFrame(frame), frame => this._wrappedFrameToWrappedVideoFrame(frame),
); );
} }
framesAtTimestamps(timestamps: AnyIterable<number>) { framesAtTimestamps(timestamps: AnyIterable<number>) {
return mapAsyncGenerator( return mapAsyncGenerator(
this.mediaFramesAtTimestamps(timestamps), this.mediaFramesAtTimestamps(timestamps),
async frame => frame && this._wrappedFrameToWrappedVideoFrame(frame), frame => frame && this._wrappedFrameToWrappedVideoFrame(frame),
); );
} }
} }
@@ -775,10 +775,10 @@ export class CanvasSink {
} }
/** @internal */ /** @internal */
async _videoFrameToWrappedCanvas(frame: WrappedVideoFrame): Promise<WrappedCanvas> { _videoFrameToWrappedCanvas(frame: WrappedVideoFrame): WrappedCanvas {
const width = this._dimensions?.width ?? await this._videoTrack.getDisplayWidth(); const width = this._dimensions?.width ?? this._videoTrack.displayWidth;
const height = this._dimensions?.height ?? await this._videoTrack.getDisplayHeight(); const height = this._dimensions?.height ?? this._videoTrack.displayHeight;
const rotation = await this._videoTrack.getRotation(); const rotation = this._videoTrack.rotation;
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = width; canvas.width = width;
@@ -822,7 +822,7 @@ export class CanvasSink {
canvasesAtTimestamps(timestamps: AnyIterable<number>) { canvasesAtTimestamps(timestamps: AnyIterable<number>) {
return mapAsyncGenerator( return mapAsyncGenerator(
this._videoFrameSink.framesAtTimestamps(timestamps), this._videoFrameSink.framesAtTimestamps(timestamps),
async frame => frame && this._videoFrameToWrappedCanvas(frame), frame => frame && this._videoFrameToWrappedCanvas(frame),
); );
} }
} }
@@ -1161,14 +1161,14 @@ export class AudioDataSink extends BaseMediaFrameSink<EncodedAudioSample, AudioD
); );
} }
const codec = await this._audioTrack.getCodec(); const codec = this._audioTrack.codec;
const decoderConfig = await this._audioTrack.getDecoderConfig(); const decoderConfig = await this._audioTrack.getDecoderConfig();
assert(codec && decoderConfig); assert(codec && decoderConfig);
if ((PCM_AUDIO_CODECS as readonly string[]).includes(decoderConfig.codec)) { if ((PCM_AUDIO_CODECS as readonly string[]).includes(decoderConfig.codec)) {
return new PcmAudioDecoderWrapper(onData, onError, decoderConfig); return new PcmAudioDecoderWrapper(onData, onError, decoderConfig);
} else { } else {
const timeResolution = await this._audioTrack.getTimeResolution(); const timeResolution = this._audioTrack.timeResolution;
return new AudioDecoderWrapper(onData, onError, codec, decoderConfig, timeResolution); return new AudioDecoderWrapper(onData, onError, codec, decoderConfig, timeResolution);
} }
} }
@@ -1199,14 +1199,14 @@ export class AudioDataSink extends BaseMediaFrameSink<EncodedAudioSample, AudioD
data(startTimestamp = 0, endTimestamp = Infinity) { data(startTimestamp = 0, endTimestamp = Infinity) {
return mapAsyncGenerator( return mapAsyncGenerator(
this.mediaFramesInRange(startTimestamp, endTimestamp), this.mediaFramesInRange(startTimestamp, endTimestamp),
async data => this._wrappedFrameToWrappedAudioData(data), data => this._wrappedFrameToWrappedAudioData(data),
); );
} }
dataAtTimestamps(timestamps: AnyIterable<number>) { dataAtTimestamps(timestamps: AnyIterable<number>) {
return mapAsyncGenerator( return mapAsyncGenerator(
this.mediaFramesAtTimestamps(timestamps), this.mediaFramesAtTimestamps(timestamps),
async data => data && this._wrappedFrameToWrappedAudioData(data), data => data && this._wrappedFrameToWrappedAudioData(data),
); );
} }
} }
@@ -1267,14 +1267,14 @@ export class AudioBufferSink {
buffers(startTimestamp = 0, endTimestamp = Infinity) { buffers(startTimestamp = 0, endTimestamp = Infinity) {
return mapAsyncGenerator( return mapAsyncGenerator(
this._audioDataSink.data(startTimestamp, endTimestamp), this._audioDataSink.data(startTimestamp, endTimestamp),
async data => this._audioDataToWrappedArrayBuffer(data), data => this._audioDataToWrappedArrayBuffer(data),
); );
} }
buffersAtTimestamps(timestamps: AnyIterable<number>) { buffersAtTimestamps(timestamps: AnyIterable<number>) {
return mapAsyncGenerator( return mapAsyncGenerator(
this._audioDataSink.dataAtTimestamps(timestamps), this._audioDataSink.dataAtTimestamps(timestamps),
async data => data && this._audioDataToWrappedArrayBuffer(data), data => data && this._audioDataToWrappedArrayBuffer(data),
); );
} }
} }
+2 -2
View File
@@ -335,7 +335,7 @@ export const setInt24 = (view: DataView, byteOffset: number, value: number, litt
*/ */
export const mapAsyncGenerator = <T, U>( export const mapAsyncGenerator = <T, U>(
generator: AsyncGenerator<T, void, unknown>, generator: AsyncGenerator<T, void, unknown>,
map: (t: T) => Promise<U>, map: (t: T) => U,
): AsyncGenerator<U, void, unknown> => { ): AsyncGenerator<U, void, unknown> => {
return { return {
async next() { async next() {
@@ -343,7 +343,7 @@ export const mapAsyncGenerator = <T, U>(
if (result.done) { if (result.done) {
return { value: undefined, done: true }; return { value: undefined, done: true };
} else { } else {
return { value: await map(result.value), done: false }; return { value: map(result.value), done: false };
} }
}, },
return() { return() {
+5 -5
View File
@@ -119,7 +119,7 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
return 0; return 0;
} }
async getTimeResolution() { getTimeResolution() {
assert(this.demuxer.firstFrameHeader); assert(this.demuxer.firstFrameHeader);
return this.demuxer.firstFrameHeader.sampleRate / this.demuxer.firstFrameHeader.audioSamplesInFrame; return this.demuxer.firstFrameHeader.sampleRate / this.demuxer.firstFrameHeader.audioSamplesInFrame;
} }
@@ -128,20 +128,20 @@ class Mp3AudioTrackBacking implements InputAudioTrackBacking {
return this.demuxer.computeDuration(); return this.demuxer.computeDuration();
} }
async getLanguageCode() { getLanguageCode() {
return UNDETERMINED_LANGUAGE; return UNDETERMINED_LANGUAGE;
} }
async getCodec(): Promise<AudioCodec> { getCodec(): AudioCodec {
return 'mp3'; return 'mp3';
} }
async getNumberOfChannels() { getNumberOfChannels() {
assert(this.demuxer.firstFrameHeader); assert(this.demuxer.firstFrameHeader);
return this.demuxer.firstFrameHeader.channel === 3 ? 1 : 2; return this.demuxer.firstFrameHeader.channel === 3 ? 1 : 2;
} }
async getSampleRate() { getSampleRate() {
assert(this.demuxer.firstFrameHeader); assert(this.demuxer.firstFrameHeader);
return this.demuxer.firstFrameHeader.sampleRate; return this.demuxer.firstFrameHeader.sampleRate;
} }
+5 -5
View File
@@ -410,19 +410,19 @@ class OggAudioTrackBacking implements InputAudioTrackBacking {
return this.bitstream.serialNumber; return this.bitstream.serialNumber;
} }
async getNumberOfChannels() { getNumberOfChannels() {
return this.bitstream.numberOfChannels; return this.bitstream.numberOfChannels;
} }
async getSampleRate() { getSampleRate() {
return this.bitstream.sampleRate; return this.bitstream.sampleRate;
} }
async getTimeResolution() { getTimeResolution() {
return this.bitstream.sampleRate; return this.bitstream.sampleRate;
} }
async getCodec() { getCodec() {
return this.bitstream.codec; return this.bitstream.codec;
} }
@@ -437,7 +437,7 @@ class OggAudioTrackBacking implements InputAudioTrackBacking {
}; };
} }
async getLanguageCode() { getLanguageCode() {
return UNDETERMINED_LANGUAGE; return UNDETERMINED_LANGUAGE;
} }
+5 -5
View File
@@ -187,7 +187,7 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
return 1; return 1;
} }
async getCodec() { getCodec() {
return this.demuxer.getCodec(); return this.demuxer.getCodec();
} }
@@ -209,22 +209,22 @@ class WaveAudioTrackBacking implements InputAudioTrackBacking {
return this.demuxer.computeDuration(); return this.demuxer.computeDuration();
} }
async getNumberOfChannels() { getNumberOfChannels() {
assert(this.demuxer.audioInfo); assert(this.demuxer.audioInfo);
return this.demuxer.audioInfo.numberOfChannels; return this.demuxer.audioInfo.numberOfChannels;
} }
async getSampleRate() { getSampleRate() {
assert(this.demuxer.audioInfo); assert(this.demuxer.audioInfo);
return this.demuxer.audioInfo.sampleRate; return this.demuxer.audioInfo.sampleRate;
} }
async getTimeResolution() { getTimeResolution() {
assert(this.demuxer.audioInfo); assert(this.demuxer.audioInfo);
return this.demuxer.audioInfo.sampleRate; return this.demuxer.audioInfo.sampleRate;
} }
async getLanguageCode() { getLanguageCode() {
return UNDETERMINED_LANGUAGE; return UNDETERMINED_LANGUAGE;
} }