mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Expand packet.clone(), make conversion start timestamp default to the input's start timestamp
This commit is contained in:
+36
-23
@@ -84,7 +84,7 @@ export type ConversionOptions = {
|
||||
trim?: {
|
||||
/**
|
||||
* The time in the input file in seconds at which the output file should start. Must be less than `end`.
|
||||
* Defaults to 0 when omitted.
|
||||
* When omitted, defaults to the start timestamp of the input or to 0, whichever is higher.
|
||||
*/
|
||||
start?: number;
|
||||
/**
|
||||
@@ -462,9 +462,9 @@ export class Conversion {
|
||||
/** @internal */
|
||||
_options: ConversionOptions;
|
||||
/** @internal */
|
||||
_startTimestamp: number;
|
||||
_startTimestamp!: number;
|
||||
/** @internal */
|
||||
_endTimestamp: number;
|
||||
_endTimestamp!: number;
|
||||
|
||||
/** @internal */
|
||||
_addedCounts: Record<TrackType, number> = {
|
||||
@@ -592,9 +592,6 @@ export class Conversion {
|
||||
this.input = options.input;
|
||||
this.output = options.output;
|
||||
|
||||
this._startTimestamp = options.trim?.start ?? 0;
|
||||
this._endTimestamp = options.trim?.end ?? Infinity;
|
||||
|
||||
const { promise: started, resolve: start } = promiseWithResolvers();
|
||||
this._started = started;
|
||||
this._start = start;
|
||||
@@ -602,6 +599,14 @@ export class Conversion {
|
||||
|
||||
/** @internal */
|
||||
async _init() {
|
||||
this._startTimestamp = this._options.trim?.start ?? Math.max(
|
||||
await this.input.getFirstTimestamp(),
|
||||
// Samples can also have negative timestamps, but the meaning typically is "don't present me", so let's cut
|
||||
// those out by default.
|
||||
0,
|
||||
);
|
||||
this._endTimestamp = this._options.trim?.end ?? Infinity;
|
||||
|
||||
const inputTracks = await this.input.getTracks();
|
||||
const outputTrackCounts = this.output.format.getSupportedTrackCounts();
|
||||
|
||||
@@ -900,8 +905,7 @@ export class Conversion {
|
||||
|
||||
const firstTimestamp = await track.getFirstTimestamp();
|
||||
const needsTranscode = !!trackOptions.forceTranscode
|
||||
|| this._startTimestamp > 0
|
||||
|| firstTimestamp < 0
|
||||
|| firstTimestamp < this._startTimestamp
|
||||
|| !!trackOptions.frameRate
|
||||
|| trackOptions.keyFrameInterval !== undefined
|
||||
|| trackOptions.process !== undefined;
|
||||
@@ -943,17 +947,19 @@ export class Conversion {
|
||||
return;
|
||||
}
|
||||
|
||||
if (alpha === 'discard') {
|
||||
// Feels hacky given that the rest of the packet is readonly. But, works for now.
|
||||
delete packet.sideData.alpha;
|
||||
delete packet.sideData.alphaByteLength;
|
||||
}
|
||||
const modifiedPacket = packet.clone({
|
||||
timestamp: packet.timestamp - this._startTimestamp,
|
||||
sideData: alpha === 'discard'
|
||||
? {} // Remove alpha side data
|
||||
: packet.sideData,
|
||||
});
|
||||
assert(modifiedPacket.timestamp >= 0);
|
||||
|
||||
this._reportProgress(track.id, packet.timestamp);
|
||||
await source.add(packet, meta);
|
||||
this._reportProgress(track.id, modifiedPacket.timestamp);
|
||||
await source.add(modifiedPacket, meta);
|
||||
|
||||
if (this._synchronizer.shouldWait(track.id, packet.timestamp)) {
|
||||
await this._synchronizer.wait(packet.timestamp);
|
||||
if (this._synchronizer.shouldWait(track.id, modifiedPacket.timestamp)) {
|
||||
await this._synchronizer.wait(modifiedPacket.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1314,8 +1320,7 @@ export class Conversion {
|
||||
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
||||
let needsResample = numberOfChannels !== originalNumberOfChannels
|
||||
|| sampleRate !== originalSampleRate
|
||||
|| this._startTimestamp > 0
|
||||
|| firstTimestamp < 0;
|
||||
|| firstTimestamp < this._startTimestamp;
|
||||
|
||||
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
||||
if (
|
||||
@@ -1346,11 +1351,16 @@ export class Conversion {
|
||||
return;
|
||||
}
|
||||
|
||||
this._reportProgress(track.id, packet.timestamp);
|
||||
await source.add(packet, meta);
|
||||
const modifiedPacket = packet.clone({
|
||||
timestamp: packet.timestamp - this._startTimestamp,
|
||||
});
|
||||
assert(modifiedPacket.timestamp >= 0);
|
||||
|
||||
if (this._synchronizer.shouldWait(track.id, packet.timestamp)) {
|
||||
await this._synchronizer.wait(packet.timestamp);
|
||||
this._reportProgress(track.id, modifiedPacket.timestamp);
|
||||
await source.add(modifiedPacket, meta);
|
||||
|
||||
if (this._synchronizer.shouldWait(track.id, modifiedPacket.timestamp)) {
|
||||
await this._synchronizer.wait(modifiedPacket.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,6 +1459,9 @@ export class Conversion {
|
||||
return;
|
||||
}
|
||||
|
||||
// Offset the timestamp as needed
|
||||
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
||||
|
||||
await this._registerAudioSample(track, trackOptions, source, sample);
|
||||
sample.close();
|
||||
}
|
||||
|
||||
@@ -228,20 +228,13 @@ export class MpegTsDemuxer extends Demuxer {
|
||||
|
||||
switch (streamType) {
|
||||
case MpegTsStreamType.MP3_MPEG1:
|
||||
case MpegTsStreamType.MP3_MPEG2: {
|
||||
info = {
|
||||
type: 'audio',
|
||||
codec: 'mp3',
|
||||
aacCodecInfo: null,
|
||||
numberOfChannels: -1,
|
||||
sampleRate: -1,
|
||||
};
|
||||
}; break;
|
||||
|
||||
case MpegTsStreamType.MP3_MPEG2:
|
||||
case MpegTsStreamType.AAC: {
|
||||
const codec = streamType === MpegTsStreamType.AAC ? 'aac' : 'mp3';
|
||||
|
||||
info = {
|
||||
type: 'audio',
|
||||
codec: 'aac',
|
||||
codec,
|
||||
aacCodecInfo: null,
|
||||
numberOfChannels: -1,
|
||||
sampleRate: -1,
|
||||
@@ -268,6 +261,11 @@ export class MpegTsDemuxer extends Demuxer {
|
||||
reorderSize: -1,
|
||||
};
|
||||
}; break;
|
||||
|
||||
default: {
|
||||
// If we don't recognize the codec, we don't surface the track at all. This is because
|
||||
// we can't determine its metadata and also have no idea how to packetize its data.
|
||||
}
|
||||
}
|
||||
|
||||
if (info) {
|
||||
|
||||
+25
-5
@@ -234,31 +234,51 @@ export class EncodedPacket {
|
||||
);
|
||||
}
|
||||
|
||||
/** Clones this packet while optionally updating timing information. */
|
||||
/** Clones this packet while optionally modifying the new packet's data. */
|
||||
clone(options?: {
|
||||
/** The data of the cloned packet. */
|
||||
data?: Uint8Array;
|
||||
/** The type of the cloned packet. */
|
||||
type?: PacketType;
|
||||
/** The timestamp of the cloned packet in seconds. */
|
||||
timestamp?: number;
|
||||
/** The duration of the cloned packet in seconds. */
|
||||
duration?: number;
|
||||
/** The sequence number of the cloned packet. */
|
||||
sequenceNumber?: number;
|
||||
/** The side data of the cloned packet. */
|
||||
sideData?: EncodedPacketSideData;
|
||||
}): EncodedPacket {
|
||||
if (options !== undefined && (typeof options !== 'object' || options === null)) {
|
||||
throw new TypeError('options, when provided, must be an object.');
|
||||
}
|
||||
if (options?.data !== undefined && !(options.data instanceof Uint8Array)) {
|
||||
throw new TypeError('options.data, when provided, must be a Uint8Array.');
|
||||
}
|
||||
if (options?.type !== undefined && options.type !== 'key' && options.type !== 'delta') {
|
||||
throw new TypeError('options.type, when provided, must be either "key" or "delta".');
|
||||
}
|
||||
if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) {
|
||||
throw new TypeError('options.timestamp, when provided, must be a number.');
|
||||
}
|
||||
if (options?.duration !== undefined && !Number.isFinite(options.duration)) {
|
||||
throw new TypeError('options.duration, when provided, must be a number.');
|
||||
}
|
||||
if (options?.sequenceNumber !== undefined && !Number.isFinite(options.sequenceNumber)) {
|
||||
throw new TypeError('options.sequenceNumber, when provided, must be a number.');
|
||||
}
|
||||
if (options?.sideData !== undefined && (typeof options.sideData !== 'object' || options.sideData === null)) {
|
||||
throw new TypeError('options.sideData, when provided, must be an object.');
|
||||
}
|
||||
|
||||
return new EncodedPacket(
|
||||
this.data,
|
||||
this.type,
|
||||
options?.data ?? this.data,
|
||||
options?.type ?? this.type,
|
||||
options?.timestamp ?? this.timestamp,
|
||||
options?.duration ?? this.duration,
|
||||
this.sequenceNumber,
|
||||
options?.sequenceNumber ?? this.sequenceNumber,
|
||||
this.byteLength,
|
||||
this.sideData,
|
||||
options?.sideData ?? this.sideData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user