mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-02 21:33:52 +02:00
with custom fps convertion
This commit is contained in:
+38
-8
@@ -83,6 +83,8 @@ export type ConversionOptions = {
|
|||||||
bitrate?: VideoEncodingConfig['bitrate'];
|
bitrate?: VideoEncodingConfig['bitrate'];
|
||||||
/** When true, video will always be re-encoded instead of directly copying over the encoded samples. */
|
/** When true, video will always be re-encoded instead of directly copying over the encoded samples. */
|
||||||
forceTranscode?: boolean;
|
forceTranscode?: boolean;
|
||||||
|
/** The desired fps of the output video, if not specified will use the fps of the input video. */
|
||||||
|
fps?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Audio-specific options. */
|
/** Audio-specific options. */
|
||||||
@@ -261,6 +263,9 @@ export class Conversion {
|
|||||||
if (options.video?.rotate !== undefined && ![0, 90, 180, 270].includes(options.video.rotate)) {
|
if (options.video?.rotate !== undefined && ![0, 90, 180, 270].includes(options.video.rotate)) {
|
||||||
throw new TypeError('options.video.rotate, when provided, must be 0, 90, 180 or 270.');
|
throw new TypeError('options.video.rotate, when provided, must be 0, 90, 180 or 270.');
|
||||||
}
|
}
|
||||||
|
if (options.video?.fps !== undefined && (typeof options.video.fps !== 'number' || isNaN(options.video.fps) || options.video.fps <= 0)) {
|
||||||
|
throw new TypeError('options.video.fps, when provided, must be a positive number.');
|
||||||
|
}
|
||||||
if (options.audio !== undefined && (!options.audio || typeof options.audio !== 'object')) {
|
if (options.audio !== undefined && (!options.audio || typeof options.audio !== 'object')) {
|
||||||
throw new TypeError('options.video, when provided, must be an object.');
|
throw new TypeError('options.video, when provided, must be an object.');
|
||||||
}
|
}
|
||||||
@@ -469,11 +474,19 @@ export class Conversion {
|
|||||||
height = ceilToMultipleOfTwo(this._options.video.height);
|
height = ceilToMultipleOfTwo(this._options.video.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const packetStats = await track.computePacketStats();
|
||||||
|
const originalFps = packetStats.averagePacketRate;
|
||||||
|
let fps = originalFps;
|
||||||
|
if (this._options.video?.fps !== undefined) {
|
||||||
|
fps = this._options.video.fps;
|
||||||
|
}
|
||||||
|
|
||||||
const firstTimestamp = await track.getFirstTimestamp();
|
const firstTimestamp = await track.getFirstTimestamp();
|
||||||
const needsTranscode = !!this._options.video?.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0;
|
const needsTranscode = !!this._options.video?.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0;
|
||||||
const needsRerender = width !== originalWidth
|
const needsRerender = width !== originalWidth
|
||||||
|| height !== originalHeight
|
|| height !== originalHeight
|
||||||
|| (totalRotation !== 0 && !outputSupportsRotation);
|
|| (totalRotation !== 0 && !outputSupportsRotation)
|
||||||
|
|| fps !== originalFps;
|
||||||
|
|
||||||
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
||||||
if (
|
if (
|
||||||
@@ -547,6 +560,7 @@ export class Conversion {
|
|||||||
onEncodedPacket: sample => this._reportProgress(track.id, sample.timestamp + sample.duration),
|
onEncodedPacket: sample => this._reportProgress(track.id, sample.timestamp + sample.duration),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log({ originalFps, fps, needsRerender });
|
||||||
if (needsRerender) {
|
if (needsRerender) {
|
||||||
const source = new VideoSampleSource(encodingConfig);
|
const source = new VideoSampleSource(encodingConfig);
|
||||||
videoSource = source;
|
videoSource = source;
|
||||||
@@ -563,6 +577,10 @@ export class Conversion {
|
|||||||
});
|
});
|
||||||
const iterator = sink.canvases(this._startTimestamp, this._endTimestamp);
|
const iterator = sink.canvases(this._startTimestamp, this._endTimestamp);
|
||||||
|
|
||||||
|
const frameDuration = 1 / fps;
|
||||||
|
|
||||||
|
let outputTimestamp = 0;
|
||||||
|
|
||||||
for await (const { canvas, timestamp, duration } of iterator) {
|
for await (const { canvas, timestamp, duration } of iterator) {
|
||||||
if (this._synchronizer.shouldWait(track.id, timestamp)) {
|
if (this._synchronizer.shouldWait(track.id, timestamp)) {
|
||||||
await this._synchronizer.wait(timestamp);
|
await this._synchronizer.wait(timestamp);
|
||||||
@@ -572,13 +590,25 @@ export class Conversion {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sample = new VideoSample(canvas, {
|
const relativeToStartTimestamp = Math.max(timestamp - this._startTimestamp, 0);
|
||||||
timestamp: Math.max(timestamp - this._startTimestamp, 0),
|
if (originalFps === fps) {
|
||||||
duration,
|
const sample = new VideoSample(canvas, {
|
||||||
});
|
timestamp: relativeToStartTimestamp,
|
||||||
|
duration,
|
||||||
await source.add(sample);
|
});
|
||||||
sample.close();
|
await source.add(sample);
|
||||||
|
sample.close();
|
||||||
|
} else {
|
||||||
|
while (relativeToStartTimestamp >= outputTimestamp) {
|
||||||
|
const sample = new VideoSample(canvas, {
|
||||||
|
timestamp: outputTimestamp,
|
||||||
|
duration: frameDuration,
|
||||||
|
});
|
||||||
|
await source.add(sample);
|
||||||
|
sample.close();
|
||||||
|
outputTimestamp += frameDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})());
|
})());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user