From dcd1f242b40023ff310294eb81f9f0a31bf2dd38 Mon Sep 17 00:00:00 2001 From: AJ Funk Date: Wed, 1 Oct 2025 09:28:06 -0700 Subject: [PATCH 1/2] add keyFrameInterval to ConversionVideoOptions --- docs/guide/converting-media-files.md | 5 ++++- src/conversion.ts | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/guide/converting-media-files.md b/docs/guide/converting-media-files.md index 58b6bce..04cd301 100644 --- a/docs/guide/converting-media-files.md +++ b/docs/guide/converting-media-files.md @@ -124,6 +124,7 @@ type ConversionVideoOptions = { codec?: VideoCodec; bitrate?: number | Quality; alpha?: 'discard' | 'keep'; // Defaults to 'discard' + keyFrameInterval?: number; forceTranscode?: boolean; }; ``` @@ -180,7 +181,9 @@ Use the `codec` property to control the codec of the output track. This should b Use the `bitrate` property to control the bitrate of the output video. For example, you can use this field to compress the video track. Accepted values are the number of bits per second or a [subjective quality](./media-sources#subjective-qualities). If this property is set, transcoding will always happen. If this property is not set but transcoding is still required, `QUALITY_HIGH` will be used as the value. -If you want to prevent direct copying of media data and force a transcoding step, use `forceTranscode: true`. +Use the `keyFrameInterval` property to control the interval in seconds between key frames in the output video. + +If you want to prevent direct copying of media data and force a transcoding step, use `forceTranscode: true`. Setting `keyFrameInterval` or `frameRate` will automatically force transcoding, even if `forceTranscode` is not explicitly set to `true`. ## Audio options diff --git a/src/conversion.ts b/src/conversion.ts index 69cb82e..e8033f5 100644 --- a/src/conversion.ts +++ b/src/conversion.ts @@ -166,6 +166,11 @@ export type ConversionVideoOptions = { * VP9. */ alpha?: 'discard' | 'keep'; + /** + * The desired interval in seconds between key frames in the output video. + * Setting this value will force transcoding (even if `forceTranscode` is not explicitly set to `true`). + */ + keyFrameInterval?: number; /** When `true`, video will always be re-encoded instead of directly copying over the encoded samples. */ forceTranscode?: boolean; }; @@ -252,6 +257,12 @@ const validateVideoOptions = (videoOptions: ConversionVideoOptions | undefined) if (videoOptions?.alpha !== undefined && !['discard', 'keep'].includes(videoOptions.alpha)) { throw new TypeError('options.video.alpha, when provided, must be either \'discard\' or \'keep\'.'); } + if ( + videoOptions?.keyFrameInterval !== undefined + && (!Number.isFinite(videoOptions.keyFrameInterval) || videoOptions.keyFrameInterval <= 0) + ) { + throw new TypeError('options.video.keyFrameInterval, when provided, must be a finite positive number.'); + } }; const validateAudioOptions = (audioOptions: ConversionAudioOptions | undefined) => { @@ -775,7 +786,8 @@ export class Conversion { const needsTranscode = !!trackOptions.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0 - || !!trackOptions.frameRate; + || !!trackOptions.frameRate + || trackOptions.keyFrameInterval !== undefined; let needsRerender = width !== originalWidth || height !== originalHeight || (totalRotation !== 0 && !outputSupportsRotation) @@ -858,6 +870,7 @@ export class Conversion { const encodingConfig: VideoEncodingConfig = { codec: encodableCodec, bitrate, + keyFrameInterval: trackOptions.keyFrameInterval, sizeChangeBehavior: trackOptions.fit ?? 'passThrough', alpha, onEncodedPacket: sample => this._reportProgress(track.id, sample.timestamp + sample.duration), From 7accface7ba0286548919093ec3ed6282845eb62 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:23:05 +0200 Subject: [PATCH 2/2] A few cleanups --- docs/guide/converting-media-files.md | 4 ++-- src/conversion.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/guide/converting-media-files.md b/docs/guide/converting-media-files.md index 04cd301..8a3daa4 100644 --- a/docs/guide/converting-media-files.md +++ b/docs/guide/converting-media-files.md @@ -181,9 +181,9 @@ Use the `codec` property to control the codec of the output track. This should b Use the `bitrate` property to control the bitrate of the output video. For example, you can use this field to compress the video track. Accepted values are the number of bits per second or a [subjective quality](./media-sources#subjective-qualities). If this property is set, transcoding will always happen. If this property is not set but transcoding is still required, `QUALITY_HIGH` will be used as the value. -Use the `keyFrameInterval` property to control the interval in seconds between key frames in the output video. +Use the `keyFrameInterval` property to control the maximum interval in seconds between key frames in the output video. Setting this fields forces a transcode. -If you want to prevent direct copying of media data and force a transcoding step, use `forceTranscode: true`. Setting `keyFrameInterval` or `frameRate` will automatically force transcoding, even if `forceTranscode` is not explicitly set to `true`. +If you want to prevent direct copying of media data and force a transcoding step, use `forceTranscode: true`. ## Audio options diff --git a/src/conversion.ts b/src/conversion.ts index e8033f5..8b75826 100644 --- a/src/conversion.ts +++ b/src/conversion.ts @@ -167,8 +167,11 @@ export type ConversionVideoOptions = { */ alpha?: 'discard' | 'keep'; /** - * The desired interval in seconds between key frames in the output video. - * Setting this value will force transcoding (even if `forceTranscode` is not explicitly set to `true`). + * The interval, in seconds, of how often frames are encoded as a key frame. The default is 5 seconds. Frequent key + * frames improve seeking behavior but increase file size. When using multiple video tracks, you should give them + * all the same key frame interval. + * + * Setting this fields forces a transcode. */ keyFrameInterval?: number; /** When `true`, video will always be re-encoded instead of directly copying over the encoded samples. */ @@ -259,9 +262,9 @@ const validateVideoOptions = (videoOptions: ConversionVideoOptions | undefined) } if ( videoOptions?.keyFrameInterval !== undefined - && (!Number.isFinite(videoOptions.keyFrameInterval) || videoOptions.keyFrameInterval <= 0) + && (!Number.isFinite(videoOptions.keyFrameInterval) || videoOptions.keyFrameInterval < 0) ) { - throw new TypeError('options.video.keyFrameInterval, when provided, must be a finite positive number.'); + throw new TypeError('config.keyFrameInterval, when provided, must be a non-negative number.'); } };