在浏览器里做视频录制功能时,MediaRecorder构造函数的第二个参数允许我们指定音频和视频的码率,其中videoBitsPerSecond直接决定了录出来画面的清晰度和文件大小。虽然TypeScript的官方库中已经内置了MediaRecorderOptions这个类型,但很多团队在封装录制模块时,还是习惯直接写一个魔法数字,既没有类型约束,也没有体现浏览器在不同分辨率下的默认峰值策略。这篇文章就来聊聊如何在TypeScript里把码率配置这一层定义得严谨、可维护。

先弄清楚MediaRecorderOptions的官方类型定义
在lib.dom.d.ts中,MediaRecorderOptions的定义非常简单,核心字段只有三个:
interface MediaRecorderOptions {
audioBitsPerSecond?: number;
bitsPerSecond?: number;
videoBitsPerSecond?: number;
mimeType?: string;
}注意这三个字段全部是可选的,类型都是number。如果你不传videoBitsPerSecond,浏览器会根据自己的默认策略来决定视频码率。以Chrome为例,在调用MediaRecorder.isTypeSupported('video/webm;codecs=vp9')确认编码可用后,不同分辨率下引擎内部有不同的建议码率区间,这个建议值就是我们常说的默认峰值倾向。不同浏览器甚至不同版本之间,这个默认值都可能发生变化,所以把它硬编码进业务代码是非常危险的做法。
还有一个容易混淆的点:bitsPerSecond是整体码率,一旦设置了它,浏览器会把这个数值在音频和视频之间自动分配,通常会覆盖你单独设置的audioBitsPerSecond和videoBitsPerSecond。也就是说,想精确控制视频码率,就不要同时传bitsPerSecond,这一点在类型层面也应该体现出来,后面我们会用联合类型来解决。
用TypeScript封装码率配置的几种方案
方案一:const对象配合类型推导
最直接的方式是定义一组预设档位,用as const锁定类型,保证档位值不可被篡改:
export const VIDEO_BITRATE_PRESETS = {
low: 500_000, // 500kbps,适合低带宽场景
medium: 2_500_000, // 2.5Mbps,常规录屏
high: 8_000_000, // 8Mbps,高清录制
peak: 12_000_000, // 峰值档,接近多数浏览器的实际上限
} as const;
export type VideoBitratePreset = keyof typeof VIDEO_BITRATE_PRESETS;
// 类型为 "low" | "medium" | "high" | "peak"这种写法的好处是调用方只能传四个档位名之一,写错任意一个都会在编译期报错,比如误写成'Peak'会直接被类型系统拦下。数字分隔符_让大数值的可读性大幅提升,这也是TypeScript从3.7版本开始支持的小特性。
方案二:带范围校验的Brand类型
如果业务上允许自由填写数值,但又想约束在合理区间内,可以用品牌类型(Branded Type)做一个只能通过校验函数构造的类型:
declare const ValidBitrateBrand: unique symbol;
type ValidBitrate = number & { readonly [ValidBitrateBrand]: true };
const MIN_BITRATE = 100_000;
const MAX_BITRATE = 20_000_000;
function createBitrate(value: number): ValidBitrate {
if (!Number.isFinite(value) || value < MIN_BITRATE || value > MAX_BITRATE) {
throw new RangeError(
`码率必须在 ${MIN_BITRATE} 到 ${MAX_BITRATE} 之间,收到的是 ${value}`
);
}
return value as ValidBitrate;
}
interface RecorderConfig {
videoBitrate: ValidBitrate;
mimeType: string;
}这样RecorderConfig的videoBitrate字段就再也不可能被随意赋一个裸数字,必须经过createBitrate的运行时校验。类型系统和运行时校验形成双保险,特别适合录制参数会被多个模块共享的中大型项目。
方案三:用联合类型互斥bitsPerSecond
针对前面提到的bitsPerSecond覆盖问题,可以让配置类型本身表达互斥关系:
type BitrateConfig =
| { overall: number; audio?: never; video?: never }
| { overall?: never; audio?: number; video?: number };
function createRecorderOptions(config: BitrateConfig): MediaRecorderOptions {
if (config.overall !== undefined) {
return { bitsPerSecond: config.overall };
}
return {
audioBitsPerSecond: config.audio,
videoBitsPerSecond: config.video,
};
}一旦调用方同时传了overall和video,TypeScript会立刻提示类型不匹配,把那个隐蔽的覆盖问题消灭在编码阶段。这种利用never字段实现互斥的技巧,在处理互斥配置项时非常实用。
常见类型错误与运行时陷阱
第一个常见错误是把码率配置字段声明为必填的number,结果想使用浏览器默认值时不得不传0或者负数。其实更合理的做法是允许undefined,并把它视为使用浏览器默认策略的信号:
interface FlexibleRecorderConfig {
// undefined 表示交给浏览器使用默认码率
videoBitsPerSecond: number | undefined;
}
function isUsingDefaultBitrate(config: FlexibleRecorderConfig): boolean {
return config.videoBitsPerSecond === undefined;
}第二个陷阱是忽略实际生效值和请求值的差异。浏览器并不保证严格遵从你设置的码率,编码器会根据画面复杂度动态浮动。稳妥的做法是录制结束后通过blob.size和时长反推平均码率,用于监控和排查:
function measureActualBitrate(blob: Blob, durationMs: number): number {
const bits = blob.size * 8;
return Math.round(bits / (durationMs / 1000));
}第三个陷阱是编码格式与码率的匹配问题。同样的videoBitsPerSecond,配vp9和配h264出来的画质差异可能很大,H.264通常需要更高的码率才能达到VP9同等的画质。所以在封装层把mimeType和码率档位绑定成一组预设,比让调用方自由组合更不容易出错。
一个可直接复用的完整封装
把前面的思路整合起来,可以得到一个类型安全、带默认档位、能探测浏览器支持的完整工具:
export const VIDEO_BITRATE_PRESETS = {
low: 500_000,
medium: 2_500_000,
high: 8_000_000,
peak: 12_000_000,
} as const;
export type VideoBitratePreset = keyof typeof VIDEO_BITRATE_PRESETS;
export interface RecorderSetup {
mimeType: string;
options: MediaRecorderOptions;
}
export function setupRecorder(
stream: MediaStream,
preset: VideoBitratePreset = 'medium'
): RecorderSetup {
const candidates = [
'video/webm;codecs=vp9',
'video/webm;codecs=vp8',
'video/mp4',
];
const mimeType = candidates.find(t => MediaRecorder.isTypeSupported(t));
if (!mimeType) {
throw new Error('当前浏览器不支持任何常见的录制编码格式');
}
return {
mimeType,
options: {
mimeType,
videoBitsPerSecond: VIDEO_BITRATE_PRESETS[preset],
audioBitsPerSecond: 128_000,
},
};
}
// 使用示例
// const setup = setupRecorder(stream, 'high');
// const recorder = new MediaRecorder(stream, setup.options);这个封装把预设档位、编码探测、默认值处理全部收拢在一处,业务代码只需要关心选哪个档位。当未来浏览器调整默认码率策略,或者团队想更换编码格式时,只需要改动这一个文件。
总结一下,TypeScript本身不会给我们提供视频码率峰值的默认值常量,浏览器也没有把这类数值暴露成标准API。正确的做法是在自己的代码层建立一套类型化的档位体系,用联合类型表达配置的互斥关系,用品牌类型约束数值范围,同时保留undefined作为使用浏览器默认策略的通道。这样既享受了类型检查带来的安全性,又不会被浏览器的实现细节绑死,录制模块的可维护性会明显提升。
TypeScriptMediaRecordervideoBitrate修改时间:2026-09-10 14:56:47