Make Conversion API copy input pairability graph by default, fix HLS muxer rotation bug, remove onfinalized callback

This commit is contained in:
Vanilagy
2026-04-14 21:14:58 +02:00
parent 53f05afbd8
commit 6410ea9f42
4 changed files with 317 additions and 29 deletions
+47 -4
View File
@@ -251,7 +251,12 @@ export type ConversionVideoOptions = {
* encoder configuration.
*/
processedHeight?: number;
/** Defines the group(s) the output track is a part of. Same semantics as {@link BaseTrackMetadata.group}. */
/**
* Defines the group(s) the output track is a part of. For more, see {@link BaseTrackMetadata.group}.
*
* If left blank, tracks will internally be assigned to groups such that the output track pairability graph exactly
* matches the input track pairability graph.
*/
group?: OutputTrackGroup | OutputTrackGroup[];
};
@@ -297,7 +302,12 @@ export type ConversionAudioOptions = {
* encoder configuration.
*/
processedSampleRate?: number;
/** Defines the group(s) the output track is a part of. Same semantics as {@link BaseTrackMetadata.group}. */
/**
* Defines the group(s) the output track is a part of. For more, see {@link BaseTrackMetadata.group}.
*
* If left blank, tracks will internally be assigned to groups such that the output track pairability graph exactly
* matches the input track pairability graph.
*/
group?: OutputTrackGroup | OutputTrackGroup[];
};
@@ -535,6 +545,8 @@ export class Conversion {
_nextOutputTrackId = 0;
/** @internal */
_outputTrackIds: number[] = [];
/** @internal */
_outputOwnTrackGroups: (OutputTrackGroup | null)[] = [];
/** @internal */
_trackPromises: Promise<void>[] = [];
@@ -869,6 +881,25 @@ export class Conversion {
}
}
// When no track groups are set by the user, then the output track pairability should be *identical* to the
// input's. We do the naive algorithm to achieve this: assign each track to its own group, and pair groups with
// each other based on input track pairability.
for (let i = 0; i < this.utilizedTracks.length - 1; i++) {
for (let j = i + 1; j < this.utilizedTracks.length; j++) {
const trackA = this.utilizedTracks[i]!;
const trackB = this.utilizedTracks[j]!;
const ownGroupA = this._outputOwnTrackGroups[i];
const ownGroupB = this._outputOwnTrackGroups[j];
assert(ownGroupA !== undefined);
assert(ownGroupB !== undefined);
if (ownGroupA && ownGroupB && trackA.canBePairedWith(trackB)) {
ownGroupA.pairWith(ownGroupB);
}
}
}
// Now, let's deal with metadata tags
const inputTags = await this.input.getMetadataTags();
@@ -1467,6 +1498,11 @@ export class Conversion {
}
}
let ownGroup: OutputTrackGroup | null = null;
if (!trackOptions.group) {
ownGroup = new OutputTrackGroup();
}
const videoTrackLanguageCode = await track.getLanguageCode();
this.output.addVideoTrack(videoSource, {
frameRate: trackOptions.frameRate,
@@ -1475,13 +1511,14 @@ export class Conversion {
name: await track.getName() ?? undefined,
disposition: await track.getDisposition(),
rotation: outputTrackRotation,
group: trackOptions.group,
group: ownGroup ?? trackOptions.group,
});
this._addedCounts.video++;
this._totalTrackCount++;
this.utilizedTracks.push(track);
this._outputTrackIds.push(outputTrackId);
this._outputOwnTrackGroups.push(ownGroup);
}
/** @internal */
@@ -1726,19 +1763,25 @@ export class Conversion {
}
}
let ownGroup: OutputTrackGroup | null = null;
if (!trackOptions.group) {
ownGroup = new OutputTrackGroup();
}
const audioTrackLanguageCode = await track.getLanguageCode();
this.output.addAudioTrack(audioSource, {
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
languageCode: isIso639Dash2LanguageCode(audioTrackLanguageCode) ? audioTrackLanguageCode : undefined,
name: await track.getName() ?? undefined,
disposition: await track.getDisposition(),
group: trackOptions.group,
group: ownGroup ?? trackOptions.group,
});
this._addedCounts.audio++;
this._totalTrackCount++;
this.utilizedTracks.push(track);
this._outputTrackIds.push(outputTrackId);
this._outputOwnTrackGroups.push(ownGroup);
}
/** @internal */
+1 -1
View File
@@ -1261,7 +1261,7 @@ export class HlsMuxer extends Muxer {
if (width !== undefined && height !== undefined) {
if (
videoTrack.metadata.rotation !== undefined
&& videoTrack.metadata.rotation % 180 !== 90
&& videoTrack.metadata.rotation % 180 === 90
) {
[width, height] = [height, width];
}
+6 -20
View File
@@ -65,13 +65,6 @@ export abstract class Target extends EventEmitter<TargetEvents> {
*/
onwrite: ((start: number, end: number) => unknown) | null = null;
/**
* Called when the target is finalized.
*
* @deprecated Use `target.on('finalized', () => ...)` instead.
*/
onfinalized: (() => unknown) | null = null;
/** @internal */
_dispatchWrite(start: number, end: number) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -79,14 +72,7 @@ export abstract class Target extends EventEmitter<TargetEvents> {
this._emit('write', { start, end });
}
/** @internal */
_dispatchFinalized() {
// eslint-disable-next-line @typescript-eslint/no-deprecated
this.onfinalized?.();
this._emit('finalized');
}
/**
/**
* Returns a new {@link RangedTarget} that writes data to this target using the given offset.
*
* Useful for writing a file into a section of a larger file.
@@ -192,7 +178,7 @@ export class BufferTarget extends Target {
/** @internal */
async _finalize() {
this.buffer = this._buffer.slice(0, this._maxPos);
this._dispatchFinalized();
this._emit('finalized');
}
/** @internal */
@@ -546,7 +532,7 @@ export class StreamTarget extends Target {
await this._streamWriter.ready;
await this._streamWriter.close();
this._dispatchFinalized();
this._emit('finalized');
}
/** @internal */
@@ -630,7 +616,7 @@ export class FilePathTarget extends Target {
/** @internal */
async _finalize() {
await this._streamTarget._finalize();
this._dispatchFinalized();
this._emit('finalized');
}
/** @internal */
@@ -660,7 +646,7 @@ export class NullTarget extends Target {
/** @internal */
async _finalize() {
this._dispatchFinalized();
this._emit('finalized');
}
/** @internal */
@@ -704,7 +690,7 @@ export class RangedTarget extends Target {
/** @internal */
async _finalize() {
this._dispatchFinalized();
this._emit('finalized');
}
/** @internal */