UNPKG

webcodecs-encoder

Version:

A TypeScript library for browser environments to encode video (H.264/AVC, VP9, VP8) and audio (AAC, Opus) using the WebCodecs API and mux them into MP4 or WebM containers with real-time streaming support. New function-first API design.

355 lines (344 loc) 11.7 kB
type Frame = VideoFrame | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | ImageData; interface VideoFile { file: File | Blob; type: string; } type VideoSource = Frame[] | AsyncIterable<Frame> | MediaStream | VideoFile; type QualityPreset = 'low' | 'medium' | 'high' | 'lossless'; interface VideoConfig { codec?: 'avc' | 'hevc' | 'vp9' | 'vp8' | 'av1'; bitrate?: number; hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software'; latencyMode?: 'quality' | 'realtime'; keyFrameInterval?: number; } interface AudioConfig { codec?: 'aac' | 'opus'; bitrate?: number; sampleRate?: number; channels?: number; bitrateMode?: 'constant' | 'variable'; } interface ProgressInfo { percent: number; processedFrames: number; totalFrames?: number; fps: number; stage: string; estimatedRemainingMs?: number; } interface EncodeOptions { width?: number; height?: number; frameRate?: number; quality?: QualityPreset; video?: VideoConfig | false; audio?: AudioConfig | false; container?: 'mp4' | 'webm'; firstTimestampBehavior?: "offset" | "strict"; latencyMode?: "quality" | "realtime"; maxVideoQueueSize?: number; maxAudioQueueSize?: number; backpressureStrategy?: "drop" | "wait"; onProgress?: (progress: ProgressInfo) => void; onError?: (error: EncodeError) => void; } type EncodeErrorType = 'not-supported' | 'initialization-failed' | 'configuration-error' | 'invalid-input' | 'encoding-failed' | 'video-encoding-error' | 'audio-encoding-error' | 'muxing-failed' | 'cancelled' | 'timeout' | 'worker-error' | 'filesystem-error' | 'unknown'; declare class EncodeError extends Error { type: EncodeErrorType; cause?: unknown; constructor(type: EncodeErrorType, message: string, cause?: unknown); } interface EncoderConfig { width: number; height: number; frameRate: number; videoBitrate: number; audioBitrate: number; /** * Controls bitrate distribution for AAC. "constant" produces constant * bitrate (CBR) output while "variable" enables variable bitrate (VBR). * Not all browsers respect this setting. Chrome 119+ improves CBR support. */ audioBitrateMode?: "constant" | "variable"; sampleRate: number; channels: number; container?: "mp4" | "webm"; codec?: { video?: "avc" | "hevc" | "vp9" | "vp8" | "av1"; audio?: "aac" | "opus"; }; /** * Optional codec string overrides passed directly to the encoders. * For example: `{ video: 'avc1.640028', audio: 'mp4a.40.2' }`. */ codecString?: { video?: string; audio?: string; }; latencyMode?: "quality" | "realtime"; /** Preference for hardware or software encoding. */ hardwareAcceleration?: "prefer-hardware" | "prefer-software" | "no-preference"; /** Drop new video frames when the number of queued frames exceeds `maxQueueDepth`. */ dropFrames?: boolean; /** Maximum number of queued video frames before dropping. Defaults to `Infinity`. */ maxQueueDepth?: number; /** Total frames for progress calculation if known in advance. */ totalFrames?: number; /** Force a key frame every N video frames. */ keyFrameInterval?: number; /** * How to handle the first timestamp of a track. * 'offset': Offsets all timestamps so the first one is 0. * 'strict': Requires the first timestamp to be 0 (default). */ firstTimestampBehavior?: "offset" | "strict"; /** Backpressure control for video queue */ maxVideoQueueSize?: number; /** Backpressure control for audio queue */ maxAudioQueueSize?: number; /** Backpressure strategy: drop frames or wait */ backpressureStrategy?: "drop" | "wait"; /** Additional VideoEncoder configuration overrides. */ videoEncoderConfig?: Partial<VideoEncoderConfig>; /** Additional AudioEncoder configuration overrides. */ audioEncoderConfig?: Partial<AudioEncoderConfig>; } declare enum ProcessingStage { Initializing = "initializing", VideoEncoding = "video-encoding", AudioEncoding = "audio-encoding", Muxing = "muxing", Finalizing = "finalizing" } declare enum EncoderErrorType { NotSupported = "not-supported", InitializationFailed = "initialization-failed", ConfigurationError = "configuration-error", InvalidInput = "invalid-input",// Input source or frame data is invalid EncodingFailed = "encoding-failed",// Generic encoding error VideoEncodingError = "video-encoding-error",// Specific video encoding error AudioEncodingError = "audio-encoding-error",// Specific audio encoding error MuxingFailed = "muxing-failed", Cancelled = "cancelled", Timeout = "timeout", WorkerError = "worker-error", FilesystemError = "filesystem-error",// VideoFile access errors Unknown = "unknown" } interface InitializeWorkerMessage { type: "initialize"; config: EncoderConfig; totalFrames?: number; } interface AddVideoFrameMessage { type: "addVideoFrame"; frame: VideoFrame; timestamp: number; } interface AddAudioDataMessage { type: "addAudioData"; audioData?: Float32Array[]; /** Optional AudioData object to be encoded directly. */ audio?: AudioData; timestamp: number; format: AudioSampleFormat; sampleRate: number; numberOfFrames: number; numberOfChannels: number; } interface FinalizeWorkerMessage { type: "finalize"; } interface CancelWorkerMessage { type: "cancel"; } type WorkerMessage = InitializeWorkerMessage | AddVideoFrameMessage | AddAudioDataMessage | FinalizeWorkerMessage | CancelWorkerMessage; interface WorkerInitializedMessage { type: "initialized"; actualVideoCodec?: string; actualAudioCodec?: string; } interface ProgressMessage { type: "progress"; processedFrames: number; totalFrames?: number; } interface WorkerFinalizedMessage { type: "finalized"; output: Uint8Array | null; } interface QueueSizeMessage { type: "queueSize"; videoQueueSize: number; audioQueueSize: number; } interface WorkerDataChunkMessage { type: "dataChunk"; chunk: Uint8Array; isHeader?: boolean; offset?: number; container: "mp4" | "webm"; } interface WorkerErrorMessage { type: "error"; errorDetail: { message: string; type: EncoderErrorType; stack?: string; }; } interface WorkerCancelledMessage { type: "cancelled"; } type MainThreadMessage = WorkerInitializedMessage | ProgressMessage | WorkerFinalizedMessage | QueueSizeMessage | WorkerDataChunkMessage | WorkerErrorMessage | WorkerCancelledMessage; type VideoEncoderConstructor = typeof VideoEncoder; type AudioEncoderConstructor = typeof AudioEncoder; type AudioDataConstructor = typeof AudioData; type VideoEncoderGetter = () => VideoEncoderConstructor | undefined; type AudioEncoderGetter = () => AudioEncoderConstructor | undefined; type AudioDataGetter = () => AudioDataConstructor | undefined; /** * Core encode function implementation */ /** * Main video encoding function * * @param source Video source to encode * @param options Encoding options * @returns Encoded binary data */ declare function encode(source: VideoSource, options?: EncodeOptions): Promise<Uint8Array>; /** * Streaming encode function implementation */ /** * Streaming encode function * * @param source Video source to encode * @param options Encoding options * @returns AsyncGenerator of encoded chunks */ declare function encodeStream(source: VideoSource, options?: EncodeOptions): AsyncGenerator<Uint8Array>; /** * Encode capability verification */ /** * Verify encode capability * * @param options Encode options * @returns Whether encoding is possible */ declare function canEncode(options?: EncodeOptions): Promise<boolean>; /** * カスタムエンコーダーファクトリ */ /** * エンコーダー関数のファクトリ * 設定を事前に部分適用した専用エンコーダー関数を作成 */ interface EncoderFactory { /** * ワンショットエンコード */ encode(source: VideoSource, additionalOptions?: Partial<EncodeOptions>): Promise<Uint8Array>; /** * ストリーミングエンコード */ encodeStream(source: VideoSource, additionalOptions?: Partial<EncodeOptions>): AsyncGenerator<Uint8Array>; /** * 設定された設定を取得 */ getConfig(): EncodeOptions; /** * 新しい設定でファクトリを拡張 */ extend(newOptions: Partial<EncodeOptions>): EncoderFactory; } /** * カスタムエンコーダーファクトリを作成 * * @param baseOptions 基本エンコードオプション * @returns 設定済みエンコーダーファクトリ */ declare function createEncoder(baseOptions?: EncodeOptions): EncoderFactory; /** * 事前定義されたエンコーダーファクトリ */ declare const encoders: { /** * YouTube向け高品質エンコーダー */ youtube: EncoderFactory; /** * Twitter向け最適化エンコーダー */ twitter: EncoderFactory; /** * Discord向け最適化エンコーダー */ discord: EncoderFactory; /** * Web再生向けバランス型エンコーダー */ web: EncoderFactory; /** * 軽量・高速エンコーダー */ fast: EncoderFactory; /** * 高品質・低圧縮エンコーダー */ lossless: EncoderFactory; /** * VP9ストリーミング用エンコーダー */ vp9Stream: EncoderFactory; }; /** * 使用例とヘルパー関数 */ declare const examples: { /** * プラットフォーム別のエンコーダーを取得 */ getEncoderForPlatform(platform: "youtube" | "twitter" | "discord" | "web"): EncoderFactory; /** * 解像度ベースのエンコーダーを作成 */ createByResolution(width: number, height: number): EncoderFactory; /** * ファイルサイズ制約ベースのエンコーダーを作成 */ createForFileSize(targetSizeMB: number, durationSeconds: number): EncoderFactory; }; interface MediaStreamRecorderOptions extends EncodeOptions { /** 最初のタイムスタンプの処理方法 */ firstTimestampBehavior?: "offset" | "strict"; } declare class MediaStreamRecorder { private options; private communicator; private videoReader?; private audioReader?; private videoTrack?; private audioTrack?; private audioSource?; private recording; private finalizing; private onErrorCallback?; private onProgressCallback?; private config; constructor(options?: MediaStreamRecorderOptions); static isSupported(): boolean; startRecording(stream: MediaStream, additionalOptions?: Partial<MediaStreamRecorderOptions>): Promise<void>; private initializeWorker; private processVideo; private processAudio; stopRecording(): Promise<Uint8Array | null>; cancel(): void; private cleanup; getActualVideoCodec(): string | null; getActualAudioCodec(): string | null; } export { type AudioConfig, type AudioDataGetter, type AudioEncoderGetter, EncodeError, type EncodeErrorType, type EncodeOptions, type EncoderConfig, EncoderErrorType, type EncoderFactory, type Frame, type MainThreadMessage, MediaStreamRecorder, ProcessingStage, type ProgressInfo, type QualityPreset, type VideoConfig, type VideoEncoderGetter, type VideoFile, type VideoSource, type WorkerMessage, canEncode, createEncoder, encode, encodeStream, encoders, examples };